Hi On Fri, Jul 11, 2014 at 9:42 AM, Daniel Mack <zonque@xxxxxxxxx> wrote: > This patch adds a driver for Microchips CAP1106, an I2C driven, 6-channel > capacitive touch sensor. > > For now, only the capacitive buttons are supported, and no specific > settings that can be tweaked for individual channels, except for the > device-wide sensitivity gain. The defaults seem to work just fine out of > the box, so I'll leave configurable parameters for someone who's in need > of them and who can actually measure the impact. All registers are > prepared, however. Many of them are just not used for now. > > The implementation does not make any attempt to be compatible to platform > data driven boards, but fully depends on CONFIG_OF. > > Power management functions are also left for volounteers with the ability > to actually test them. > > Signed-off-by: Daniel Mack <zonque@xxxxxxxxx> > --- > .../devicetree/bindings/input/cap1106.txt | 63 ++++ > drivers/input/keyboard/Kconfig | 10 + > drivers/input/keyboard/Makefile | 1 + > drivers/input/keyboard/cap1106.c | 376 +++++++++++++++++++++ > 4 files changed, 450 insertions(+) > create mode 100644 Documentation/devicetree/bindings/input/cap1106.txt > create mode 100644 drivers/input/keyboard/cap1106.c > > diff --git a/Documentation/devicetree/bindings/input/cap1106.txt b/Documentation/devicetree/bindings/input/cap1106.txt > new file mode 100644 > index 0000000..57f5af3 > --- /dev/null > +++ b/Documentation/devicetree/bindings/input/cap1106.txt > @@ -0,0 +1,63 @@ > +Device tree bindings for Microchip CAP1106, 6 channel capacitive touch sensor > + > +The node for this driver must be a child of a I2C controller node, as the > +device communication via I2C only. > + > +Required properties: > + > + compatible: Must be "microchip,cap1106" > + reg: The I2C slave address of the device. > + Only 0x28 is valid. > + interrupt: Node describing the interrupt line the device's > + ALERT#/CM_IRQ# pin is connected to. > + > +Optional properties: > + > + autorepeat: Enables the Linux input system's autorepeat > + feature on the input device. > + microchip,sensor-gain: Defines the gain of the sensor circuitry. This > + effectively controls the sensitivity, as a > + smaller delta capacitance is required to > + generate the same delta count values. > + Valid values are 1, 2, 4, and 8. > + By default, a gain of 1 is set. > + > +To define details of each individual button channel, six subnodes can be > +specified. Inside each of those, the following property is valid: > + > + linux,keycode: Specify the numeric identifier of the keycode to be > + generated when this channel is activated. If this > + property is omitted, KEY_A, KEY_B, etc are used as > + defaults. > + > +Example: > + > +i2c_controller { > + cap1106@28 { > + compatible = "microchip,cap1106"; > + interrupt-parent = <&gpio1>; > + interrupts = <0 0>; > + reg = <0x28>; > + autorepeat; > + microchip,sensor-gain = <2>; > + > + up { > + linux,keycode = <103>; /* KEY_UP */ > + }; > + right { > + linux,keycode = <106>; /* KEY_RIGHT */ > + }; > + down { > + linux,keycode = <108>; /* KEY_DOWN */ > + }; > + left { > + linux,keycode = <105>; /* KEY_LEFT */ > + }; > + pagedown { > + linux,keycode = <109>; /* KEY_PAGEDOWN */ > + }; > + pageup { > + linux,keycode = <104>; /* KEY_PAGEUP */ > + }; > + }; > +} > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig > index f7e79b4..a3958c6 100644 > --- a/drivers/input/keyboard/Kconfig > +++ b/drivers/input/keyboard/Kconfig > @@ -665,4 +665,14 @@ config KEYBOARD_CROS_EC > To compile this driver as a module, choose M here: the > module will be called cros_ec_keyb. > > +config KEYBOARD_CAP1106 > + tristate "Microchip CAP1106 touch sensor" > + depends on OF && I2C > + select REGMAP_I2C > + help > + Say Y here to enable the CAP1106 touch sensor driver. > + > + To compile this driver as a module, choose M here: the > + module will be called cap1106. > + > endif > diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile > index 7504ae1..0a33456 100644 > --- a/drivers/input/keyboard/Makefile > +++ b/drivers/input/keyboard/Makefile > @@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o > obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o > obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o > obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o > +obj-$(CONFIG_KEYBOARD_CAP1106) += cap1106.o > obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o > obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o > obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o > diff --git a/drivers/input/keyboard/cap1106.c b/drivers/input/keyboard/cap1106.c > new file mode 100644 > index 0000000..ad0f0fb > --- /dev/null > +++ b/drivers/input/keyboard/cap1106.c > @@ -0,0 +1,376 @@ > +/* > + * Input driver for Microchip CAP1106, 6 channel capacitive touch sensor > + * > + * http://www.microchip.com/wwwproducts/Devices.aspx?product=CAP1106 > + * > + * (c) 2014 Daniel Mack <linux@xxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/interrupt.h> > +#include <linux/input.h> > +#include <linux/of_irq.h> > +#include <linux/regmap.h> > +#include <linux/i2c.h> > +#include <linux/gpio/consumer.h> > + > +#define CAP1106_REG_MAIN_CONTROL 0x00 > +#define CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT (6) > +#define CAP1106_REG_MAIN_CONTROL_GAIN_MASK (0xc0) > +#define CAP1106_REG_MAIN_CONTROL_DLSEEP BIT(4) > +#define CAP1106_REG_GENERAL_STATUS 0x02 > +#define CAP1106_REG_SENSOR_INPUT 0x03 > +#define CAP1106_REG_NOISE_FLAG_STATUS 0x0a > +#define CAP1106_REG_SENOR_DELTA(X) (0x10 + (X)) > +#define CAP1106_REG_SENSITIVITY_CONTROL 0x1f > +#define CAP1106_REG_CONFIG 0x20 > +#define CAP1106_REG_SENSOR_ENABLE 0x21 > +#define CAP1106_REG_SENSOR_CONFIG 0x22 > +#define CAP1106_REG_SENSOR_CONFIG2 0x23 > +#define CAP1106_REG_SAMPLING_CONFIG 0x24 > +#define CAP1106_REG_CALIBRATION 0x25 > +#define CAP1106_REG_INT_ENABLE 0x26 > +#define CAP1106_REG_REPEAT_RATE 0x28 > +#define CAP1106_REG_MT_CONFIG 0x2a > +#define CAP1106_REG_MT_PATTERN_CONFIG 0x2b > +#define CAP1106_REG_MT_PATTERN 0x2d > +#define CAP1106_REG_RECALIB_CONFIG 0x2f > +#define CAP1106_REG_SENSOR_THRESH(X) (0x30 + (X)) > +#define CAP1106_REG_SENSOR_NOISE_THRESH 0x38 > +#define CAP1106_REG_STANDBY_CHANNEL 0x40 > +#define CAP1106_REG_STANDBY_CONFIG 0x41 > +#define CAP1106_REG_STANDBY_SENSITIVITY 0x42 > +#define CAP1106_REG_STANDBY_THRESH 0x43 > +#define CAP1106_REG_CONFIG2 0x44 > +#define CAP1106_REG_SENSOR_BASE_CNT(X) (0x50 + (X)) > +#define CAP1106_REG_SENSOR_CALIB (0xb1 + (X)) > +#define CAP1106_REG_SENSOR_CALIB_LSB1 0xb9 > +#define CAP1106_REG_SENSOR_CALIB_LSB2 0xba > +#define CAP1106_REG_PRODUCT_ID 0xfd > +#define CAP1106_REG_MANUFACTURER_ID 0xfe > +#define CAP1106_REG_REVISION 0xff > + > +#define CAP1106_NUM_CHN 6 > +#define CAP1106_PRODUCT_ID 0x55 > +#define CAP1106_MANUFACTURER_ID 0x5d > + > +struct cap1106_priv { > + struct regmap *regmap; > + struct input_dev *idev; > + struct work_struct work; > + spinlock_t lock; > + int irq; > + > + /* config */ > + unsigned int keycodes[CAP1106_NUM_CHN]; > +}; > + > +static const struct reg_default cap1106_reg_defaults[] = { > + { CAP1106_REG_MAIN_CONTROL, 0x00 }, > + { CAP1106_REG_GENERAL_STATUS, 0x00 }, > + { CAP1106_REG_SENSOR_INPUT, 0x00 }, > + { CAP1106_REG_NOISE_FLAG_STATUS, 0x00 }, > + { CAP1106_REG_SENSITIVITY_CONTROL, 0x2f }, > + { CAP1106_REG_CONFIG, 0x20 }, > + { CAP1106_REG_SENSOR_ENABLE, 0x3f }, > + { CAP1106_REG_SENSOR_CONFIG, 0xa4 }, > + { CAP1106_REG_SENSOR_CONFIG2, 0x07 }, > + { CAP1106_REG_SAMPLING_CONFIG, 0x39 }, > + { CAP1106_REG_CALIBRATION, 0x00 }, > + { CAP1106_REG_INT_ENABLE, 0x3f }, > + { CAP1106_REG_REPEAT_RATE, 0x3f }, > + { CAP1106_REG_MT_CONFIG, 0x80 }, > + { CAP1106_REG_MT_PATTERN_CONFIG, 0x00 }, > + { CAP1106_REG_MT_PATTERN, 0x3f }, > + { CAP1106_REG_RECALIB_CONFIG, 0x8a }, > + { CAP1106_REG_SENSOR_THRESH(0), 0x40 }, > + { CAP1106_REG_SENSOR_THRESH(1), 0x40 }, > + { CAP1106_REG_SENSOR_THRESH(2), 0x40 }, > + { CAP1106_REG_SENSOR_THRESH(3), 0x40 }, > + { CAP1106_REG_SENSOR_THRESH(4), 0x40 }, > + { CAP1106_REG_SENSOR_THRESH(5), 0x40 }, > + { CAP1106_REG_SENSOR_NOISE_THRESH, 0x01 }, > + { CAP1106_REG_STANDBY_CHANNEL, 0x00 }, > + { CAP1106_REG_STANDBY_CONFIG, 0x39 }, > + { CAP1106_REG_STANDBY_SENSITIVITY, 0x02 }, > + { CAP1106_REG_STANDBY_THRESH, 0x40 }, > + { CAP1106_REG_CONFIG2, 0x40 }, > + { CAP1106_REG_SENSOR_CALIB_LSB1, 0x00 }, > + { CAP1106_REG_SENSOR_CALIB_LSB2, 0x00 }, > +}; > + > +static bool cap1106_volatile_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case CAP1106_REG_MAIN_CONTROL: > + case CAP1106_REG_SENSOR_INPUT: > + case CAP1106_REG_SENOR_DELTA(0): > + case CAP1106_REG_SENOR_DELTA(1): > + case CAP1106_REG_SENOR_DELTA(2): > + case CAP1106_REG_SENOR_DELTA(3): > + case CAP1106_REG_SENOR_DELTA(4): > + case CAP1106_REG_SENOR_DELTA(5): > + case CAP1106_REG_PRODUCT_ID: > + case CAP1106_REG_MANUFACTURER_ID: > + case CAP1106_REG_REVISION: > + return true; > + } > + > + return false; > +} > + > +static const struct regmap_config cap1106_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + > + .max_register = CAP1106_REG_REVISION, > + .reg_defaults = cap1106_reg_defaults, > + > + .num_reg_defaults = ARRAY_SIZE(cap1106_reg_defaults), > + .cache_type = REGCACHE_RBTREE, > + .volatile_reg = cap1106_volatile_reg, > +}; > + > +static void cap1106_work(struct work_struct *w) > +{ > + struct cap1106_priv *priv = container_of(w, struct cap1106_priv, work); > + unsigned int status; > + int ret, i; > + > + spin_lock(&priv->lock); If your worker started and is _about_ to take the spinlock, you might dead-lock with cap1106_close(). They might already hold the spinlock and then call cancel_work_sync(), thus waiting for this task to take the lock end exit.. How about you add "bool closed : 1;" to your cap1106_priv structure and drop the lock here, but.. > + > + /* > + * Deassert interrupt. This needs to be done before reading the status > + * registers, which will not carry valid values otherwise. > + */ > + ret = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, 1, 0); > + if (ret < 0) > + goto out; > + > + ret = regmap_read(priv->regmap, CAP1106_REG_SENSOR_INPUT, &status); > + if (ret < 0) > + goto out; > + > + for (i = 0; i < CAP1106_NUM_CHN; i++) > + input_report_key(priv->idev, priv->keycodes[i], > + status & (1 << i)); > + > + input_sync(priv->idev); > + > +out: > + enable_irq(priv->irq); > + spin_unlock(&priv->lock); ...change this to: spin_lock(&priv->lock); if (!priv->closed) enable_irq(priv->irq); spin_unlock(&priv->lock); > +} > + > +static irqreturn_t cap1106_isr(int irq_num, void *data) > +{ > + struct cap1106_priv *priv = data; > + > + spin_lock(&priv->lock); > + disable_irq_nosync(priv->irq); > + schedule_work(&priv->work); > + spin_unlock(&priv->lock); Locks here can be simply dropped. The IRQ is always stopped synchronously before any worker is stopped, so this cannot race. > + > + return IRQ_HANDLED; > +} > + > +static int cap1106_open(struct input_dev *input_dev) > +{ > + struct cap1106_priv *priv = input_get_drvdata(input_dev); > + int ret; > + > + /* disable deep sleep */ > + ret = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, > + CAP1106_REG_MAIN_CONTROL_DLSEEP, 0); > + You'd have to add: priv->closed = false; No need for locking as enable_irq and work-structs work as barriers. > + enable_irq(priv->irq); > + > + return ret; > +} > + > +static void cap1106_close(struct input_dev *input_dev) > +{ > + struct cap1106_priv *priv = input_get_drvdata(input_dev); > + unsigned long flags; > + > + spin_lock_irqsave(&priv->lock, flags); > + disable_irq(priv->irq); > + cancel_work_sync(&priv->work); > + spin_unlock_irqrestore(&priv->lock, flags); This would become: spin_lock(&priv->lock); priv->closed = true; disable_irq(priv->irq); spin_unlock(&priv->lock); cancel_work_sync(&priv->work); Btw., no need for irqsave/irqrestore here, ->closed is called in user-context. Everything else looks good to me: Thanks David > + > + /* enable deep sleep */ > + regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, > + CAP1106_REG_MAIN_CONTROL_DLSEEP, > + CAP1106_REG_MAIN_CONTROL_DLSEEP); > +} > + > +static int cap1106_i2c_probe(struct i2c_client *i2c_client, > + const struct i2c_device_id *id) > +{ > + struct device *dev = &i2c_client->dev; > + struct device_node *child, *node; > + struct cap1106_priv *priv; > + unsigned int val, rev; > + u8 gain = 0; > + int i, ret; > + u32 tmp32; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->regmap = devm_regmap_init_i2c(i2c_client, &cap1106_regmap_config); > + if (IS_ERR(priv->regmap)) > + return PTR_ERR(priv->regmap); > + > + ret = regmap_read(priv->regmap, CAP1106_REG_PRODUCT_ID, &val); > + if (ret < 0) > + return ret; > + > + if (val != CAP1106_PRODUCT_ID) { > + dev_err(dev, "Product ID: Got 0x%02x, expected 0x%02x\n", > + val, CAP1106_PRODUCT_ID); > + return -ENODEV; > + } > + > + ret = regmap_read(priv->regmap, CAP1106_REG_MANUFACTURER_ID, &val); > + if (ret < 0) > + return ret; > + > + if (val != CAP1106_MANUFACTURER_ID) { > + dev_err(dev, "Manufacturer ID: Got 0x%02x, expected 0x%02x\n", > + val, CAP1106_MANUFACTURER_ID); > + return -ENODEV; > + } > + > + ret = regmap_read(priv->regmap, CAP1106_REG_REVISION, &rev); > + if (ret < 0) > + return ret; > + > + dev_info(dev, "CAP1106 detected, revision 0x%02x\n", rev); > + i2c_set_clientdata(i2c_client, priv); > + INIT_WORK(&priv->work, cap1106_work); > + spin_lock_init(&priv->lock); > + node = dev->of_node; > + > + if (!of_property_read_u32(node, "microchip,sensor-gain", &tmp32)) { > + if (is_power_of_2(tmp32) && tmp32 <= 8) > + gain = ilog2(tmp32); > + else > + dev_err(dev, "Invalid sensor-gain value %d\n", tmp32); > + } > + > + ret = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, > + CAP1106_REG_MAIN_CONTROL_GAIN_MASK, > + gain << CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT); > + if (ret < 0) > + return ret; > + > + /* disable autorepeat. The Linux input system has its own handling. */ > + ret = regmap_write(priv->regmap, CAP1106_REG_REPEAT_RATE, 0); > + if (ret < 0) > + return ret; > + > + /* set the defaults */ > + for (i = 0; i < CAP1106_NUM_CHN; i++) > + priv->keycodes[i] = KEY_A + i; > + > + i = 0; > + for_each_child_of_node(node, child) { > + if (i == CAP1106_NUM_CHN) { > + dev_err(dev, "Too many button nodes.\n"); > + return -EINVAL; > + } > + > + if (!of_property_read_u32(child, "linux,keycode", &tmp32)) > + priv->keycodes[i] = tmp32; > + > + i++; > + } > + > + priv->idev = devm_input_allocate_device(dev); > + if (!priv->idev) > + return -ENOMEM; > + > + priv->idev->name = "CAP1106 capacitive touch sensor"; > + priv->idev->id.bustype = BUS_I2C; > + priv->idev->evbit[0] = BIT_MASK(EV_KEY); > + > + if (of_get_property(node, "autorepeat", NULL)) > + __set_bit(EV_REP, priv->idev->evbit); > + > + for (i = 0; i < CAP1106_NUM_CHN; i++) { > + unsigned int code = priv->keycodes[i]; > + priv->idev->keybit[BIT_WORD(code)] |= BIT_MASK(code); > + } > + > + priv->idev->id.vendor = CAP1106_MANUFACTURER_ID; > + priv->idev->id.product = CAP1106_PRODUCT_ID; > + priv->idev->id.version = rev; > + > + priv->idev->open = cap1106_open; > + priv->idev->close = cap1106_close; > + > + input_set_drvdata(priv->idev, priv); > + > + ret = input_register_device(priv->idev); > + if (ret < 0) > + return ret; > + > + priv->irq = irq_of_parse_and_map(node, 0); > + if (!priv->irq) { > + dev_err(dev, "Unable to parse or map IRQ\n"); > + return -ENXIO; > + } > + > + ret = devm_request_irq(dev, priv->irq, cap1106_isr, IRQF_DISABLED, > + dev_name(dev), priv); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static int cap1106_i2c_remove(struct i2c_client *i2c_client) > +{ > + struct cap1106_priv *priv = i2c_get_clientdata(i2c_client); > + > + input_unregister_device(priv->idev); > + > + return 0; > +} > + > +static const struct of_device_id cap1106_dt_ids[] = { > + { .compatible = "microchip,cap1106", }, > + {} > +}; > +MODULE_DEVICE_TABLE(of, cap1106_dt_ids); > + > +static const struct i2c_device_id cap1106_i2c_ids[] = { > + { "cap1106", 0 }, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, cap1106_i2c_ids); > + > +static struct i2c_driver cap1106_i2c_driver = { > + .driver = { > + .name = "cap1106", > + .owner = THIS_MODULE, > + .of_match_table = cap1106_dt_ids, > + }, > + .id_table = cap1106_i2c_ids, > + .probe = cap1106_i2c_probe, > + .remove = cap1106_i2c_remove, > +}; > + > +module_i2c_driver(cap1106_i2c_driver); > + > +MODULE_ALIAS("platform:" DRV_NAME); > +MODULE_DESCRIPTION("Microchip CAP1106 driver"); > +MODULE_AUTHOR("Daniel Mack <linux@xxxxxxxxxx>"); > +MODULE_LICENSE("GPL v2"); > -- > 1.9.3 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-input" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html