@@ -346,6 +346,159 @@ static int cap11xx_init_leds(struct device *dev,
}
#endif
+/* register attribute:
+ * create a simple way to export properties in the register map
+ */
+struct register_attribute {
+ struct device_attribute dev_attr;
+ int reg;
+ int mask;
+ int shift;
+};
+#define to_reg_attr(_dev_attr) container_of((dev_attr), \
+ struct register_attribute, dev_attr)
+#define to_dev_attr(_attr) container_of(attr, struct device_attribute, attr)
+
+static ssize_t show_reg_attr(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct cap11xx_priv *priv = dev_get_drvdata(dev);
+ struct register_attribute *rattr = to_reg_attr(attr);
+ int ret, value;
+
+ ret = regmap_read(priv->regmap, rattr->reg, &value);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "0x%02x\n", (value >> rattr->shift) & rattr->mask);
+}
+
+static ssize_t store_reg_attr(struct device *dev, struct device_attribute * attr,
+ const char *buf, size_t len)
+{
+ struct cap11xx_priv *priv = dev_get_drvdata(dev);
+ struct register_attribute *rattr = to_reg_attr(attr);
+ int ret;
+ long value;
+
+ ret = kstrtoul(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value & ~rattr->mask)
+ return -ERANGE;
+
+ ret = regmap_update_bits(priv->regmap, rattr->reg,
+ rattr->mask << rattr->shift, value << rattr->shift);
+ if (ret)
+ return ret;
+ return len;
+}
+
+#define REG_ATTR(_name, _mode, _reg, _nbits, _shift) \
+ struct register_attribute reg_dev_attr_##_name = { \
+ __ATTR(_name, _mode, show_reg_attr, store_reg_attr), \
+ .reg = _reg, \
+ .mask = (1 << (_nbits)) -1, \
+ .shift = _shift, \
+ }
+#define TO_ATTR(_name) (&(reg_dev_attr_##_name).dev_attr.attr)
+
+static REG_ATTR(gain, 0644, CAP11XX_REG_MAIN_CONTROL, 2, 6);
+static REG_ATTR(delta_sense, 0644, CAP11XX_REG_SENSITIVITY_CONTROL, 3, 4);
+static REG_ATTR(base_shift, 0644, CAP11XX_REG_SENSITIVITY_CONTROL, 4, 0);
+static REG_ATTR(dis_dig_noise, 0644, CAP11XX_REG_CONFIG, 1, 5);
+static REG_ATTR(dis_ana_noise, 0644, CAP11XX_REG_CONFIG, 1, 4);
+static REG_ATTR(max_dur_enable, 0644, CAP11XX_REG_CONFIG, 1, 3);
+static REG_ATTR(cs_en, 0644, CAP11XX_REG_SENSOR_ENABLE, 8, 0);
+static REG_ATTR(max_dur, 0644, CAP11XX_REG_SENSOR_CONFIG, 4, 4);
+static REG_ATTR(avg, 0644, CAP11XX_REG_SAMPLING_CONFIG, 3, 4);
+static REG_ATTR(samp_time, 0644, CAP11XX_REG_SAMPLING_CONFIG, 2, 2);
+static REG_ATTR(cycle_time, 0644, CAP11XX_REG_SAMPLING_CONFIG, 2, 0);
+static REG_ATTR(mulkt_blk_en, 0644, CAP11XX_REG_MT_CONFIG, 1, 7);
+static REG_ATTR(b_mult_t, 0644, CAP11XX_REG_MT_CONFIG, 2, 2);
+static REG_ATTR(mtp_en, 0644, CAP11XX_REG_MT_PATTERN_CONFIG, 1, 7);
+static REG_ATTR(mtp_th, 0644, CAP11XX_REG_MT_PATTERN_CONFIG, 2, 2);
+static REG_ATTR(comp_ptrn, 0644, CAP11XX_REG_MT_PATTERN_CONFIG, 1, 1);
+static REG_ATTR(cs_ptrn, 0644, CAP11XX_REG_MT_PATTERN, 8, 0);
+static REG_ATTR(cs_th1, 0644, CAP11XX_REG_SENSOR_THRESH(0), 8, 0);
+static REG_ATTR(cs_th2, 0644, CAP11XX_REG_SENSOR_THRESH(1), 8, 0);
+static REG_ATTR(cs_th3, 0644, CAP11XX_REG_SENSOR_THRESH(2), 8, 0);
+static REG_ATTR(cs_th4, 0644, CAP11XX_REG_SENSOR_THRESH(3), 8, 0);
+static REG_ATTR(cs_th5, 0644, CAP11XX_REG_SENSOR_THRESH(4), 8, 0);
+static REG_ATTR(cs_th6, 0644, CAP11XX_REG_SENSOR_THRESH(5), 8, 0);
+static REG_ATTR(cs_th7, 0644, CAP11XX_REG_SENSOR_THRESH(6), 8, 0);
+static REG_ATTR(cs_th8, 0644, CAP11XX_REG_SENSOR_THRESH(7), 8, 0);
+static REG_ATTR(cs_bn_th, 0644, CAP11XX_REG_SENSOR_NOISE_THRESH, 2, 0);
+static REG_ATTR(cs_led, 0644, CAP11XX_REG_SENSOR_LED_LINK, 8, 0);
+
+static struct attribute *reg_attrs[] = {
+ TO_ATTR(gain),
+ TO_ATTR(delta_sense),
+ TO_ATTR(base_shift),
+ TO_ATTR(dis_dig_noise),
+ TO_ATTR(dis_ana_noise),
+ TO_ATTR(max_dur_enable),
+ TO_ATTR(cs_en),
+ TO_ATTR(max_dur),
+ TO_ATTR(avg),
+ TO_ATTR(samp_time),
+ TO_ATTR(cycle_time),
+ TO_ATTR(mulkt_blk_en),
+ TO_ATTR(b_mult_t),
+ TO_ATTR(mtp_en),
+ TO_ATTR(mtp_th),
+ TO_ATTR(comp_ptrn),
+ TO_ATTR(cs_ptrn),
+ TO_ATTR(cs_th1),
+ TO_ATTR(cs_th2),
+ TO_ATTR(cs_th3),
+ TO_ATTR(cs_th4),
+ TO_ATTR(cs_th5),
+ TO_ATTR(cs_th6),
+ TO_ATTR(cs_th7),
+ TO_ATTR(cs_th8),
+ TO_ATTR(cs_bn_th),
+ TO_ATTR(cs_led),
+ NULL,
+};
+
+static const struct attribute_group reg_attr_group = {
+ .name = "reg",
+ .attrs = reg_attrs,
+};
+
+/* load register properties from OF */
+static int cap11xx_load_regs_from_of(struct device *dev)
+{
+ struct cap11xx_priv *priv = dev_get_drvdata(dev);
+ struct attribute **attr;
+ struct register_attribute *rattr;
+ char name[32];
+ int ret, value;
+
+ for (attr = reg_attrs; *attr; ++attr) {
+ rattr = to_reg_attr(to_dev_attr(*attr));
+
+ sprintf(name, "reg,%s", rattr->dev_attr.attr.name);
+ ret = of_property_read_u32(dev->of_node, name, &value);
+ if (ret)
+ continue;
+ if (value & ~rattr->mask) {
+ dev_warn(dev, "of %s: value %u, max %u\n",
+ name, value, rattr->mask);
+ continue;
+ }
+ ret = regmap_update_bits(priv->regmap, rattr->reg,
+ rattr->mask << rattr->shift,
+ value << rattr->shift);
+ if (ret) {
+ dev_err(dev, "of %s, regmap returned %i\n", name, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
static int cap11xx_i2c_remove(struct i2c_client *i2c_client)
{
struct cap11xx_priv *priv = i2c_get_clientdata(i2c_client);
@@ -430,6 +583,10 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
return error;
}
+ error = cap11xx_load_regs_from_of(dev);
+ if (error)
+ return error;
+
/* Provide some useful defaults */
for (i = 0; i < cap->num_channels; i++)
priv->keycodes[i] = KEY_A + i;
@@ -491,6 +648,10 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
if (error)
return error;
+ error = devm_device_add_group(dev, ®_attr_group);
+ if (error)
+ return error;
+
irq = irq_of_parse_and_map(node, 0);
if (!irq) {
if (!of_property_read_bool(node, "linux,irq-poll")) {
Signed-off-by: Kurt Van Dijck <dev.kurt@vandijck-laurijssen.be> --- drivers/input/keyboard/cap11xx.c | 161 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+)