/* * 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; }
/* * 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; }