Path: blob/master/drivers/input/keyboard/imx_keypad.c
15109 views
/*1* Driver for the IMX keypad port.2* Copyright (C) 2009 Alberto Panizzo <[email protected]>3*4* This program is free software; you can redistribute it and/or modify5* it under the terms of the GNU General Public License version 2 as6* published by the Free Software Foundation.7*8* <<Power management needs to be implemented>>.9*/1011#include <linux/clk.h>12#include <linux/delay.h>13#include <linux/device.h>14#include <linux/err.h>15#include <linux/init.h>16#include <linux/input/matrix_keypad.h>17#include <linux/interrupt.h>18#include <linux/io.h>19#include <linux/jiffies.h>20#include <linux/kernel.h>21#include <linux/module.h>22#include <linux/platform_device.h>23#include <linux/slab.h>24#include <linux/timer.h>2526/*27* Keypad Controller registers (halfword)28*/29#define KPCR 0x00 /* Keypad Control Register */3031#define KPSR 0x02 /* Keypad Status Register */32#define KBD_STAT_KPKD (0x1 << 0) /* Key Press Interrupt Status bit (w1c) */33#define KBD_STAT_KPKR (0x1 << 1) /* Key Release Interrupt Status bit (w1c) */34#define KBD_STAT_KDSC (0x1 << 2) /* Key Depress Synch Chain Status bit (w1c)*/35#define KBD_STAT_KRSS (0x1 << 3) /* Key Release Synch Status bit (w1c)*/36#define KBD_STAT_KDIE (0x1 << 8) /* Key Depress Interrupt Enable Status bit */37#define KBD_STAT_KRIE (0x1 << 9) /* Key Release Interrupt Enable */38#define KBD_STAT_KPPEN (0x1 << 10) /* Keypad Clock Enable */3940#define KDDR 0x04 /* Keypad Data Direction Register */41#define KPDR 0x06 /* Keypad Data Register */4243#define MAX_MATRIX_KEY_ROWS 844#define MAX_MATRIX_KEY_COLS 845#define MATRIX_ROW_SHIFT 34647#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)4849struct imx_keypad {5051struct clk *clk;52struct input_dev *input_dev;53void __iomem *mmio_base;5455int irq;56struct timer_list check_matrix_timer;5758/*59* The matrix is stable only if no changes are detected after60* IMX_KEYPAD_SCANS_FOR_STABILITY scans61*/62#define IMX_KEYPAD_SCANS_FOR_STABILITY 363int stable_count;6465bool enabled;6667/* Masks for enabled rows/cols */68unsigned short rows_en_mask;69unsigned short cols_en_mask;7071unsigned short keycodes[MAX_MATRIX_KEY_NUM];7273/*74* Matrix states:75* -stable: achieved after a complete debounce process.76* -unstable: used in the debouncing process.77*/78unsigned short matrix_stable_state[MAX_MATRIX_KEY_COLS];79unsigned short matrix_unstable_state[MAX_MATRIX_KEY_COLS];80};8182/* Scan the matrix and return the new state in *matrix_volatile_state. */83static void imx_keypad_scan_matrix(struct imx_keypad *keypad,84unsigned short *matrix_volatile_state)85{86int col;87unsigned short reg_val;8889for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {90if ((keypad->cols_en_mask & (1 << col)) == 0)91continue;92/*93* Discharge keypad capacitance:94* 2. write 1s on column data.95* 3. configure columns as totem-pole to discharge capacitance.96* 4. configure columns as open-drain.97*/98reg_val = readw(keypad->mmio_base + KPDR);99reg_val |= 0xff00;100writew(reg_val, keypad->mmio_base + KPDR);101102reg_val = readw(keypad->mmio_base + KPCR);103reg_val &= ~((keypad->cols_en_mask & 0xff) << 8);104writew(reg_val, keypad->mmio_base + KPCR);105106udelay(2);107108reg_val = readw(keypad->mmio_base + KPCR);109reg_val |= (keypad->cols_en_mask & 0xff) << 8;110writew(reg_val, keypad->mmio_base + KPCR);111112/*113* 5. Write a single column to 0, others to 1.114* 6. Sample row inputs and save data.115* 7. Repeat steps 2 - 6 for remaining columns.116*/117reg_val = readw(keypad->mmio_base + KPDR);118reg_val &= ~(1 << (8 + col));119writew(reg_val, keypad->mmio_base + KPDR);120121/*122* Delay added to avoid propagating the 0 from column to row123* when scanning.124*/125udelay(5);126127/*128* 1s in matrix_volatile_state[col] means key pressures129* throw data from non enabled rows.130*/131reg_val = readw(keypad->mmio_base + KPDR);132matrix_volatile_state[col] = (~reg_val) & keypad->rows_en_mask;133}134135/*136* Return in standby mode:137* 9. write 0s to columns138*/139reg_val = readw(keypad->mmio_base + KPDR);140reg_val &= 0x00ff;141writew(reg_val, keypad->mmio_base + KPDR);142}143144/*145* Compare the new matrix state (volatile) with the stable one stored in146* keypad->matrix_stable_state and fire events if changes are detected.147*/148static void imx_keypad_fire_events(struct imx_keypad *keypad,149unsigned short *matrix_volatile_state)150{151struct input_dev *input_dev = keypad->input_dev;152int row, col;153154for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {155unsigned short bits_changed;156int code;157158if ((keypad->cols_en_mask & (1 << col)) == 0)159continue; /* Column is not enabled */160161bits_changed = keypad->matrix_stable_state[col] ^162matrix_volatile_state[col];163164if (bits_changed == 0)165continue; /* Column does not contain changes */166167for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {168if ((keypad->rows_en_mask & (1 << row)) == 0)169continue; /* Row is not enabled */170if ((bits_changed & (1 << row)) == 0)171continue; /* Row does not contain changes */172173code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);174input_event(input_dev, EV_MSC, MSC_SCAN, code);175input_report_key(input_dev, keypad->keycodes[code],176matrix_volatile_state[col] & (1 << row));177dev_dbg(&input_dev->dev, "Event code: %d, val: %d",178keypad->keycodes[code],179matrix_volatile_state[col] & (1 << row));180}181}182input_sync(input_dev);183}184185/*186* imx_keypad_check_for_events is the timer handler.187*/188static void imx_keypad_check_for_events(unsigned long data)189{190struct imx_keypad *keypad = (struct imx_keypad *) data;191unsigned short matrix_volatile_state[MAX_MATRIX_KEY_COLS];192unsigned short reg_val;193bool state_changed, is_zero_matrix;194int i;195196memset(matrix_volatile_state, 0, sizeof(matrix_volatile_state));197198imx_keypad_scan_matrix(keypad, matrix_volatile_state);199200state_changed = false;201for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) {202if ((keypad->cols_en_mask & (1 << i)) == 0)203continue;204205if (keypad->matrix_unstable_state[i] ^ matrix_volatile_state[i]) {206state_changed = true;207break;208}209}210211/*212* If the matrix state is changed from the previous scan213* (Re)Begin the debouncing process, saving the new state in214* keypad->matrix_unstable_state.215* else216* Increase the count of number of scans with a stable state.217*/218if (state_changed) {219memcpy(keypad->matrix_unstable_state, matrix_volatile_state,220sizeof(matrix_volatile_state));221keypad->stable_count = 0;222} else223keypad->stable_count++;224225/*226* If the matrix is not as stable as we want reschedule scan227* in the near future.228*/229if (keypad->stable_count < IMX_KEYPAD_SCANS_FOR_STABILITY) {230mod_timer(&keypad->check_matrix_timer,231jiffies + msecs_to_jiffies(10));232return;233}234235/*236* If the matrix state is stable, fire the events and save the new237* stable state. Note, if the matrix is kept stable for longer238* (keypad->stable_count > IMX_KEYPAD_SCANS_FOR_STABILITY) all239* events have already been generated.240*/241if (keypad->stable_count == IMX_KEYPAD_SCANS_FOR_STABILITY) {242imx_keypad_fire_events(keypad, matrix_volatile_state);243244memcpy(keypad->matrix_stable_state, matrix_volatile_state,245sizeof(matrix_volatile_state));246}247248is_zero_matrix = true;249for (i = 0; i < MAX_MATRIX_KEY_COLS; i++) {250if (matrix_volatile_state[i] != 0) {251is_zero_matrix = false;252break;253}254}255256257if (is_zero_matrix) {258/*259* All keys have been released. Enable only the KDI260* interrupt for future key presses (clear the KDI261* status bit and its sync chain before that).262*/263reg_val = readw(keypad->mmio_base + KPSR);264reg_val |= KBD_STAT_KPKD | KBD_STAT_KDSC;265writew(reg_val, keypad->mmio_base + KPSR);266267reg_val = readw(keypad->mmio_base + KPSR);268reg_val |= KBD_STAT_KDIE;269reg_val &= ~KBD_STAT_KRIE;270writew(reg_val, keypad->mmio_base + KPSR);271} else {272/*273* Some keys are still pressed. Schedule a rescan in274* attempt to detect multiple key presses and enable275* the KRI interrupt to react quickly to key release276* event.277*/278mod_timer(&keypad->check_matrix_timer,279jiffies + msecs_to_jiffies(60));280281reg_val = readw(keypad->mmio_base + KPSR);282reg_val |= KBD_STAT_KPKR | KBD_STAT_KRSS;283writew(reg_val, keypad->mmio_base + KPSR);284285reg_val = readw(keypad->mmio_base + KPSR);286reg_val |= KBD_STAT_KRIE;287reg_val &= ~KBD_STAT_KDIE;288writew(reg_val, keypad->mmio_base + KPSR);289}290}291292static irqreturn_t imx_keypad_irq_handler(int irq, void *dev_id)293{294struct imx_keypad *keypad = dev_id;295unsigned short reg_val;296297reg_val = readw(keypad->mmio_base + KPSR);298299/* Disable both interrupt types */300reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE);301/* Clear interrupts status bits */302reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;303writew(reg_val, keypad->mmio_base + KPSR);304305if (keypad->enabled) {306/* The matrix is supposed to be changed */307keypad->stable_count = 0;308309/* Schedule the scanning procedure near in the future */310mod_timer(&keypad->check_matrix_timer,311jiffies + msecs_to_jiffies(2));312}313314return IRQ_HANDLED;315}316317static void imx_keypad_config(struct imx_keypad *keypad)318{319unsigned short reg_val;320321/*322* Include enabled rows in interrupt generation (KPCR[7:0])323* Configure keypad columns as open-drain (KPCR[15:8])324*/325reg_val = readw(keypad->mmio_base + KPCR);326reg_val |= keypad->rows_en_mask & 0xff; /* rows */327reg_val |= (keypad->cols_en_mask & 0xff) << 8; /* cols */328writew(reg_val, keypad->mmio_base + KPCR);329330/* Write 0's to KPDR[15:8] (Colums) */331reg_val = readw(keypad->mmio_base + KPDR);332reg_val &= 0x00ff;333writew(reg_val, keypad->mmio_base + KPDR);334335/* Configure columns as output, rows as input (KDDR[15:0]) */336writew(0xff00, keypad->mmio_base + KDDR);337338/*339* Clear Key Depress and Key Release status bit.340* Clear both synchronizer chain.341*/342reg_val = readw(keypad->mmio_base + KPSR);343reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD |344KBD_STAT_KDSC | KBD_STAT_KRSS;345writew(reg_val, keypad->mmio_base + KPSR);346347/* Enable KDI and disable KRI (avoid false release events). */348reg_val |= KBD_STAT_KDIE;349reg_val &= ~KBD_STAT_KRIE;350writew(reg_val, keypad->mmio_base + KPSR);351}352353static void imx_keypad_inhibit(struct imx_keypad *keypad)354{355unsigned short reg_val;356357/* Inhibit KDI and KRI interrupts. */358reg_val = readw(keypad->mmio_base + KPSR);359reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE);360writew(reg_val, keypad->mmio_base + KPSR);361362/* Colums as open drain and disable all rows */363writew(0xff00, keypad->mmio_base + KPCR);364}365366static void imx_keypad_close(struct input_dev *dev)367{368struct imx_keypad *keypad = input_get_drvdata(dev);369370dev_dbg(&dev->dev, ">%s\n", __func__);371372/* Mark keypad as being inactive */373keypad->enabled = false;374synchronize_irq(keypad->irq);375del_timer_sync(&keypad->check_matrix_timer);376377imx_keypad_inhibit(keypad);378379/* Disable clock unit */380clk_disable(keypad->clk);381}382383static int imx_keypad_open(struct input_dev *dev)384{385struct imx_keypad *keypad = input_get_drvdata(dev);386387dev_dbg(&dev->dev, ">%s\n", __func__);388389/* We became active from now */390keypad->enabled = true;391392/* Enable the kpp clock */393clk_enable(keypad->clk);394imx_keypad_config(keypad);395396/* Sanity control, not all the rows must be actived now. */397if ((readw(keypad->mmio_base + KPDR) & keypad->rows_en_mask) == 0) {398dev_err(&dev->dev,399"too many keys pressed, control pins initialisation\n");400goto open_err;401}402403return 0;404405open_err:406imx_keypad_close(dev);407return -EIO;408}409410static int __devinit imx_keypad_probe(struct platform_device *pdev)411{412const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data;413struct imx_keypad *keypad;414struct input_dev *input_dev;415struct resource *res;416int irq, error, i;417418if (keymap_data == NULL) {419dev_err(&pdev->dev, "no keymap defined\n");420return -EINVAL;421}422423irq = platform_get_irq(pdev, 0);424if (irq < 0) {425dev_err(&pdev->dev, "no irq defined in platform data\n");426return -EINVAL;427}428429res = platform_get_resource(pdev, IORESOURCE_MEM, 0);430if (res == NULL) {431dev_err(&pdev->dev, "no I/O memory defined in platform data\n");432return -EINVAL;433}434435res = request_mem_region(res->start, resource_size(res), pdev->name);436if (res == NULL) {437dev_err(&pdev->dev, "failed to request I/O memory\n");438return -EBUSY;439}440441input_dev = input_allocate_device();442if (!input_dev) {443dev_err(&pdev->dev, "failed to allocate the input device\n");444error = -ENOMEM;445goto failed_rel_mem;446}447448keypad = kzalloc(sizeof(struct imx_keypad), GFP_KERNEL);449if (!keypad) {450dev_err(&pdev->dev, "not enough memory for driver data\n");451error = -ENOMEM;452goto failed_free_input;453}454455keypad->input_dev = input_dev;456keypad->irq = irq;457keypad->stable_count = 0;458459setup_timer(&keypad->check_matrix_timer,460imx_keypad_check_for_events, (unsigned long) keypad);461462keypad->mmio_base = ioremap(res->start, resource_size(res));463if (keypad->mmio_base == NULL) {464dev_err(&pdev->dev, "failed to remap I/O memory\n");465error = -ENOMEM;466goto failed_free_priv;467}468469keypad->clk = clk_get(&pdev->dev, "kpp");470if (IS_ERR(keypad->clk)) {471dev_err(&pdev->dev, "failed to get keypad clock\n");472error = PTR_ERR(keypad->clk);473goto failed_unmap;474}475476/* Search for rows and cols enabled */477for (i = 0; i < keymap_data->keymap_size; i++) {478keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]);479keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]);480}481482if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) ||483keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) {484dev_err(&pdev->dev,485"invalid key data (too many rows or colums)\n");486error = -EINVAL;487goto failed_clock_put;488}489dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);490dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);491492/* Init the Input device */493input_dev->name = pdev->name;494input_dev->id.bustype = BUS_HOST;495input_dev->dev.parent = &pdev->dev;496input_dev->open = imx_keypad_open;497input_dev->close = imx_keypad_close;498input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);499input_dev->keycode = keypad->keycodes;500input_dev->keycodesize = sizeof(keypad->keycodes[0]);501input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);502503matrix_keypad_build_keymap(keymap_data, MATRIX_ROW_SHIFT,504keypad->keycodes, input_dev->keybit);505506input_set_capability(input_dev, EV_MSC, MSC_SCAN);507input_set_drvdata(input_dev, keypad);508509/* Ensure that the keypad will stay dormant until opened */510imx_keypad_inhibit(keypad);511512error = request_irq(irq, imx_keypad_irq_handler, IRQF_DISABLED,513pdev->name, keypad);514if (error) {515dev_err(&pdev->dev, "failed to request IRQ\n");516goto failed_clock_put;517}518519/* Register the input device */520error = input_register_device(input_dev);521if (error) {522dev_err(&pdev->dev, "failed to register input device\n");523goto failed_free_irq;524}525526platform_set_drvdata(pdev, keypad);527device_init_wakeup(&pdev->dev, 1);528529return 0;530531failed_free_irq:532free_irq(irq, pdev);533failed_clock_put:534clk_put(keypad->clk);535failed_unmap:536iounmap(keypad->mmio_base);537failed_free_priv:538kfree(keypad);539failed_free_input:540input_free_device(input_dev);541failed_rel_mem:542release_mem_region(res->start, resource_size(res));543return error;544}545546static int __devexit imx_keypad_remove(struct platform_device *pdev)547{548struct imx_keypad *keypad = platform_get_drvdata(pdev);549struct resource *res;550551dev_dbg(&pdev->dev, ">%s\n", __func__);552553platform_set_drvdata(pdev, NULL);554555input_unregister_device(keypad->input_dev);556557free_irq(keypad->irq, keypad);558clk_put(keypad->clk);559560iounmap(keypad->mmio_base);561res = platform_get_resource(pdev, IORESOURCE_MEM, 0);562release_mem_region(res->start, resource_size(res));563564kfree(keypad);565566return 0;567}568569static struct platform_driver imx_keypad_driver = {570.driver = {571.name = "imx-keypad",572.owner = THIS_MODULE,573},574.probe = imx_keypad_probe,575.remove = __devexit_p(imx_keypad_remove),576};577578static int __init imx_keypad_init(void)579{580return platform_driver_register(&imx_keypad_driver);581}582583static void __exit imx_keypad_exit(void)584{585platform_driver_unregister(&imx_keypad_driver);586}587588module_init(imx_keypad_init);589module_exit(imx_keypad_exit);590591MODULE_AUTHOR("Alberto Panizzo <[email protected]>");592MODULE_DESCRIPTION("IMX Keypad Port Driver");593MODULE_LICENSE("GPL v2");594MODULE_ALIAS("platform:imx-keypad");595596597