Path: blob/master/drivers/input/keyboard/bf54x-keys.c
15111 views
/*1* File: drivers/input/keyboard/bf54x-keys.c2* Based on:3* Author: Michael Hennerich <[email protected]>4*5* Created:6* Description: keypad driver for Analog Devices Blackfin BF54x Processors7*8*9* Modified:10* Copyright 2007-2008 Analog Devices Inc.11*12* Bugs: Enter bugs at http://blackfin.uclinux.org/13*14* This program is free software; you can redistribute it and/or modify15* it under the terms of the GNU General Public License as published by16* the Free Software Foundation; either version 2 of the License, or17* (at your option) any later version.18*19* This program is distributed in the hope that it will be useful,20* but WITHOUT ANY WARRANTY; without even the implied warranty of21* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the22* GNU General Public License for more details.23*24* You should have received a copy of the GNU General Public License25* along with this program; if not, see the file COPYING, or write26* to the Free Software Foundation, Inc.,27* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA28*/2930#include <linux/module.h>3132#include <linux/init.h>33#include <linux/fs.h>34#include <linux/interrupt.h>35#include <linux/irq.h>36#include <linux/slab.h>37#include <linux/sched.h>38#include <linux/pm.h>39#include <linux/sysctl.h>40#include <linux/proc_fs.h>41#include <linux/delay.h>42#include <linux/platform_device.h>43#include <linux/input.h>4445#include <asm/portmux.h>46#include <mach/bf54x_keys.h>4748#define DRV_NAME "bf54x-keys"49#define TIME_SCALE 100 /* 100 ns */50#define MAX_MULT (0xFF * TIME_SCALE)51#define MAX_RC 8 /* Max Row/Col */5253static const u16 per_rows[] = {54P_KEY_ROW7,55P_KEY_ROW6,56P_KEY_ROW5,57P_KEY_ROW4,58P_KEY_ROW3,59P_KEY_ROW2,60P_KEY_ROW1,61P_KEY_ROW0,62063};6465static const u16 per_cols[] = {66P_KEY_COL7,67P_KEY_COL6,68P_KEY_COL5,69P_KEY_COL4,70P_KEY_COL3,71P_KEY_COL2,72P_KEY_COL1,73P_KEY_COL0,74075};7677struct bf54x_kpad {78struct input_dev *input;79int irq;80unsigned short lastkey;81unsigned short *keycode;82struct timer_list timer;83unsigned int keyup_test_jiffies;84unsigned short kpad_msel;85unsigned short kpad_prescale;86unsigned short kpad_ctl;87};8889static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,90struct input_dev *input, u16 keyident)91{92u16 i;9394for (i = 0; i < input->keycodemax; i++)95if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)96return bf54x_kpad->keycode[i];97return -1;98}99100static inline void bfin_keycodecpy(unsigned short *keycode,101const unsigned int *pdata_kc,102unsigned short keymapsize)103{104unsigned int i;105106for (i = 0; i < keymapsize; i++) {107keycode[i] = pdata_kc[i] & 0xffff;108keycode[i + keymapsize] = pdata_kc[i] >> 16;109}110}111112static inline u16 bfin_kpad_get_prescale(u32 timescale)113{114u32 sclk = get_sclk();115116return ((((sclk / 1000) * timescale) / 1024) - 1);117}118119static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)120{121return (bfin_read_KPAD_STAT() & KPAD_PRESSED);122}123124static inline void bfin_kpad_clear_irq(void)125{126bfin_write_KPAD_STAT(0xFFFF);127bfin_write_KPAD_ROWCOL(0xFFFF);128}129130static void bfin_kpad_timer(unsigned long data)131{132struct platform_device *pdev = (struct platform_device *) data;133struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);134135if (bfin_kpad_get_keypressed(bf54x_kpad)) {136/* Try again later */137mod_timer(&bf54x_kpad->timer,138jiffies + bf54x_kpad->keyup_test_jiffies);139return;140}141142input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);143input_sync(bf54x_kpad->input);144145/* Clear IRQ Status */146147bfin_kpad_clear_irq();148enable_irq(bf54x_kpad->irq);149}150151static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)152{153struct platform_device *pdev = dev_id;154struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);155struct input_dev *input = bf54x_kpad->input;156int key;157u16 rowcol = bfin_read_KPAD_ROWCOL();158159key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);160161input_report_key(input, key, 1);162input_sync(input);163164if (bfin_kpad_get_keypressed(bf54x_kpad)) {165disable_irq_nosync(bf54x_kpad->irq);166bf54x_kpad->lastkey = key;167mod_timer(&bf54x_kpad->timer,168jiffies + bf54x_kpad->keyup_test_jiffies);169} else {170input_report_key(input, key, 0);171input_sync(input);172173bfin_kpad_clear_irq();174}175176return IRQ_HANDLED;177}178179static int __devinit bfin_kpad_probe(struct platform_device *pdev)180{181struct bf54x_kpad *bf54x_kpad;182struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;183struct input_dev *input;184int i, error;185186if (!pdata->rows || !pdata->cols || !pdata->keymap) {187dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n");188return -EINVAL;189}190191if (!pdata->keymapsize ||192pdata->keymapsize > (pdata->rows * pdata->cols)) {193dev_err(&pdev->dev, "invalid keymapsize\n");194return -EINVAL;195}196197bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);198if (!bf54x_kpad)199return -ENOMEM;200201platform_set_drvdata(pdev, bf54x_kpad);202203/* Allocate memory for keymap followed by private LUT */204bf54x_kpad->keycode = kmalloc(pdata->keymapsize *205sizeof(unsigned short) * 2, GFP_KERNEL);206if (!bf54x_kpad->keycode) {207error = -ENOMEM;208goto out;209}210211if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||212!pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {213dev_warn(&pdev->dev,214"invalid platform debounce/columndrive time\n");215bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */216} else {217bfin_write_KPAD_MSEL(218((pdata->debounce_time / TIME_SCALE)219& DBON_SCALE) |220(((pdata->coldrive_time / TIME_SCALE) << 8)221& COLDRV_SCALE));222223}224225if (!pdata->keyup_test_interval)226bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);227else228bf54x_kpad->keyup_test_jiffies =229msecs_to_jiffies(pdata->keyup_test_interval);230231if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],232DRV_NAME)) {233dev_err(&pdev->dev, "requesting peripherals failed\n");234error = -EFAULT;235goto out0;236}237238if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],239DRV_NAME)) {240dev_err(&pdev->dev, "requesting peripherals failed\n");241error = -EFAULT;242goto out1;243}244245bf54x_kpad->irq = platform_get_irq(pdev, 0);246if (bf54x_kpad->irq < 0) {247error = -ENODEV;248goto out2;249}250251error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,2520, DRV_NAME, pdev);253if (error) {254dev_err(&pdev->dev, "unable to claim irq %d\n",255bf54x_kpad->irq);256goto out2;257}258259input = input_allocate_device();260if (!input) {261error = -ENOMEM;262goto out3;263}264265bf54x_kpad->input = input;266267input->name = pdev->name;268input->phys = "bf54x-keys/input0";269input->dev.parent = &pdev->dev;270271input_set_drvdata(input, bf54x_kpad);272273input->id.bustype = BUS_HOST;274input->id.vendor = 0x0001;275input->id.product = 0x0001;276input->id.version = 0x0100;277278input->keycodesize = sizeof(unsigned short);279input->keycodemax = pdata->keymapsize;280input->keycode = bf54x_kpad->keycode;281282bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);283284/* setup input device */285__set_bit(EV_KEY, input->evbit);286287if (pdata->repeat)288__set_bit(EV_REP, input->evbit);289290for (i = 0; i < input->keycodemax; i++)291__set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit);292__clear_bit(KEY_RESERVED, input->keybit);293294error = input_register_device(input);295if (error) {296dev_err(&pdev->dev, "unable to register input device\n");297goto out4;298}299300/* Init Keypad Key Up/Release test timer */301302setup_timer(&bf54x_kpad->timer, bfin_kpad_timer, (unsigned long) pdev);303304bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));305306bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |307(((pdata->rows - 1) << 10) & KPAD_ROWEN) |308(2 & KPAD_IRQMODE));309310bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);311312device_init_wakeup(&pdev->dev, 1);313314return 0;315316out4:317input_free_device(input);318out3:319free_irq(bf54x_kpad->irq, pdev);320out2:321peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);322out1:323peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);324out0:325kfree(bf54x_kpad->keycode);326out:327kfree(bf54x_kpad);328platform_set_drvdata(pdev, NULL);329330return error;331}332333static int __devexit bfin_kpad_remove(struct platform_device *pdev)334{335struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;336struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);337338del_timer_sync(&bf54x_kpad->timer);339free_irq(bf54x_kpad->irq, pdev);340341input_unregister_device(bf54x_kpad->input);342343peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);344peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);345346kfree(bf54x_kpad->keycode);347kfree(bf54x_kpad);348platform_set_drvdata(pdev, NULL);349350return 0;351}352353#ifdef CONFIG_PM354static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)355{356struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);357358bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL();359bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE();360bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL();361362if (device_may_wakeup(&pdev->dev))363enable_irq_wake(bf54x_kpad->irq);364365return 0;366}367368static int bfin_kpad_resume(struct platform_device *pdev)369{370struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);371372bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel);373bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale);374bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl);375376if (device_may_wakeup(&pdev->dev))377disable_irq_wake(bf54x_kpad->irq);378379return 0;380}381#else382# define bfin_kpad_suspend NULL383# define bfin_kpad_resume NULL384#endif385386struct platform_driver bfin_kpad_device_driver = {387.driver = {388.name = DRV_NAME,389.owner = THIS_MODULE,390},391.probe = bfin_kpad_probe,392.remove = __devexit_p(bfin_kpad_remove),393.suspend = bfin_kpad_suspend,394.resume = bfin_kpad_resume,395};396397static int __init bfin_kpad_init(void)398{399return platform_driver_register(&bfin_kpad_device_driver);400}401402static void __exit bfin_kpad_exit(void)403{404platform_driver_unregister(&bfin_kpad_device_driver);405}406407module_init(bfin_kpad_init);408module_exit(bfin_kpad_exit);409410MODULE_LICENSE("GPL");411MODULE_AUTHOR("Michael Hennerich <[email protected]>");412MODULE_DESCRIPTION("Keypad driver for BF54x Processors");413MODULE_ALIAS("platform:bf54x-keys");414415416