diff options
| -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 */  |