/*
 * Registers keypad device with input subsystem
 * and configures TWL4030 keypad registers
 */
static int __devinit twl4030_kp_probe(struct platform_device *pdev)
{
	u8 reg;
	int i;
	int ret = 0;
	struct twl4030_keypad *kp;
	struct twl4030_keypad_data *pdata = pdev->dev.platform_data;

	if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap
			|| pdata->rows > 8 || pdata->cols > 8) {
		dev_err(&pdev->dev, "Invalid platform_data\n");
		return -EINVAL;
	}

	kp = kzalloc(sizeof(*kp), GFP_KERNEL);
	if (!kp)
		return -ENOMEM;

	platform_set_drvdata(pdev, kp);

	/* Get the debug Device */
	kp->dbg_dev = &pdev->dev;

	kp->input = input_allocate_device();
	if (!kp->input) {
		kfree(kp);
		return -ENOMEM;
	}

	kp->keymap = pdata->keymap;
	kp->keymapsize = pdata->keymapsize;
	kp->n_rows = pdata->rows;
	kp->n_cols = pdata->cols;
	kp->irq = platform_get_irq(pdev, 0);

	/* setup input device */
	__set_bit(EV_KEY, kp->input->evbit);

	/* Enable auto repeat feature of Linux input subsystem */
	if (pdata->rep)
		__set_bit(EV_REP, kp->input->evbit);

	for (i = 0; i < kp->keymapsize; i++)
		__set_bit(kp->keymap[i] & KEYNUM_MASK,
				kp->input->keybit);

	kp->input->name		= "TWL4030 Keypad";
	kp->input->phys		= "twl4030_keypad/input0";
	kp->input->dev.parent	= &pdev->dev;

	kp->input->id.bustype	= BUS_HOST;
	kp->input->id.vendor	= 0x0001;
	kp->input->id.product	= 0x0001;
	kp->input->id.version	= 0x0003;

	kp->input->keycode	= kp->keymap;
	kp->input->keycodesize	= sizeof(unsigned int);
	kp->input->keycodemax	= kp->keymapsize;

	ret = input_register_device(kp->input);
	if (ret < 0) {
		dev_err(kp->dbg_dev,
			"Unable to register twl4030 keypad device\n");
		goto err2;
	}

	/* Enable controller, with hardware decoding but not autorepeat */
	reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
		| KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON;
	ret = twl4030_kpwrite_u8(kp, reg, KEYP_CTRL);
	if (ret < 0)
		goto err3;

	/* NOTE:  we could use sih_setup() here to package keypad
	 * event sources as four different IRQs ... but we don't.
	 */

	/* Enable TO rising and KP rising and falling edge detection */
	reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
	ret = twl4030_kpwrite_u8(kp, reg, KEYP_EDR);
	if (ret < 0)
		goto err3;

	/* Set PTV prescaler Field */
	reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
	ret = twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV);
	if (ret < 0)
		goto err3;

	/* Set key debounce time to 20 ms */
	i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
	ret = twl4030_kpwrite_u8(kp, i, KEYP_DEB);
	if (ret < 0)
		goto err3;

	/* Set timeout period to 100 ms */
	i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
	ret = twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L);
	if (ret < 0)
		goto err3;
	ret = twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H);
	if (ret < 0)
		goto err3;

	/* Enable Clear-on-Read; disable remembering events that fire
	 * after the IRQ but before our handler acks (reads) them,
	 */
	reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK;
	ret = twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL);
	if (ret < 0)
		goto err3;

	/* initialize key state; irqs update it from here on */
	ret = twl4030_read_kp_matrix_state(kp, kp->kp_state);
	if (ret < 0)
		goto err3;

	/*
	 * This ISR will always execute in kernel thread context because of
	 * the need to access the TWL4030 over the I2C bus.
	 *
	 * NOTE:  we assume this host is wired to TWL4040 INT1, not INT2 ...
	 */
	ret = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp);
	if (ret < 0) {
		dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
			kp->irq);
		goto err3;
	} else {
		/* Enable KP and TO interrupts now. */
		reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
		ret = twl4030_kpwrite_u8(kp, reg, KEYP_IMR1);
		if (ret < 0)
			goto err5;
	}

	return ret;
err5:
	/* mask all events - we don't care about the result */
	(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
	free_irq(kp->irq, NULL);
err3:
	input_unregister_device(kp->input);
	kp->input = NULL;
err2:
	input_free_device(kp->input);
	kfree(kp);
	return -ENODEV;
}
Beispiel #2
0
/*
 * Registers keypad device with input sub system
 * and configures TWL4030 keypad registers
 */
