Path: blob/master/drivers/input/keyboard/nomadik-ske-keypad.c
15111 views
/*1* Copyright (C) ST-Ericsson SA 20102*3* Author: Naveen Kumar G <[email protected]> for ST-Ericsson4* Author: Sundar Iyer <[email protected]> for ST-Ericsson5*6* License terms:GNU General Public License (GPL) version 27*8* Keypad controller driver for the SKE (Scroll Key Encoder) module used in9* the Nomadik 8815 and Ux500 platforms.10*/1112#include <linux/platform_device.h>13#include <linux/interrupt.h>14#include <linux/spinlock.h>15#include <linux/io.h>16#include <linux/delay.h>17#include <linux/input.h>18#include <linux/slab.h>19#include <linux/clk.h>2021#include <plat/ske.h>2223/* SKE_CR bits */24#define SKE_KPMLT (0x1 << 6)25#define SKE_KPCN (0x7 << 3)26#define SKE_KPASEN (0x1 << 2)27#define SKE_KPASON (0x1 << 7)2829/* SKE_IMSC bits */30#define SKE_KPIMA (0x1 << 2)3132/* SKE_ICR bits */33#define SKE_KPICS (0x1 << 3)34#define SKE_KPICA (0x1 << 2)3536/* SKE_RIS bits */37#define SKE_KPRISA (0x1 << 2)3839#define SKE_KEYPAD_ROW_SHIFT 340#define SKE_KPD_KEYMAP_SIZE (8 * 8)4142/* keypad auto scan registers */43#define SKE_ASR0 0x2044#define SKE_ASR1 0x2445#define SKE_ASR2 0x2846#define SKE_ASR3 0x2C4748#define SKE_NUM_ASRX_REGISTERS (4)4950/**51* struct ske_keypad - data structure used by keypad driver52* @irq: irq no53* @reg_base: ske regsiters base address54* @input: pointer to input device object55* @board: keypad platform device56* @keymap: matrix scan code table for keycodes57* @clk: clock structure pointer58*/59struct ske_keypad {60int irq;61void __iomem *reg_base;62struct input_dev *input;63const struct ske_keypad_platform_data *board;64unsigned short keymap[SKE_KPD_KEYMAP_SIZE];65struct clk *clk;66spinlock_t ske_keypad_lock;67};6869static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,70u8 mask, u8 data)71{72u32 ret;7374spin_lock(&keypad->ske_keypad_lock);7576ret = readl(keypad->reg_base + addr);77ret &= ~mask;78ret |= data;79writel(ret, keypad->reg_base + addr);8081spin_unlock(&keypad->ske_keypad_lock);82}8384/*85* ske_keypad_chip_init: init keypad controller configuration86*87* Enable Multi key press detection, auto scan mode88*/89static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad)90{91u32 value;92int timeout = 50;9394/* check SKE_RIS to be 0 */95while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)96cpu_relax();9798if (!timeout)99return -EINVAL;100101/*102* set debounce value103* keypad dbounce is configured in DBCR[15:8]104* dbounce value in steps of 32/32.768 ms105*/106spin_lock(&keypad->ske_keypad_lock);107value = readl(keypad->reg_base + SKE_DBCR);108value = value & 0xff;109value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;110writel(value, keypad->reg_base + SKE_DBCR);111spin_unlock(&keypad->ske_keypad_lock);112113/* enable multi key detection */114ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);115116/*117* set up the number of columns118* KPCN[5:3] defines no. of keypad columns to be auto scanned119*/120value = (keypad->board->kcol - 1) << 3;121ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);122123/* clear keypad interrupt for auto(and pending SW) scans */124ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);125126/* un-mask keypad interrupts */127ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);128129/* enable automatic scan */130ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);131132return 0;133}134135static void ske_keypad_read_data(struct ske_keypad *keypad)136{137struct input_dev *input = keypad->input;138u16 status;139int col = 0, row = 0, code;140int ske_asr, ske_ris, key_pressed, i;141142/*143* Read the auto scan registers144*145* Each SKE_ASRx (x=0 to x=3) contains two row values.146* lower byte contains row value for column 2*x,147* upper byte contains row value for column 2*x + 1148*/149for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {150ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));151if (!ske_asr)152continue;153154/* now that ASRx is zero, find out the column x and row y*/155if (ske_asr & 0xff) {156col = i * 2;157status = ske_asr & 0xff;158} else {159col = (i * 2) + 1;160status = (ske_asr & 0xff00) >> 8;161}162163/* find out the row */164row = __ffs(status);165166code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);167ske_ris = readl(keypad->reg_base + SKE_RIS);168key_pressed = ske_ris & SKE_KPRISA;169170input_event(input, EV_MSC, MSC_SCAN, code);171input_report_key(input, keypad->keymap[code], key_pressed);172input_sync(input);173}174}175176static irqreturn_t ske_keypad_irq(int irq, void *dev_id)177{178struct ske_keypad *keypad = dev_id;179int retries = 20;180181/* disable auto scan interrupt; mask the interrupt generated */182ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);183ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);184185while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries)186msleep(5);187188if (retries) {189/* SKEx registers are stable and can be read */190ske_keypad_read_data(keypad);191}192193/* enable auto scan interrupts */194ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);195196return IRQ_HANDLED;197}198199static int __devinit ske_keypad_probe(struct platform_device *pdev)200{201const struct ske_keypad_platform_data *plat = pdev->dev.platform_data;202struct ske_keypad *keypad;203struct input_dev *input;204struct resource *res;205int irq;206int error;207208if (!plat) {209dev_err(&pdev->dev, "invalid keypad platform data\n");210return -EINVAL;211}212213irq = platform_get_irq(pdev, 0);214if (irq < 0) {215dev_err(&pdev->dev, "failed to get keypad irq\n");216return -EINVAL;217}218219res = platform_get_resource(pdev, IORESOURCE_MEM, 0);220if (!res) {221dev_err(&pdev->dev, "missing platform resources\n");222return -EINVAL;223}224225keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);226input = input_allocate_device();227if (!keypad || !input) {228dev_err(&pdev->dev, "failed to allocate keypad memory\n");229error = -ENOMEM;230goto err_free_mem;231}232233keypad->irq = irq;234keypad->board = plat;235keypad->input = input;236spin_lock_init(&keypad->ske_keypad_lock);237238if (!request_mem_region(res->start, resource_size(res), pdev->name)) {239dev_err(&pdev->dev, "failed to request I/O memory\n");240error = -EBUSY;241goto err_free_mem;242}243244keypad->reg_base = ioremap(res->start, resource_size(res));245if (!keypad->reg_base) {246dev_err(&pdev->dev, "failed to remap I/O memory\n");247error = -ENXIO;248goto err_free_mem_region;249}250251keypad->clk = clk_get(&pdev->dev, NULL);252if (IS_ERR(keypad->clk)) {253dev_err(&pdev->dev, "failed to get clk\n");254error = PTR_ERR(keypad->clk);255goto err_iounmap;256}257258input->id.bustype = BUS_HOST;259input->name = "ux500-ske-keypad";260input->dev.parent = &pdev->dev;261262input->keycode = keypad->keymap;263input->keycodesize = sizeof(keypad->keymap[0]);264input->keycodemax = ARRAY_SIZE(keypad->keymap);265266input_set_capability(input, EV_MSC, MSC_SCAN);267268__set_bit(EV_KEY, input->evbit);269if (!plat->no_autorepeat)270__set_bit(EV_REP, input->evbit);271272matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT,273input->keycode, input->keybit);274275clk_enable(keypad->clk);276277/* go through board initialization helpers */278if (keypad->board->init)279keypad->board->init();280281error = ske_keypad_chip_init(keypad);282if (error) {283dev_err(&pdev->dev, "unable to init keypad hardware\n");284goto err_clk_disable;285}286287error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,288IRQF_ONESHOT, "ske-keypad", keypad);289if (error) {290dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);291goto err_clk_disable;292}293294error = input_register_device(input);295if (error) {296dev_err(&pdev->dev,297"unable to register input device: %d\n", error);298goto err_free_irq;299}300301if (plat->wakeup_enable)302device_init_wakeup(&pdev->dev, true);303304platform_set_drvdata(pdev, keypad);305306return 0;307308err_free_irq:309free_irq(keypad->irq, keypad);310err_clk_disable:311clk_disable(keypad->clk);312clk_put(keypad->clk);313err_iounmap:314iounmap(keypad->reg_base);315err_free_mem_region:316release_mem_region(res->start, resource_size(res));317err_free_mem:318input_free_device(input);319kfree(keypad);320return error;321}322323static int __devexit ske_keypad_remove(struct platform_device *pdev)324{325struct ske_keypad *keypad = platform_get_drvdata(pdev);326struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);327328free_irq(keypad->irq, keypad);329330input_unregister_device(keypad->input);331332clk_disable(keypad->clk);333clk_put(keypad->clk);334335if (keypad->board->exit)336keypad->board->exit();337338iounmap(keypad->reg_base);339release_mem_region(res->start, resource_size(res));340kfree(keypad);341342return 0;343}344345#ifdef CONFIG_PM346static int ske_keypad_suspend(struct device *dev)347{348struct platform_device *pdev = to_platform_device(dev);349struct ske_keypad *keypad = platform_get_drvdata(pdev);350int irq = platform_get_irq(pdev, 0);351352if (device_may_wakeup(dev))353enable_irq_wake(irq);354else355ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);356357return 0;358}359360static int ske_keypad_resume(struct device *dev)361{362struct platform_device *pdev = to_platform_device(dev);363struct ske_keypad *keypad = platform_get_drvdata(pdev);364int irq = platform_get_irq(pdev, 0);365366if (device_may_wakeup(dev))367disable_irq_wake(irq);368else369ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);370371return 0;372}373374static const struct dev_pm_ops ske_keypad_dev_pm_ops = {375.suspend = ske_keypad_suspend,376.resume = ske_keypad_resume,377};378#endif379380struct platform_driver ske_keypad_driver = {381.driver = {382.name = "nmk-ske-keypad",383.owner = THIS_MODULE,384#ifdef CONFIG_PM385.pm = &ske_keypad_dev_pm_ops,386#endif387},388.probe = ske_keypad_probe,389.remove = __devexit_p(ske_keypad_remove),390};391392static int __init ske_keypad_init(void)393{394return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe);395}396module_init(ske_keypad_init);397398static void __exit ske_keypad_exit(void)399{400platform_driver_unregister(&ske_keypad_driver);401}402module_exit(ske_keypad_exit);403404MODULE_LICENSE("GPL v2");405MODULE_AUTHOR("Naveen Kumar <[email protected]> / Sundar Iyer <[email protected]>");406MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");407MODULE_ALIAS("platform:nomadik-ske-keypad");408409410