/* * The TWL4030 family chips include a keypad controller that supports * up to an 8x8 switch matrix. The controller can issue system wakeup * events, since it uses only the always-on 32KiHz oscillator, and has * an internal state machine that decodes pressed keys, including * multi-key combinations. * * This driver lets boards define what keycodes they wish to report for * which scancodes, as part of the "struct twl4030_keypad_data" used in * the probe() routine. * * See the TPS65950 documentation; that's the general availability * version of the TWL5030 second generation part.
*/ #define TWL4030_MAX_ROWS 8 /* TWL4030 hard limit */ #define TWL4030_MAX_COLS 8 /* * Note that we add space for an extra column so that we can handle * row lines connected to the gnd (see twl4030_col_xlate()).
*/ #define TWL4030_ROW_SHIFT 4 #define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS << TWL4030_ROW_SHIFT)
staticint twl4030_kpread(struct twl4030_keypad *kp,
u8 *data, u32 reg, u8 num_bytes)
{ int ret = twl_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
if (ret < 0)
dev_warn(kp->dbg_dev, "Couldn't read TWL4030: %X - ret %d[%x]\n",
reg, ret, ret);
return ret;
}
staticint twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
{ int ret = twl_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
if (ret < 0)
dev_warn(kp->dbg_dev, "Could not write TWL4030: %X - ret %d[%x]\n",
reg, ret, ret);
return ret;
}
staticinline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
{ /* * If all bits in a row are active for all columns then * we have that row line connected to gnd. Mark this * key on as if it was on matrix position n_cols (i.e. * one higher than the size of the matrix).
*/ if (col == 0xFF) return 1 << kp->n_cols; else return col & ((1 << kp->n_cols) - 1);
}
staticint twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
{
u8 new_state[TWL4030_MAX_ROWS]; int row; int ret = twl4030_kpread(kp, new_state,
KEYP_FULL_CODE_7_0, kp->n_rows); if (ret >= 0) for (row = 0; row < kp->n_rows; row++)
state[row] = twl4030_col_xlate(kp, new_state[row]);
if (release_all) {
memset(new_state, 0, sizeof(new_state));
} else { /* check for any changes */ int ret = twl4030_read_kp_matrix_state(kp, new_state);
if (ret < 0) /* panic ... */ return;
if (twl4030_is_in_ghost_state(kp, new_state)) return;
}
/* check for changes and print those */ for (row = 0; row < kp->n_rows; row++) { int changed = new_state[row] ^ kp->kp_state[row];
if (!changed) continue;
/* Extra column handles "all gnd" rows */ for (col = 0; col < kp->n_cols + 1; col++) { int code;
/* * Release all keys if I2C has gone bad or * the KEYP has gone to idle state.
*/ if (ret >= 0 && (reg & KEYP_IMR1_KP))
twl4030_kp_scan(kp, false); else
twl4030_kp_scan(kp, true);
return IRQ_HANDLED;
}
staticint twl4030_kp_program(struct twl4030_keypad *kp)
{
u8 reg; int i;
/* Enable controller, with hardware decoding but not autorepeat */
reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
| KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON; if (twl4030_kpwrite_u8(kp, reg, KEYP_CTRL) < 0) return -EIO;
/* * 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; if (twl4030_kpwrite_u8(kp, reg, KEYP_EDR) < 0) return -EIO;
/* Set PTV prescaler Field */
reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT); if (twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV) < 0) return -EIO;
/* Set key debounce time to 20 ms */
i = KEYP_PERIOD_US(20000, PTV_PRESCALER); if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0) return -EIO;
/* Set timeout period to 200 ms */
i = KEYP_PERIOD_US(200000, PTV_PRESCALER); if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0) return -EIO;
if (twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H) < 0) return -EIO;
/* * 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; if (twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL) < 0) return -EIO;
/* initialize key state; irqs update it from here on */ if (twl4030_read_kp_matrix_state(kp, kp->kp_state) < 0) return -EIO;
input_set_capability(input, EV_MSC, MSC_SCAN); /* Enable auto repeat feature of Linux input subsystem */ if (kp->autorepeat)
__set_bit(EV_REP, input->evbit);
error = input_register_device(input); if (error) {
dev_err(kp->dbg_dev, "Unable to register twl4030 keypad device\n"); return error;
}
error = twl4030_kp_program(kp); if (error) return error;
/* * 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 ...
*/
error = devm_request_threaded_irq(&pdev->dev, kp->irq, NULL, do_kp_irq,
0, pdev->name, kp); if (error) {
dev_info(kp->dbg_dev, "request_irq failed for irq no=%d: %d\n",
kp->irq, error); return error;
}
/* Enable KP and TO interrupts now. */
reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO); if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) { /* mask all events - we don't care about the result */
(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); return -EIO;
}
/* * NOTE: twl4030 are multi-function devices connected via I2C. * So this device is a child of an I2C parent, thus it needs to * support unplug/replug (which most platform devices don't).
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.