[PATCH V2 2/2] mfd: stmpe: Extend DT support in stmpe driver

From: Viresh Kumar
Date: Thu Nov 22 2012 - 13:28:50 EST


From: Vipul Kumar Samar <vipulkumar.samar@xxxxxx>

This patch extends existing DT support for stmpe devices. Bindings are mentioned
in binding document.

Signed-off-by: Vipul Kumar Samar <vipulkumar.samar@xxxxxx>
Signed-off-by: Viresh Kumar <viresh.kumar@xxxxxxxxxx>
---
V1->V2:
------
- Partial DT support was already there, which i missed earlier.
- Now, this patch is an enhancement of existing DT support.

Documentation/devicetree/bindings/mfd/stmpe.txt | 127 ++++++++++--
drivers/mfd/stmpe-i2c.c | 23 ++-
drivers/mfd/stmpe-spi.c | 15 ++
drivers/mfd/stmpe.c | 264 +++++++++++++++++++++---
4 files changed, 384 insertions(+), 45 deletions(-)

diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt
index 8f0aeda..44aebf3 100644
--- a/Documentation/devicetree/bindings/mfd/stmpe.txt
+++ b/Documentation/devicetree/bindings/mfd/stmpe.txt
@@ -1,25 +1,124 @@
-* STMPE Multi-Functional Device
+ST Microelectronics STMPE Multi-Functional Device
+-------------------------------------------------

+STMPE is an MFD device which may expose following inbuilt devices: gpio, keypad,
+touchscreen, adc, pwm, rotator.
+
+stmpe:
+-----
Required properties:
- - compatible : "st,stmpe[811|1601|2401|2403]"
- - reg : I2C address of the device
+- compatible: Must be one of: "st,stmpe610", "st,stmpe801", "st,stmpe811",
+ "st,stmpe1601", "st,stmpe2401", "st,stmpe2403",
+- id: device id to distinguish between multiple STMPEs on the same board
+- reg: I2C/SPI address of the device
+
+Optional properties:
+- irq-trigger: IRQ trigger to use for the interrupt to the host
+- irq-invert-polarity: bool, IRQ line is connected with reversed polarity
+- autosleep-timeout: inactivity timeout in milliseconds for autosleep. Valid
+ entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024
+- interrupts: interrupt number of the device, if interrupt is not via gpio
+- interrupt-parent: Specifies which IRQ controller we're connected to
+- irq-over-gpio: bool, true if gpio is used to get irq
+- irq-gpios: gpio number over which irq will be requested (significant only if
+ irq-over-gpio is true)
+- i2c-client-wake: Marks the input device as wakable
+
+stmpe-keypad:
+--------------
+Required properties in addition to those specified by the shared matrix-keyboard
+bindings:
+- compatible: Must be "stmpe,keypad"

