root/drivers/input/keyboard/mcs_touchkey.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mcs_touchkey_interrupt
  2. mcs_touchkey_probe
  3. mcs_touchkey_remove
  4. mcs_touchkey_shutdown
  5. mcs_touchkey_suspend
  6. mcs_touchkey_resume

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Touchkey driver for MELFAS MCS5000/5080 controller
   4  *
   5  * Copyright (C) 2010 Samsung Electronics Co.Ltd
   6  * Author: HeungJun Kim <riverful.kim@samsung.com>
   7  * Author: Joonyoung Shim <jy0922.shim@samsung.com>
   8  */
   9 
  10 #include <linux/module.h>
  11 #include <linux/i2c.h>
  12 #include <linux/interrupt.h>
  13 #include <linux/input.h>
  14 #include <linux/irq.h>
  15 #include <linux/slab.h>
  16 #include <linux/platform_data/mcs.h>
  17 #include <linux/pm.h>
  18 
  19 /* MCS5000 Touchkey */
  20 #define MCS5000_TOUCHKEY_STATUS         0x04
  21 #define MCS5000_TOUCHKEY_STATUS_PRESS   7
  22 #define MCS5000_TOUCHKEY_FW             0x0a
  23 #define MCS5000_TOUCHKEY_BASE_VAL       0x61
  24 
  25 /* MCS5080 Touchkey */
  26 #define MCS5080_TOUCHKEY_STATUS         0x00
  27 #define MCS5080_TOUCHKEY_STATUS_PRESS   3
  28 #define MCS5080_TOUCHKEY_FW             0x01
  29 #define MCS5080_TOUCHKEY_BASE_VAL       0x1
  30 
  31 enum mcs_touchkey_type {
  32         MCS5000_TOUCHKEY,
  33         MCS5080_TOUCHKEY,
  34 };
  35 
  36 struct mcs_touchkey_chip {
  37         unsigned int status_reg;
  38         unsigned int pressbit;
  39         unsigned int press_invert;
  40         unsigned int baseval;
  41 };
  42 
  43 struct mcs_touchkey_data {
  44         void (*poweron)(bool);
  45 
  46         struct i2c_client *client;
  47         struct input_dev *input_dev;
  48         struct mcs_touchkey_chip chip;
  49         unsigned int key_code;
  50         unsigned int key_val;
  51         unsigned short keycodes[];
  52 };
  53 
  54 static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id)
  55 {
  56         struct mcs_touchkey_data *data = dev_id;
  57         struct mcs_touchkey_chip *chip = &data->chip;
  58         struct i2c_client *client = data->client;
  59         struct input_dev *input = data->input_dev;
  60         unsigned int key_val;
  61         unsigned int pressed;
  62         int val;
  63 
  64         val = i2c_smbus_read_byte_data(client, chip->status_reg);
  65         if (val < 0) {
  66                 dev_err(&client->dev, "i2c read error [%d]\n", val);
  67                 goto out;
  68         }
  69 
  70         pressed = (val & (1 << chip->pressbit)) >> chip->pressbit;
  71         if (chip->press_invert)
  72                 pressed ^= chip->press_invert;
  73 
  74         /* key_val is 0 when released, so we should use key_val of press. */
  75         if (pressed) {
  76                 key_val = val & (0xff >> (8 - chip->pressbit));
  77                 if (!key_val)
  78                         goto out;
  79                 key_val -= chip->baseval;
  80                 data->key_code = data->keycodes[key_val];
  81                 data->key_val = key_val;
  82         }
  83 
  84         input_event(input, EV_MSC, MSC_SCAN, data->key_val);
  85         input_report_key(input, data->key_code, pressed);
  86         input_sync(input);
  87 
  88         dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code,
  89                 pressed ? "pressed" : "released");
  90 
  91  out:
  92         return IRQ_HANDLED;
  93 }
  94 
  95 static int mcs_touchkey_probe(struct i2c_client *client,
  96                 const struct i2c_device_id *id)
  97 {
  98         const struct mcs_platform_data *pdata;
  99         struct mcs_touchkey_data *data;
 100         struct input_dev *input_dev;
 101         unsigned int fw_reg;
 102         int fw_ver;
 103         int error;
 104         int i;
 105 
 106         pdata = dev_get_platdata(&client->dev);
 107         if (!pdata) {
 108                 dev_err(&client->dev, "no platform data defined\n");
 109                 return -EINVAL;
 110         }
 111 
 112         data = kzalloc(struct_size(data, keycodes, pdata->key_maxval + 1),
 113                        GFP_KERNEL);
 114         input_dev = input_allocate_device();
 115         if (!data || !input_dev) {
 116                 dev_err(&client->dev, "Failed to allocate memory\n");
 117                 error = -ENOMEM;
 118                 goto err_free_mem;
 119         }
 120 
 121         data->client = client;
 122         data->input_dev = input_dev;
 123 
 124         if (id->driver_data == MCS5000_TOUCHKEY) {
 125                 data->chip.status_reg = MCS5000_TOUCHKEY_STATUS;
 126                 data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS;
 127                 data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL;
 128                 fw_reg = MCS5000_TOUCHKEY_FW;
 129         } else {
 130                 data->chip.status_reg = MCS5080_TOUCHKEY_STATUS;
 131                 data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS;
 132                 data->chip.press_invert = 1;
 133                 data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL;
 134                 fw_reg = MCS5080_TOUCHKEY_FW;
 135         }
 136 
 137         fw_ver = i2c_smbus_read_byte_data(client, fw_reg);
 138         if (fw_ver < 0) {
 139                 error = fw_ver;
 140                 dev_err(&client->dev, "i2c read error[%d]\n", error);
 141                 goto err_free_mem;
 142         }
 143         dev_info(&client->dev, "Firmware version: %d\n", fw_ver);
 144 
 145         input_dev->name = "MELFAS MCS Touchkey";
 146         input_dev->id.bustype = BUS_I2C;
 147         input_dev->dev.parent = &client->dev;
 148         input_dev->evbit[0] = BIT_MASK(EV_KEY);
 149         if (!pdata->no_autorepeat)
 150                 input_dev->evbit[0] |= BIT_MASK(EV_REP);
 151         input_dev->keycode = data->keycodes;
 152         input_dev->keycodesize = sizeof(data->keycodes[0]);
 153         input_dev->keycodemax = pdata->key_maxval + 1;
 154 
 155         for (i = 0; i < pdata->keymap_size; i++) {
 156                 unsigned int val = MCS_KEY_VAL(pdata->keymap[i]);
 157                 unsigned int code = MCS_KEY_CODE(pdata->keymap[i]);
 158 
 159                 data->keycodes[val] = code;
 160                 __set_bit(code, input_dev->keybit);
 161         }
 162 
 163         input_set_capability(input_dev, EV_MSC, MSC_SCAN);
 164         input_set_drvdata(input_dev, data);
 165 
 166         if (pdata->cfg_pin)
 167                 pdata->cfg_pin();
 168 
 169         if (pdata->poweron) {
 170                 data->poweron = pdata->poweron;
 171                 data->poweron(true);
 172         }
 173 
 174         error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt,
 175                                      IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 176                                      client->dev.driver->name, data);
 177         if (error) {
 178                 dev_err(&client->dev, "Failed to register interrupt\n");
 179                 goto err_free_mem;
 180         }
 181 
 182         error = input_register_device(input_dev);
 183         if (error)
 184                 goto err_free_irq;
 185 
 186         i2c_set_clientdata(client, data);
 187         return 0;
 188 
 189 err_free_irq:
 190         free_irq(client->irq, data);
 191 err_free_mem:
 192         input_free_device(input_dev);
 193         kfree(data);
 194         return error;
 195 }
 196 
 197 static int mcs_touchkey_remove(struct i2c_client *client)
 198 {
 199         struct mcs_touchkey_data *data = i2c_get_clientdata(client);
 200 
 201         free_irq(client->irq, data);
 202         if (data->poweron)
 203                 data->poweron(false);
 204         input_unregister_device(data->input_dev);
 205         kfree(data);
 206 
 207         return 0;
 208 }
 209 
 210 static void mcs_touchkey_shutdown(struct i2c_client *client)
 211 {
 212         struct mcs_touchkey_data *data = i2c_get_clientdata(client);
 213 
 214         if (data->poweron)
 215                 data->poweron(false);
 216 }
 217 
 218 #ifdef CONFIG_PM_SLEEP
 219 static int mcs_touchkey_suspend(struct device *dev)
 220 {
 221         struct mcs_touchkey_data *data = dev_get_drvdata(dev);
 222         struct i2c_client *client = data->client;
 223 
 224         /* Disable the work */
 225         disable_irq(client->irq);
 226 
 227         /* Finally turn off the power */
 228         if (data->poweron)
 229                 data->poweron(false);
 230 
 231         return 0;
 232 }
 233 
 234 static int mcs_touchkey_resume(struct device *dev)
 235 {
 236         struct mcs_touchkey_data *data = dev_get_drvdata(dev);
 237         struct i2c_client *client = data->client;
 238 
 239         /* Enable the device first */
 240         if (data->poweron)
 241                 data->poweron(true);
 242 
 243         /* Enable irq again */
 244         enable_irq(client->irq);
 245 
 246         return 0;
 247 }
 248 #endif
 249 
 250 static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops,
 251                          mcs_touchkey_suspend, mcs_touchkey_resume);
 252 
 253 static const struct i2c_device_id mcs_touchkey_id[] = {
 254         { "mcs5000_touchkey", MCS5000_TOUCHKEY },
 255         { "mcs5080_touchkey", MCS5080_TOUCHKEY },
 256         { }
 257 };
 258 MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
 259 
 260 static struct i2c_driver mcs_touchkey_driver = {
 261         .driver = {
 262                 .name   = "mcs_touchkey",
 263                 .pm     = &mcs_touchkey_pm_ops,
 264         },
 265         .probe          = mcs_touchkey_probe,
 266         .remove         = mcs_touchkey_remove,
 267         .shutdown       = mcs_touchkey_shutdown,
 268         .id_table       = mcs_touchkey_id,
 269 };
 270 
 271 module_i2c_driver(mcs_touchkey_driver);
 272 
 273 /* Module information */
 274 MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
 275 MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
 276 MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller");
 277 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */