diff options
Diffstat (limited to 'drivers/input/keyboard/imx_keypad.c')
| -rw-r--r-- | drivers/input/keyboard/imx_keypad.c | 207 | 
1 files changed, 104 insertions, 103 deletions
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index d92c15c39e6..8280cb16260 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -13,13 +13,13 @@  #include <linux/delay.h>  #include <linux/device.h>  #include <linux/err.h> -#include <linux/init.h>  #include <linux/input/matrix_keypad.h>  #include <linux/interrupt.h>  #include <linux/io.h>  #include <linux/jiffies.h>  #include <linux/kernel.h>  #include <linux/module.h> +#include <linux/of.h>  #include <linux/platform_device.h>  #include <linux/slab.h>  #include <linux/timer.h> @@ -358,10 +358,12 @@ static void imx_keypad_inhibit(struct imx_keypad *keypad)  	/* Inhibit KDI and KRI interrupts. */  	reg_val = readw(keypad->mmio_base + KPSR);  	reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE); +	reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;  	writew(reg_val, keypad->mmio_base + KPSR);  	/* Colums as open drain and disable all rows */ -	writew(0xff00, keypad->mmio_base + KPCR); +	reg_val = (keypad->cols_en_mask & 0xff) << 8; +	writew(reg_val, keypad->mmio_base + KPCR);  }  static void imx_keypad_close(struct input_dev *dev) @@ -378,20 +380,24 @@ static void imx_keypad_close(struct input_dev *dev)  	imx_keypad_inhibit(keypad);  	/* Disable clock unit */ -	clk_disable(keypad->clk); +	clk_disable_unprepare(keypad->clk);  }  static int imx_keypad_open(struct input_dev *dev)  {  	struct imx_keypad *keypad = input_get_drvdata(dev); +	int error;  	dev_dbg(&dev->dev, ">%s\n", __func__); +	/* Enable the kpp clock */ +	error = clk_prepare_enable(keypad->clk); +	if (error) +		return error; +  	/* We became active from now */  	keypad->enabled = true; -	/* Enable the kpp clock */ -	clk_enable(keypad->clk);  	imx_keypad_config(keypad);  	/* Sanity control, not all the rows must be actived now. */ @@ -408,15 +414,24 @@ open_err:  	return -EIO;  } -static int __devinit imx_keypad_probe(struct platform_device *pdev) +#ifdef CONFIG_OF +static const struct of_device_id imx_keypad_of_match[] = { +	{ .compatible = "fsl,imx21-kpp", }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_keypad_of_match); +#endif + +static int imx_keypad_probe(struct platform_device *pdev)  { -	const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data; +	const struct matrix_keymap_data *keymap_data = +			dev_get_platdata(&pdev->dev);  	struct imx_keypad *keypad;  	struct input_dev *input_dev;  	struct resource *res; -	int irq, error, i; +	int irq, error, i, row, col; -	if (keymap_data == NULL) { +	if (!keymap_data && !pdev->dev.of_node) {  		dev_err(&pdev->dev, "no keymap defined\n");  		return -EINVAL;  	} @@ -424,33 +439,20 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) {  		dev_err(&pdev->dev, "no irq defined in platform data\n"); -		return -EINVAL; -	} - -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (res == NULL) { -		dev_err(&pdev->dev, "no I/O memory defined in platform data\n"); -		return -EINVAL; -	} - -	res = request_mem_region(res->start, resource_size(res), pdev->name); -	if (res == NULL) { -		dev_err(&pdev->dev, "failed to request I/O memory\n"); -		return -EBUSY; +		return irq;  	} -	input_dev = input_allocate_device(); +	input_dev = devm_input_allocate_device(&pdev->dev);  	if (!input_dev) {  		dev_err(&pdev->dev, "failed to allocate the input device\n"); -		error = -ENOMEM; -		goto failed_rel_mem; +		return -ENOMEM;  	} -	keypad = kzalloc(sizeof(struct imx_keypad), GFP_KERNEL); +	keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad), +			      GFP_KERNEL);  	if (!keypad) {  		dev_err(&pdev->dev, "not enough memory for driver data\n"); -		error = -ENOMEM; -		goto failed_free_input; +		return -ENOMEM;  	}  	keypad->input_dev = input_dev; @@ -460,134 +462,133 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)  	setup_timer(&keypad->check_matrix_timer,  		    imx_keypad_check_for_events, (unsigned long) keypad); -	keypad->mmio_base = ioremap(res->start, resource_size(res)); -	if (keypad->mmio_base == NULL) { -		dev_err(&pdev->dev, "failed to remap I/O memory\n"); -		error = -ENOMEM; -		goto failed_free_priv; -	} +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(keypad->mmio_base)) +		return PTR_ERR(keypad->mmio_base); -	keypad->clk = clk_get(&pdev->dev, "kpp"); +	keypad->clk = devm_clk_get(&pdev->dev, NULL);  	if (IS_ERR(keypad->clk)) {  		dev_err(&pdev->dev, "failed to get keypad clock\n"); -		error = PTR_ERR(keypad->clk); -		goto failed_unmap; +		return PTR_ERR(keypad->clk);  	} -	/* Search for rows and cols enabled */ -	for (i = 0; i < keymap_data->keymap_size; i++) { -		keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]); -		keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]); -	} - -	if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) || -	   keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) { -		dev_err(&pdev->dev, -			"invalid key data (too many rows or colums)\n"); -		error = -EINVAL; -		goto failed_clock_put; -	} -	dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask); -	dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask); -  	/* Init the Input device */  	input_dev->name = pdev->name;  	input_dev->id.bustype = BUS_HOST;  	input_dev->dev.parent = &pdev->dev;  	input_dev->open = imx_keypad_open;  	input_dev->close = imx_keypad_close; -	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); -	input_dev->keycode = keypad->keycodes; -	input_dev->keycodesize = sizeof(keypad->keycodes[0]); -	input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); -	matrix_keypad_build_keymap(keymap_data, MATRIX_ROW_SHIFT, -				keypad->keycodes, input_dev->keybit); +	error = matrix_keypad_build_keymap(keymap_data, NULL, +					   MAX_MATRIX_KEY_ROWS, +					   MAX_MATRIX_KEY_COLS, +					   keypad->keycodes, input_dev); +	if (error) { +		dev_err(&pdev->dev, "failed to build keymap\n"); +		return error; +	} +	/* Search for rows and cols enabled */ +	for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) { +		for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) { +			i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT); +			if (keypad->keycodes[i] != KEY_RESERVED) { +				keypad->rows_en_mask |= 1 << row; +				keypad->cols_en_mask |= 1 << col; +			} +		} +	} +	dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask); +	dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask); + +	__set_bit(EV_REP, input_dev->evbit);  	input_set_capability(input_dev, EV_MSC, MSC_SCAN);  	input_set_drvdata(input_dev, keypad);  	/* Ensure that the keypad will stay dormant until opened */ +	clk_prepare_enable(keypad->clk);  	imx_keypad_inhibit(keypad); +	clk_disable_unprepare(keypad->clk); -	error = request_irq(irq, imx_keypad_irq_handler, IRQF_DISABLED, +	error = devm_request_irq(&pdev->dev, irq, imx_keypad_irq_handler, 0,  			    pdev->name, keypad);  	if (error) {  		dev_err(&pdev->dev, "failed to request IRQ\n"); -		goto failed_clock_put; +		return error;  	}  	/* Register the input device */  	error = input_register_device(input_dev);  	if (error) {  		dev_err(&pdev->dev, "failed to register input device\n"); -		goto failed_free_irq; +		return error;  	}  	platform_set_drvdata(pdev, keypad);  	device_init_wakeup(&pdev->dev, 1);  	return 0; - -failed_free_irq: -	free_irq(irq, pdev); -failed_clock_put: -	clk_put(keypad->clk); -failed_unmap: -	iounmap(keypad->mmio_base); -failed_free_priv: -	kfree(keypad); -failed_free_input: -	input_free_device(input_dev); -failed_rel_mem: -	release_mem_region(res->start, resource_size(res)); -	return error;  } -static int __devexit imx_keypad_remove(struct platform_device *pdev) +#ifdef CONFIG_PM_SLEEP +static int imx_kbd_suspend(struct device *dev)  { -	struct imx_keypad *keypad = platform_get_drvdata(pdev); -	struct resource *res; +	struct platform_device *pdev = to_platform_device(dev); +	struct imx_keypad *kbd = platform_get_drvdata(pdev); +	struct input_dev *input_dev = kbd->input_dev; -	dev_dbg(&pdev->dev, ">%s\n", __func__); +	/* imx kbd can wake up system even clock is disabled */ +	mutex_lock(&input_dev->mutex); -	platform_set_drvdata(pdev, NULL); +	if (input_dev->users) +		clk_disable_unprepare(kbd->clk); -	input_unregister_device(keypad->input_dev); +	mutex_unlock(&input_dev->mutex); -	free_irq(keypad->irq, keypad); -	clk_put(keypad->clk); +	if (device_may_wakeup(&pdev->dev)) +		enable_irq_wake(kbd->irq); -	iounmap(keypad->mmio_base); -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	release_mem_region(res->start, resource_size(res)); +	return 0; +} -	kfree(keypad); +static int imx_kbd_resume(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct imx_keypad *kbd = platform_get_drvdata(pdev); +	struct input_dev *input_dev = kbd->input_dev; +	int ret = 0; -	return 0; +	if (device_may_wakeup(&pdev->dev)) +		disable_irq_wake(kbd->irq); + +	mutex_lock(&input_dev->mutex); + +	if (input_dev->users) { +		ret = clk_prepare_enable(kbd->clk); +		if (ret) +			goto err_clk; +	} + +err_clk: +	mutex_unlock(&input_dev->mutex); + +	return ret;  } +#endif + +static SIMPLE_DEV_PM_OPS(imx_kbd_pm_ops, imx_kbd_suspend, imx_kbd_resume);  static struct platform_driver imx_keypad_driver = {  	.driver		= {  		.name	= "imx-keypad",  		.owner	= THIS_MODULE, +		.pm	= &imx_kbd_pm_ops, +		.of_match_table = of_match_ptr(imx_keypad_of_match),  	},  	.probe		= imx_keypad_probe, -	.remove		= __devexit_p(imx_keypad_remove),  }; - -static int __init imx_keypad_init(void) -{ -	return platform_driver_register(&imx_keypad_driver); -} - -static void __exit imx_keypad_exit(void) -{ -	platform_driver_unregister(&imx_keypad_driver); -} - -module_init(imx_keypad_init); -module_exit(imx_keypad_exit); +module_platform_driver(imx_keypad_driver);  MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>");  MODULE_DESCRIPTION("IMX Keypad Port Driver");  | 