Optional properties:
- - interrupts : The interrupt outputs from the controller
- - interrupt-controller : Marks the device node as an interrupt controller
- - interrupt-parent : Specifies which IRQ controller we're connected to
- - i2c-client-wake : Marks the input device as wakable
- - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024
+- keypad,scan-count: number of key scanning cycles to confirm key data. Maximum
+ is STMPE_KEYPAD_MAX_SCAN_COUNT.
+- keypad,debounce-ms: debounce interval, in ms. Maximum is
+ STMPE_KEYPAD_MAX_DEBOUNCE.
+- keypad,no-autorepeat: bool, disable key autorepeat
+
+stmpe-gpio:
+-----------
+Required properties:
+- compatible: Must be "stmpe,gpio"
+
+Optional properties:
+- norequest-mask: bitmask specifying which GPIOs should _not_ be requestable due
+ to different usage (e.g. touch, keypad) STMPE_GPIO_NOREQ_* macros can be used
+ here.
+
+stmpe-ts:
+-----------
+Required properties:
+- compatible: Must be "stmpe,ts"
+
+Optional properties:
+- sample-time: ADC converstion time in number of clock. (0 -> 36 clocks, 1 ->
+ 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks, 4 -> 80 clocks, 5 -> 96 clocks, 6
+ -> 144 clocks), recommended is 4.
+- mod-12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC)
+- ref-sel: ADC reference source (0 -> internal reference, 1 -> external
+ reference)
+- adc-freq: ADC Clock speed (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz)
+- ave-ctrl: Sample average control (0 -> 1 sample, 1 -> 2 samples, 2 -> 4
+ samples, 3 -> 8 samples)
+- touch-det-delay: Touch detect interrupt delay (0 -> 10 us, 1 -> 50 us, 2 ->
+ 100 us, 3 -> 500 us, 4-> 1 ms, 5 -> 5 ms, 6 -> 10 ms, 7 -> 50 ms) recommended
+ is 3
+- settling: Panel driver settling time (0 -> 10 us, 1 -> 100 us, 2 -> 500 us, 3
+ -> 1 ms, 4 -> 5 ms, 5 -> 10 ms, 6 for 50 ms, 7 -> 100 ms) recommended is 2
+- fraction-z: Length of the fractional part in z (fraction-z ([0..7]) = Count of
+ the fractional part) recommended is 7
+- i-drive: current limit value of the touchscreen drivers (0 -> 20 mA typical 35
+ mA max, 1 -> 50 mA typical 80 mA max)
+
+stmpe-adc:
+-----------
+Required properties:
+- compatible: Must be "stmpe,adc"
+
+stmpe-pwm:
+-----------
+Required properties:
+- compatible: Must be "stmpe,pwm"
+
+stmpe-rotator:
+-----------
+Required properties:
+- compatible: Must be "stmpe,rotator"

Example:
+-------
+stmpe can be present over i2c or spi bus, so its node would be added as child
+node of either of spi or i2c device.
+
+stmpe-devices should be added as child nodes of parent stmpe node.

+spi@e0100000 {
+ <...>
stmpe1601: stmpe1601@40 {
+ status = "okay";
compatible = "st,stmpe1601";
- reg = <0x40>;
- interrupts = <26 0x4>;
- interrupt-parent = <&gpio6>;
- interrupt-controller;
+ reg = <0>;
+
+ <.. spi controller specific data ..>
+
+ id = <0>;
+ irq-over-gpio;
+ irq-gpios = <&gpio1 6 0x4>;
+ irq-trigger = <0x2>;

- i2c-client-wake;
- st,autosleep-timeout = <1024>;
+ stmpe610-ts {
+ compatible = "stmpe,ts";
+ ts,sample-time = <4>;
+ ts,mod-12b = <1>;
+ ts,ref-sel = <0>;
+ ts,adc-freq = <1>;
+ ts,ave-ctrl = <1>;
+ ts,touch-det-delay = <2>;
+ ts,settling = <2>;
+ ts,fraction-z = <7>;
+ ts,i-drive = <1>;
+ };
};
+};
diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c
index 947a06a..aaa2da7 100644
--- a/drivers/mfd/stmpe-i2c.c
+++ b/drivers/mfd/stmpe-i2c.c
@@ -13,6 +13,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/types.h>
#include "stmpe.h"

@@ -81,12 +82,28 @@ static const struct i2c_device_id stmpe_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, stmpe_id);

