diff options
| author | a1205z <a1205z@motorola.com> | 2013-12-13 10:02:53 -0600 |
|---|---|---|
| committer | James Wylder <jwylder@motorola.com> | 2014-03-05 17:46:58 -0600 |
| commit | 25372d59ec0eddc27d6025be01e0f402657c96c6 (patch) | |
| tree | b2b88b109a9b8bd401b442132571e674c3ed72bb | |
| parent | c4d75a7ed02adc3dc7fd471f4b1c63df6d3b227a (diff) | |
| download | olio-linux-3.10-25372d59ec0eddc27d6025be01e0f402657c96c6.tar.xz olio-linux-3.10-25372d59ec0eddc27d6025be01e0f402657c96c6.zip | |
IKXCLOCK-50: Bring up PMIC TPS interrupt
Bring up PMIC TPS interrupt for power key detection
Change-Id: I405c19c04ab0d9308308f41377f9ce25deb732e8
Signed-off-by: a1205z <a1205z@motorola.com>
Reviewed-on: http://gerrit.pcs.mot.com/590472
SLTApproved: Slta Waiver <sltawvr@motorola.com>
Tested-by: Jira Key <jirakey@motorola.com>
Reviewed-by: Douglas Zobel <dzobel1@motorola.com>
Submit-Approved: Jira Key <jirakey@motorola.com>
| -rw-r--r-- | arch/arm/boot/dts/omap3-minnow-p0.dts | 2 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/board-minnow-spi.c | 2 | ||||
| -rw-r--r-- | drivers/mfd/Makefile | 2 | ||||
| -rw-r--r-- | drivers/mfd/tps65912-core.c | 37 | ||||
| -rw-r--r-- | drivers/mfd/tps65912-irq.c | 51 | ||||
| -rw-r--r-- | drivers/mfd/tps65912-key.c | 132 | ||||
| -rw-r--r-- | include/linux/mfd/tps65912.h | 14 |
7 files changed, 232 insertions, 8 deletions
diff --git a/arch/arm/boot/dts/omap3-minnow-p0.dts b/arch/arm/boot/dts/omap3-minnow-p0.dts index 0b0f2ccdfa0..917a7a2e70e 100644 --- a/arch/arm/boot/dts/omap3-minnow-p0.dts +++ b/arch/arm/boot/dts/omap3-minnow-p0.dts @@ -27,6 +27,8 @@ dcdc1_avs; dcdc4_avs; + tps_irq_gpio = <0>; + regulators { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm/mach-omap2/board-minnow-spi.c b/arch/arm/mach-omap2/board-minnow-spi.c index bc0a1abd38c..5d0f2815c8d 100644 --- a/arch/arm/mach-omap2/board-minnow-spi.c +++ b/arch/arm/mach-omap2/board-minnow-spi.c @@ -114,7 +114,7 @@ unsigned short cpcap_regulator_off_mode_values[CPCAP_NUM_REGULATORS] = { [CPCAP_VAUDIO] = 0x0000, }; -#define CPCAP_GPIO 0 +#define CPCAP_GPIO 1 struct regulator_consumer_supply cpcap_sw4_consumers[] = { REGULATOR_SUPPLY("sw4", NULL /* DSP */), diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b11b8bf992f..254ffaabdb4 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -60,7 +60,7 @@ obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MFD_TPS65217) += tps65217.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o -tps65912-objs := tps65912-core.o tps65912-irq.o +tps65912-objs := tps65912-core.o tps65912-irq.o tps65912-key.o obj-$(CONFIG_MFD_TPS65912) += tps65912.o obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c index 6165c1fc36d..f0bcdce6df2 100644 --- a/drivers/mfd/tps65912-core.c +++ b/drivers/mfd/tps65912-core.c @@ -21,6 +21,7 @@ #include <linux/mfd/core.h> #include <linux/mfd/tps65912.h> #include <linux/of_device.h> +#include <linux/irq.h> static struct mfd_cell tps65912s[] = { { @@ -28,6 +29,12 @@ static struct mfd_cell tps65912s[] = { }, }; +static struct platform_device tps65912_key_device = { + .name = "tps65912_key", + .id = -1, + .dev.platform_data = NULL, +}; + int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask) { u8 data; @@ -127,6 +134,7 @@ static struct tps65912_board *tps65912_parse_dt(struct tps65912 *tps65912) { struct device_node *np = tps65912->dev->of_node; struct tps65912_board *board_info; + int tps_irq_gpio, ret; board_info = kzalloc(sizeof(struct tps65912_board), GFP_KERNEL); if (!board_info) { @@ -154,9 +162,25 @@ static struct tps65912_board *tps65912_parse_dt(struct tps65912 *tps65912) pr_info("dcdc4_avs is 1\n"); } - board_info->irq = tps65912->irq_num; - board_info->irq_base = 0; + if (!of_property_read_u32(np,"tps_irq_gpio",&tps_irq_gpio)) { + ret = gpio_request(tps_irq_gpio, "tps65912-irq"); + if (ret) + goto err; + ret = gpio_direction_input(tps_irq_gpio); + if (ret) { + gpio_free(tps_irq_gpio); + goto err; + } + + board_info->irq = __gpio_to_irq(tps_irq_gpio); + pr_info("tps_irq_gpio:%d irq:%d\n", tps_irq_gpio, board_info->irq); + } + board_info->irq_base = irq_alloc_descs(-1, 0, TPS65912_NUM_IRQ, 0); + pr_info("irq_base:%d\n", board_info->irq_base); return board_info; +err: + kfree(board_info); + return NULL; } #else static inline @@ -207,11 +231,16 @@ int tps65912_device_init(struct tps65912 *tps65912) if (ret < 0) goto err; + tps65912_key_device.dev.platform_data = tps65912; + ret = platform_device_register(&tps65912_key_device); + if (ret < 0) + goto err; + init_data->irq = pmic_plat_data->irq; init_data->irq_base = pmic_plat_data->irq_base; ret = tps65912_irq_init(tps65912, init_data->irq, init_data); if (ret < 0) - goto err; + goto err_irq; #ifdef CONFIG_MFD_TPS65912_DEBUGFS ret = tps65912_debugfs_create(tps65912); @@ -226,6 +255,8 @@ int tps65912_device_init(struct tps65912 *tps65912) err_debugfs: #endif tps65912_irq_exit(tps65912); +err_irq: + platform_device_unregister(&tps65912_key_device); err: kfree(init_data); kfree(pmic_plat_data); diff --git a/drivers/mfd/tps65912-irq.c b/drivers/mfd/tps65912-irq.c index d360a83a273..0e696c41bfe 100644 --- a/drivers/mfd/tps65912-irq.c +++ b/drivers/mfd/tps65912-irq.c @@ -22,6 +22,13 @@ #include <linux/irq.h> #include <linux/gpio.h> #include <linux/mfd/tps65912.h> +#include <linux/input.h> + +enum pwrkey_states { + PWRKEY_RELEASE, /* Power key released state. */ + PWRKEY_PRESS, /* Power key pressed state. */ + PWRKEY_UNKNOWN, /* Unknown power key state. */ +}; static inline int irq_to_tps65912_irq(struct tps65912 *tps65912, int irq) @@ -29,6 +36,30 @@ static inline int irq_to_tps65912_irq(struct tps65912 *tps65912, return irq - tps65912->irq_base; } +/* this powerkey_handler runs in process contextt, not in interrupt context*/ +static irqreturn_t powerkey_handler(int irq, void *irq_data) +{ + struct tps65912 *tps65912 = irq_data; + u32 new_state; + + if (irq - tps65912->irq_base == TPS65912_IRQ_GPIO3_R) { + new_state = PWRKEY_RELEASE; + } else if (irq - tps65912->irq_base == TPS65912_IRQ_GPIO3_F) { + new_state = PWRKEY_PRESS; + } else { + pr_info("incorrect interrupt %d for power key\n", + irq - tps65912->irq_base); + return IRQ_HANDLED; + } + + mutex_lock(&tps65912->irq_lock); + if (new_state != tps65912->powerkey_state) { + tps65912_broadcast_key_event(tps65912, KEY_END, new_state); + tps65912->powerkey_state = new_state; + } + mutex_unlock(&tps65912->irq_lock); + return IRQ_HANDLED; +} /* * This is a threaded IRQ handler so can access I2C/SPI. Since the * IRQ handler explicitly clears the IRQ it handles the IRQ line @@ -158,7 +189,7 @@ int tps65912_irq_init(struct tps65912 *tps65912, int irq, struct tps65912_platform_data *pdata) { int ret, cur_irq; - int flags = IRQF_ONESHOT; + int flags = IRQF_ONESHOT|IRQF_TRIGGER_RISING; u8 reg; if (!irq) { @@ -171,6 +202,8 @@ int tps65912_irq_init(struct tps65912 *tps65912, int irq, return 0; } + tps65912->powerkey_state = PWRKEY_RELEASE; + /* Clear unattended interrupts */ tps65912->read(tps65912, TPS65912_INT_STS, 1, ®); tps65912->write(tps65912, TPS65912_INT_STS, 1, ®); @@ -207,10 +240,22 @@ int tps65912_irq_init(struct tps65912 *tps65912, int irq, #endif } + ret = request_threaded_irq(tps65912->irq_base + TPS65912_IRQ_GPIO3_R, + NULL, powerkey_handler, flags, "tps65912_key_rel", tps65912); + + if (ret != 0) + dev_err(tps65912->dev, + "Failed to request sub-IRQ for power key rel: %d\n", ret); + + ret = request_threaded_irq(tps65912->irq_base + TPS65912_IRQ_GPIO3_F, + NULL, powerkey_handler, flags, "tps65912_key_press", tps65912); + + if (ret != 0) + dev_err(tps65912->dev, + "Failed to request sub-IRQ for power key press: %d\n", ret); + ret = request_threaded_irq(irq, NULL, tps65912_irq, flags, "tps65912", tps65912); - - irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); if (ret != 0) dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret); diff --git a/drivers/mfd/tps65912-key.c b/drivers/mfd/tps65912-key.c new file mode 100644 index 00000000000..5b0b0ebb2c4 --- /dev/null +++ b/drivers/mfd/tps65912-key.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2013 Motorola, Inc. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/gpio.h> +#include <linux/mfd/tps65912.h> + +struct tps65912_key_data { + struct input_dev *input_dev; + struct tps65912 *tps65912; +}; + +static int __init tps65912_key_probe(struct platform_device *pdev) +{ + int err; + struct tps65912_key_data *key; + + pr_info("tps65912_key_probe begin\n"); + + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "no platform_data\n"); + return -EINVAL; + } + + key = kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) + return -ENOMEM; + + key->tps65912 = pdev->dev.platform_data; + + key->input_dev = input_allocate_device(); + if (key->input_dev == NULL) { + dev_err(&pdev->dev, "can't allocate input device\n"); + err = -ENOMEM; + goto err0; + } + + set_bit(EV_KEY, key->input_dev->evbit); + set_bit(KEY_MEDIA, key->input_dev->keybit); + set_bit(KEY_END, key->input_dev->keybit); + set_bit(KEY_POWER_DOUBLE, key->input_dev->keybit); + + key->input_dev->name = "tps65912-key"; + + err = input_register_device(key->input_dev); + if (err < 0) { + dev_err(&pdev->dev, "could not register input device.\n"); + goto err1; + } + + platform_set_drvdata(pdev, key); + tps65912_set_keydata(key->tps65912, key); + + dev_info(&pdev->dev, "tps65912 key device probed\n"); + + return 0; + +err1: + input_free_device(key->input_dev); +err0: + kfree(key); + return err; +} + +static int __exit tps65912_key_remove(struct platform_device *pdev) +{ + struct tps65912_key_data *key = platform_get_drvdata(pdev); + + input_unregister_device(key->input_dev); + input_free_device(key->input_dev); + kfree(key); + + return 0; +} + +void tps65912_broadcast_key_event(struct tps65912 *tps65912, + unsigned int code, int value) +{ + struct tps65912_key_data *key = tps65912_get_keydata(tps65912); + + if (key && key->input_dev) { + input_report_key(key->input_dev, code, value); + /*sync with input subsystem to solve the key cached problem*/ + input_sync(key->input_dev); + } +} +EXPORT_SYMBOL(tps65912_broadcast_key_event); + +static struct platform_driver tps65912_key_driver = { + .probe = tps65912_key_probe, + .remove = __exit_p(tps65912_key_remove), + .driver = { + .name = "tps65912_key", + .owner = THIS_MODULE, + }, +}; + +static int __init tps65912_key_init(void) +{ + return platform_driver_register(&tps65912_key_driver); +} +module_init(tps65912_key_init); + +static void __exit tps65912_key_exit(void) +{ + platform_driver_unregister(&tps65912_key_driver); +} +module_exit(tps65912_key_exit); + +MODULE_ALIAS("platform:tps65912_key"); +MODULE_DESCRIPTION("TPS65912 KEY driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/tps65912.h b/include/linux/mfd/tps65912.h index 16375fe5132..7b38617e041 100644 --- a/include/linux/mfd/tps65912.h +++ b/include/linux/mfd/tps65912.h @@ -317,6 +317,8 @@ struct tps65912 { int irq_num; u32 irq_mask; void *debugfs_data; + void *keydata; + u32 powerkey_state; }; struct tps65912_platform_data { @@ -324,6 +326,16 @@ struct tps65912_platform_data { int irq_base; }; +static inline void tps65912_set_keydata(struct tps65912 *tps65912, void *data) +{ + tps65912->keydata = data; +} + +static inline void *tps65912_get_keydata(struct tps65912 *tps65912) +{ + return tps65912->keydata; +} + unsigned int tps_chip(void); int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask); @@ -338,5 +350,7 @@ int tps65912_irq_exit(struct tps65912 *tps65912); int tps65912_debugfs_create(struct tps65912 *tps65912); void tps65912_debugfs_remove(struct tps65912 *tps65912); void tps65912_dump_registers(struct tps65912 *tps65912); +void tps65912_broadcast_key_event(struct tps65912 *tps65912, + unsigned int code, int value); #endif /* __LINUX_MFD_TPS65912_H */ |