static int __init omap_kp_probe(struct platform_device *pdev)
{
	u8 reg;
	int i;
	int ret = 0;
	struct omap_keypad *kp;
	struct twl4030_keypad_data *pdata = pdev->dev.platform_data;

	kp = kzalloc(sizeof(*kp), GFP_KERNEL);
	if (!kp)
		return -ENOMEM;

	if (!pdata->rows || !pdata->cols || !pdata->keymap) {
		dev_err(&pdev->dev, "No rows, cols or keymap from pdata\n");
		kfree(kp);
		return -EINVAL;
	}

	dev_set_drvdata(&pdev->dev, kp);

	/* Get the debug Device */
	kp->dbg_dev = &pdev->dev;

	kp->omap_twl4030kp = input_allocate_device();
	if (!kp->omap_twl4030kp) {
		kfree(kp);
		return -ENOMEM;
	}

	mutex_init(&kp->mutex);

	kp->keymap = pdata->keymap;
	kp->keymapsize = pdata->keymapsize;
	kp->n_rows = pdata->rows;
	kp->n_cols = pdata->cols;
	kp->irq = pdata->irq;

	/* setup input device */
	set_bit(EV_KEY, kp->omap_twl4030kp->evbit);

	/* Enable auto repeat feature of Linux input subsystem */
	if (pdata->rep)
		set_bit(EV_REP, kp->omap_twl4030kp->evbit);

	for (i = 0; i < kp->keymapsize; i++)
		set_bit(kp->keymap[i] & KEYNUM_MASK,
				kp->omap_twl4030kp->keybit);

	kp->omap_twl4030kp->name	= "omap_twl4030keypad";
	kp->omap_twl4030kp->phys	= "omap_twl4030keypad/input0";
	kp->omap_twl4030kp->dev.parent	= &pdev->dev;

	kp->omap_twl4030kp->id.bustype	= BUS_HOST;
	kp->omap_twl4030kp->id.vendor	= 0x0001;
	kp->omap_twl4030kp->id.product	= 0x0001;
	kp->omap_twl4030kp->id.version	= 0x0003;

	kp->omap_twl4030kp->keycode	= kp->keymap;
	kp->omap_twl4030kp->keycodesize	= sizeof(unsigned int);
	kp->omap_twl4030kp->keycodemax	= kp->keymapsize;

	ret = input_register_device(kp->omap_twl4030kp);
	if (ret < 0) {
		dev_err(kp->dbg_dev,
			"Unable to register twl4030 keypad device\n");
		goto err2;
	}

	/* Disable auto-repeat */
	reg = KEYP_CTRL_NOAUTORPT;
	ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, reg, KEYP_CTRL);
	if (ret < 0)
		goto err3;

	/* Enable TO rising and KP rising and falling edge detection */
	reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
	ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, reg, KEYP_EDR);
	if (ret < 0)
		goto err3;

	/* Set PTV prescaler Field */
	reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
	ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, reg, KEYP_LK_PTV);
	if (ret < 0)
		goto err3;

	/* Set key debounce time to 20 ms */
	i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
	ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, i, KEYP_DEB);
	if (ret < 0)
		goto err3;

	/* Set timeout period to 100 ms */
	i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
	ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD,
				 (i & 0xFF), KEYP_TIMEOUT_L);
	if (ret < 0)
		goto err3;

	ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD,
				 (i >> 8), KEYP_TIMEOUT_H);
	if (ret < 0)
		goto err3;

	/* Enable Clear-on-Read */
	reg = KEYP_SIH_CTRL_COR | KEYP_SIH_CTRL_PEND_DIS;
	ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD,
				 reg, KEYP_SIH_CTRL);
	if (ret < 0)
		goto err3;

	/*
	 * This ISR will always execute in kernel thread context because of
	 * the need to access the TWL4030 over the I2C bus.
	 */
	ret = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp);
	if (ret < 0) {
		dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
			kp->irq);
		goto err3;
	} else {
		/* Enable KP and TO interrupts now. */
		reg = ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
		ret = twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD,
					 reg, KEYP_IMR1);
		if (ret < 0)
			goto err5;
	}

	ret = omap_kp_read_kp_matrix_state(kp, kp->kp_state);
	if (ret < 0)
		goto err4;

	return ret;
err5:
	/* mask all events - we don't care about the result */
	(void) twl4030_kpwrite_u8(kp, TWL4030_MODULE_KEYPAD, 0xff, KEYP_IMR1);
err4:
	free_irq(kp->irq, NULL);
err3:
	input_unregister_device(kp->omap_twl4030kp);
err2:
	input_free_device(kp->omap_twl4030kp);

	return -ENODEV;
}