+#ifdef CONFIG_OF
+static const struct of_device_id stmpe_dt_ids[] = {
+ { .compatible = "st,stmpe610", .data = &stmpe_i2c_id[0], },
+ { .compatible = "st,stmpe801", .data = &stmpe_i2c_id[1], },
+ { .compatible = "st,stmpe811", .data = &stmpe_i2c_id[2], },
+ { .compatible = "st,stmpe1601", .data = &stmpe_i2c_id[3], },
+ { .compatible = "st,stmpe2401", .data = &stmpe_i2c_id[4], },
+ { .compatible = "st,stmpe2403", .data = &stmpe_i2c_id[5], },
+ { }
+};
+MODULE_DEVICE_TABLE(of, stmpe_dt_ids);
+#endif
+
static struct i2c_driver stmpe_i2c_driver = {
- .driver.name = "stmpe-i2c",
- .driver.owner = THIS_MODULE,
+ .driver = {
+ .name = "stmpe-i2c",
+ .owner = THIS_MODULE,
#ifdef CONFIG_PM
- .driver.pm = &stmpe_dev_pm_ops,
+ .pm = &stmpe_dev_pm_ops,
#endif
+ .of_match_table = of_match_ptr(stmpe_dt_ids),
+ },
.probe = stmpe_i2c_probe,
.remove = __devexit_p(stmpe_i2c_remove),
.id_table = stmpe_i2c_id,
diff --git a/drivers/mfd/stmpe-spi.c b/drivers/mfd/stmpe-spi.c
index 9edfe86..1e2bff0 100644
--- a/drivers/mfd/stmpe-spi.c
+++ b/drivers/mfd/stmpe-spi.c
@@ -11,6 +11,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/types.h>
#include "stmpe.h"

@@ -119,6 +120,19 @@ static const struct spi_device_id stmpe_spi_id[] = {
};
MODULE_DEVICE_TABLE(spi, stmpe_id);

+#ifdef CONFIG_OF
+static const struct of_device_id stmpe_dt_ids[] = {
+ { .compatible = "st,stmpe610", .data = (void *)STMPE610, },
+ { .compatible = "st,stmpe801", .data = (void *)STMPE801, },
+ { .compatible = "st,stmpe811", .data = (void *)STMPE811, },
+ { .compatible = "st,stmpe1601", .data = (void *)STMPE1601, },
+ { .compatible = "st,stmpe2401", .data = (void *)STMPE2401, },
+ { .compatible = "st,stmpe2403", .data = (void *)STMPE2403, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, stmpe_dt_ids);
+#endif
+
static struct spi_driver stmpe_spi_driver = {
.driver = {
.name = "stmpe-spi",
@@ -126,6 +140,7 @@ static struct spi_driver stmpe_spi_driver = {
#ifdef CONFIG_PM
.pm = &stmpe_dev_pm_ops,
#endif
+ .of_match_table = of_match_ptr(stmpe_dt_ids),
},
.probe = stmpe_spi_probe,
.remove = __devexit_p(stmpe_spi_remove),
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index c0df4b9..d69f24b 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -8,11 +8,14 @@
*/

#include <linux/gpio.h>
+#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/mfd/core.h>
@@ -981,6 +984,230 @@ static int __devinit stmpe_add_device(struct stmpe *stmpe,
NULL, stmpe->irq_base, stmpe->domain);
}

+#ifdef CONFIG_OF
+static const struct of_device_id stmpe_keypad_ids[] = {
+ { .compatible = "stmpe,keypad" },
+ {},
+};
+
+static const struct of_device_id stmpe_gpio_ids[] = {
+ { .compatible = "stmpe,gpio" },
+ {},
+};
+
+static const struct of_device_id stmpe_ts_ids[] = {
+ { .compatible = "stmpe,ts" },
+ {},
+};
+
+static const struct of_device_id stmpe_adc_ids[] = {
+ { .compatible = "stmpe,adc" },
+ {},
+};
+
+static const struct of_device_id stmpe_pwm_ids[] = {
+ { .compatible = "stmpe,pwm" },
+ {},
+};
+
+static const struct of_device_id stmpe_rotator_ids[] = {
+ { .compatible = "stmpe,rotator" },
+ {},
+};
+
+static struct stmpe_keypad_platform_data *
+get_keyboard_pdata_dt(struct device *dev, struct device_node *np)
+{
+ struct stmpe_keypad_platform_data *pdata;
+ u32 val;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_warn(dev, "stmpe keypad kzalloc fail\n");
+ return NULL;
+ }
+
+ if (!of_property_read_u32(np, "keypad,scan-count", &val))
+ pdata->scan_count = val;
+ if (!of_property_read_u32(np, "keypad,debounce-ms", &val))
+ pdata->debounce_ms = val;
+ if (of_property_read_bool(np, "keypad,no-autorepeat"))
+ pdata->no_autorepeat = true;
+
+ return pdata;
+}
+
+static struct stmpe_gpio_platform_data *get_gpio_pdata_dt(struct device *dev,
+ struct device_node *np)
+{
+ struct stmpe_gpio_platform_data *pdata;
+ u32 val;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_warn(dev, "stmpe gpio kzalloc fail\n");
+ return NULL;
+ }
+
+ if (!of_property_read_u32(np, "gpio,norequest-mask", &val))
+ pdata->norequest_mask = val;
+
+ /* assign gpio numbers dynamically */
+ pdata->gpio_base = -1;
+
+ return pdata;
+}
+
+static struct stmpe_ts_platform_data *get_ts_pdata_dt(struct device *dev,
+ struct device_node *np)
+{
+ struct stmpe_ts_platform_data *pdata;
+ u32 val;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_warn(dev, "stmpe ts kzalloc fail\n");
+ return NULL;
+ }
+
+ if (!of_property_read_u32(np, "ts,sample-time", &val))
+ pdata->sample_time = val;
+ if (!of_property_read_u32(np, "ts,mod-12b", &val))
+ pdata->mod_12b = val;
+ if (!of_property_read_u32(np, "ts,ref-sel", &val))
+ pdata->ref_sel = val;
+ if (!of_property_read_u32(np, "ts,adc-freq", &val))
+ pdata->adc_freq = val;
+ if (!of_property_read_u32(np, "ts,ave-ctrl", &val))
+ pdata->ave_ctrl = val;
+ if (!of_property_read_u32(np, "ts,touch-det-delay", &val))
+ pdata->touch_det_delay = val;
+ if (!of_property_read_u32(np, "ts,settling", &val))
+ pdata->settling = val;
+ if (!of_property_read_u32(np, "ts,fraction-z", &val))
+ pdata->fraction_z = val;
+ if (!of_property_read_u32(np, "ts,i-drive", &val))
+ pdata->i_drive = val;
+
+ return pdata;
+}
+
+static struct stmpe_variant_block *
+match_variant_block(struct stmpe_variant_info *variant,
+ enum stmpe_block blocks)
+{
+ int i;
+
+ for (i = 0; i < variant->num_blocks; i++) {
+ struct stmpe_variant_block *block = &variant->blocks[i];
+
+ if (blocks & block->block)
+ return block;
+ }
+ return NULL;
+}
+
+static int stmpe_probe_config_dt(struct device *dev,
+ struct stmpe_platform_data *pdata, struct device_node *np)
+{
+ enum of_gpio_flags flags;
+
+ pdata->irq_base = irq_alloc_descs(-1, 0, STMPE_NR_IRQS, 0);
+ if (IS_ERR_VALUE(pdata->irq_base)) {
+ dev_err(dev, "%s: irq desc alloc failed\n", __func__);
+ return -ENXIO;
+ }
+
+ if (of_property_read_bool(np, "irq_over_gpio")) {
+ pdata->irq_over_gpio = true;
+
+ pdata->irq_gpio = of_get_named_gpio_flags(np, "irq-gpios", 0,
+ &flags);
+ if (!pdata->irq_gpio)
+ dev_dbg(dev, "unable to get irq_gpio from %s.",
+ __func__);
+ }
+
+ if (of_property_read_u32(np, "irq-trigger", &pdata->irq_trigger))
+ dev_dbg(dev, "unable to get irq_trigger\n");
+
+ if (of_property_read_bool(np, "irq-invert-polarity"))
+ pdata->irq_invert_polarity = true;
+
+ if (!of_property_read_u32(np, "autosleep-timeout",
+ &pdata->autosleep_timeout))
+ pdata->autosleep = true;
+
+ if (of_property_read_u32(np, "id", &pdata->id))
+ dev_dbg(dev, "unable to get id\n");
+
+ return 0;
+}
+
+static int stmpe_of_devices_init(struct stmpe *stmpe)
+{
+ struct stmpe_variant_info *variant = stmpe->variant;
+ struct device_node *nc, *np = stmpe->dev->of_node;
+ struct stmpe_variant_block *block;
+ enum stmpe_block blockid;
+ int ret = -EINVAL;
+
+ for_each_child_of_node(np, nc) {
+ if (of_match_node(stmpe_keypad_ids, nc)) {
+ blockid = STMPE_BLOCK_KEYPAD;
+ stmpe->pdata->keypad = get_keyboard_pdata_dt(stmpe->dev,
+ nc);
+ if (!stmpe->pdata->keypad)
+ return -ENOMEM;
+ } else if (of_match_node(stmpe_gpio_ids, nc)) {
+ blockid = STMPE_BLOCK_GPIO;
+ stmpe->pdata->gpio = get_gpio_pdata_dt(stmpe->dev, nc);
+ if (!stmpe->pdata->gpio)
+ return -ENOMEM;
+ } else if (of_match_node(stmpe_ts_ids, nc)) {
+ blockid = STMPE_BLOCK_TOUCHSCREEN;
+ stmpe->pdata->ts = get_ts_pdata_dt(stmpe->dev, nc);
+ if (!stmpe->pdata->ts)
+ return -ENOMEM;
+ } else if (of_match_node(stmpe_adc_ids, nc)) {
+ blockid = STMPE_BLOCK_ADC;
+ } else if (of_match_node(stmpe_pwm_ids, nc)) {
+ blockid = STMPE_BLOCK_PWM;
+ } else if (of_match_node(stmpe_rotator_ids, nc)) {
+ blockid = STMPE_BLOCK_ROTATOR;
+ } else {
+ dev_warn(stmpe->dev, "no matching device node found\n");
+ return -EINVAL;
+ }
+
+ block = match_variant_block(variant, blockid);
+ if (!block) {
+ dev_err(stmpe->dev, "variant doesn't support blockid: %d\n",
+ blockid);
+ continue;
+ }
+
+ ret = stmpe_add_device(stmpe, block->cell);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+#else
+static inline int stmpe_probe_config_dt(struct stmpe_platform_data *pdata,
+ struct device_node *np)
+{
+ return -ENODEV;
+}
+
+static inline int stmpe_of_devices_init(struct stmpe *stmpe)
+{
+ return -ENODEV;
+}
+#endif
+
static int __devinit stmpe_devices_init(struct stmpe *stmpe)
{
struct stmpe_variant_info *variant = stmpe->variant;
@@ -1017,32 +1244,6 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe)
return ret;
}

-void __devinit stmpe_of_probe(struct stmpe_platform_data *pdata,
- struct device_node *np)
-{
- struct device_node *child;
-
- of_property_read_u32(np, "st,autosleep-timeout",
- &pdata->autosleep_timeout);
-
- pdata->autosleep = (pdata->autosleep_timeout) ? true : false;
-
- for_each_child_of_node(np, child) {
- if (!strcmp(child->name, "stmpe_gpio")) {
- pdata->blocks |= STMPE_BLOCK_GPIO;
- }
- if (!strcmp(child->name, "stmpe_keypad")) {
- pdata->blocks |= STMPE_BLOCK_KEYPAD;
- }
- if (!strcmp(child->name, "stmpe_touchscreen")) {
- pdata->blocks |= STMPE_BLOCK_TOUCHSCREEN;
- }
- if (!strcmp(child->name, "stmpe_adc")) {
- pdata->blocks |= STMPE_BLOCK_ADC;
- }
- }
-}
-
/* Called from client specific probe routines */
int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
{
@@ -1059,7 +1260,11 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
if (!pdata)
return -ENOMEM;

- stmpe_of_probe(pdata, np);
+ ret = stmpe_probe_config_dt(ci->dev, pdata, np);
+ if (ret) {
+ dev_err(ci->dev, "probe_config_dt failed\n");
+ return ret;
+ }
}

stmpe = devm_kzalloc(ci->dev, sizeof(struct stmpe), GFP_KERNEL);
@@ -1130,7 +1335,10 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
}
}

- ret = stmpe_devices_init(stmpe);
+ if (np)
+ ret = stmpe_of_devices_init(stmpe);
+ else
+ ret = stmpe_devices_init(stmpe);
if (!ret)
return 0;

--
1.7.12.rc2.18.g61b472e


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/