Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754142Ab2KIPcR (ORCPT ); Fri, 9 Nov 2012 10:32:17 -0500 Received: from mail-pb0-f46.google.com ([209.85.160.46]:59443 "EHLO mail-pb0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750922Ab2KIPcM (ORCPT ); Fri, 9 Nov 2012 10:32:12 -0500 From: Viresh Kumar To: sameo@linux.intel.com Cc: devicetree-discuss@lists.ozlabs.org, linux-kernel@vger.kernel.org, spear-devel@list.st.com, Vipul Kumar Samar , Viresh Kumar Subject: [PATCH 2/2] mfd: stmpe: Add DT support in stmpe driver Date: Fri, 9 Nov 2012 21:01:55 +0530 Message-Id: <52354a3da05aca599f47ad06c1b291ebc4f87c84.1352475033.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 1.7.12.rc2.18.g61b472e In-Reply-To: <07672d0597e23fbc580843193415b89e81dba326.1352475033.git.viresh.kumar@linaro.org> References: <07672d0597e23fbc580843193415b89e81dba326.1352475033.git.viresh.kumar@linaro.org> In-Reply-To: <07672d0597e23fbc580843193415b89e81dba326.1352475033.git.viresh.kumar@linaro.org> References: <07672d0597e23fbc580843193415b89e81dba326.1352475033.git.viresh.kumar@linaro.org> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15686 Lines: 538 From: Vipul Kumar Samar This patch adds support to probe stmpe devices via DT. Bindings are mentioned in binding document. Signed-off-by: Vipul Kumar Samar Signed-off-by: Viresh Kumar --- Documentation/devicetree/bindings/mfd/stmpe.txt | 121 +++++++++++ drivers/mfd/stmpe-i2c.c | 23 ++- drivers/mfd/stmpe-spi.c | 15 ++ drivers/mfd/stmpe.c | 264 +++++++++++++++++++++++- 4 files changed, 418 insertions(+), 5 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/stmpe.txt diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt new file mode 100644 index 0000000..2986129 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -0,0 +1,121 @@ +ST Microelectronics STMPE family +-------------------------------- + +STMPE is an MFD device which may expose following inbuilt devices: gpio, keypad, +touchscreen, adc, pwm, rotator. + +stmpe: +----- +Required properties: +- 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 + +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: bool, bool to enable/disable stmpe autosleep +- autosleep-timeout: inactivity timeout in milliseconds for autosleep +- interrupts: interrupt number of the device, if interrupt is not via gpio +- 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) + +stmpe-keypad: +-------------- +Required properties in addition to those specified by the shared matrix-keyboard +bindings: +- compatible: Must be "stmpe,keypad" + +Optional properties: +- 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 { + <...> + stmpe610@0 { + status = "okay"; + compatible = "st,stmpe610"; + reg = <0>; + + <.. spi controller specific data ..> + + id = <0>; + irq-over-gpio; + irq-gpios = <&gpio1 6 0x4>; + irq-trigger = <0x2>; + + 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 #include #include +#include #include #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 #include #include +#include #include #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 761348c..aa5f0094 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -8,10 +8,14 @@ */ #include +#include #include #include #include #include +#include +#include +#include #include #include #include @@ -965,6 +969,245 @@ static int __devinit stmpe_add_device(struct stmpe *stmpe, NULL, stmpe->irq_base + irq, NULL); } +#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) +{ + static struct irq_domain *stmpe_irq_domain; + enum of_gpio_flags flags; + int irq_base; + + irq_base = irq_alloc_descs(-1, 0, STMPE_NR_IRQS, 0); + if (IS_ERR_VALUE(irq_base)) { + dev_err(dev, "%s: irq desc alloc failed\n", __func__); + return -ENXIO; + } + + stmpe_irq_domain = irq_domain_add_legacy(np, STMPE_NR_IRQS, irq_base, + 0, &irq_domain_simple_ops, NULL); + if (!stmpe_irq_domain) { + dev_warn(dev, "%s: irq domain init failed\n", __func__); + irq_free_descs(irq_base, 32); + return -ENXIO; + } + + pdata->irq_base = irq_find_mapping(stmpe_irq_domain, 0); + + 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_bool(np, "autosleep")) { + pdata->autosleep = true; + + of_property_read_u32(np, "autosleep-timeout", + &pdata->autosleep_timeout); + } + + 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, block->irq); + 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; @@ -996,11 +1239,25 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe) int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) { struct stmpe_platform_data *pdata = dev_get_platdata(ci->dev); + struct device_node *np = ci->dev->of_node; struct stmpe *stmpe; int ret; - if (!pdata) + if (np) { + pdata = devm_kzalloc(ci->dev, sizeof(*pdata), GFP_KERNEL); + if (pdata) { + ret = stmpe_probe_config_dt(ci->dev, pdata, np); + if (ret) { + dev_err(ci->dev, "probe_config_dt failed\n"); + return ret; + } + } + } + + if (!pdata) { + dev_err(ci->dev, "no platform data\n"); return -EINVAL; + } stmpe = devm_kzalloc(ci->dev, sizeof(struct stmpe), GFP_KERNEL); if (!stmpe) @@ -1070,7 +1327,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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/