blob: e83cab27eb569442362d9a0e987a53c8445e381c [file] [log] [blame]
Rajeev Kumarbc95df72010-11-19 12:41:19 -08001/*
2 * SPEAr Keyboard Driver
3 * Based on omap-keypad driver
4 *
5 * Copyright (C) 2010 ST Microelectronics
6 * Rajeev Kumar<rajeev-dlh.kumar@st.com>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 */
12
13#include <linux/clk.h>
14#include <linux/errno.h>
15#include <linux/init.h>
16#include <linux/interrupt.h>
17#include <linux/input.h>
18#include <linux/io.h>
19#include <linux/irq.h>
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/platform_device.h>
23#include <linux/pm_wakeup.h>
24#include <linux/slab.h>
25#include <linux/types.h>
26#include <plat/keyboard.h>
27
28/* Keyboard Registers */
29#define MODE_REG 0x00 /* 16 bit reg */
30#define STATUS_REG 0x0C /* 2 bit reg */
31#define DATA_REG 0x10 /* 8 bit reg */
32#define INTR_MASK 0x54
33
34/* Register Values */
35/*
36 * pclk freq mask = (APB FEQ -1)= 82 MHZ.Programme bit 15-9 in mode
37 * control register as 1010010(82MHZ)
38 */
39#define PCLK_FREQ_MSK 0xA400 /* 82 MHz */
40#define START_SCAN 0x0100
41#define SCAN_RATE_10 0x0000
42#define SCAN_RATE_20 0x0004
43#define SCAN_RATE_40 0x0008
44#define SCAN_RATE_80 0x000C
45#define MODE_KEYBOARD 0x0002
46#define DATA_AVAIL 0x2
47
48#define KEY_MASK 0xFF000000
49#define KEY_VALUE 0x00FFFFFF
50#define ROW_MASK 0xF0
51#define COLUMN_MASK 0x0F
Dmitry Torokhov19328112012-05-10 22:37:08 -070052#define NUM_ROWS 16
53#define NUM_COLS 16
54
Rajeev Kumarf8354c62012-02-24 00:51:40 -080055#define KEY_MATRIX_SHIFT 6
Rajeev Kumarbc95df72010-11-19 12:41:19 -080056
57struct spear_kbd {
58 struct input_dev *input;
59 struct resource *res;
60 void __iomem *io_base;
61 struct clk *clk;
62 unsigned int irq;
Rajeev Kumarf8354c62012-02-24 00:51:40 -080063 unsigned int mode;
Rajeev Kumarbc95df72010-11-19 12:41:19 -080064 unsigned short last_key;
Dmitry Torokhov19328112012-05-10 22:37:08 -070065 unsigned short keycodes[NUM_ROWS * NUM_COLS];
Rajeev Kumarbc95df72010-11-19 12:41:19 -080066};
67
68static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
69{
70 struct spear_kbd *kbd = dev_id;
71 struct input_dev *input = kbd->input;
72 unsigned int key;
73 u8 sts, val;
74
75 sts = readb(kbd->io_base + STATUS_REG);
Rajeev Kumar799a2a22011-03-30 22:33:42 -070076 if (!(sts & DATA_AVAIL))
Rajeev Kumarbc95df72010-11-19 12:41:19 -080077 return IRQ_NONE;
78
79 if (kbd->last_key != KEY_RESERVED) {
80 input_report_key(input, kbd->last_key, 0);
81 kbd->last_key = KEY_RESERVED;
82 }
83
84 /* following reads active (row, col) pair */
85 val = readb(kbd->io_base + DATA_REG);
86 key = kbd->keycodes[val];
87
88 input_event(input, EV_MSC, MSC_SCAN, val);
89 input_report_key(input, key, 1);
90 input_sync(input);
91
92 kbd->last_key = key;
93
94 /* clear interrupt */
95 writeb(0, kbd->io_base + STATUS_REG);
96
97 return IRQ_HANDLED;
98}
99
100static int spear_kbd_open(struct input_dev *dev)
101{
102 struct spear_kbd *kbd = input_get_drvdata(dev);
103 int error;
104 u16 val;
105
106 kbd->last_key = KEY_RESERVED;
107
108 error = clk_enable(kbd->clk);
109 if (error)
110 return error;
111
112 /* program keyboard */
Rajeev Kumarf8354c62012-02-24 00:51:40 -0800113 val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK |
114 (kbd->mode << KEY_MATRIX_SHIFT);
Rajeev Kumarbc95df72010-11-19 12:41:19 -0800115 writew(val, kbd->io_base + MODE_REG);
116 writeb(1, kbd->io_base + STATUS_REG);
117
118 /* start key scan */
119 val = readw(kbd->io_base + MODE_REG);
120 val |= START_SCAN;
121 writew(val, kbd->io_base + MODE_REG);
122
123 return 0;
124}
125
126static void spear_kbd_close(struct input_dev *dev)
127{
128 struct spear_kbd *kbd = input_get_drvdata(dev);
129 u16 val;
130
131 /* stop key scan */
132 val = readw(kbd->io_base + MODE_REG);
133 val &= ~START_SCAN;
134 writew(val, kbd->io_base + MODE_REG);
135
136 clk_disable(kbd->clk);
137
138 kbd->last_key = KEY_RESERVED;
139}
140
141static int __devinit spear_kbd_probe(struct platform_device *pdev)
142{
143 const struct kbd_platform_data *pdata = pdev->dev.platform_data;
144 const struct matrix_keymap_data *keymap;
145 struct spear_kbd *kbd;
146 struct input_dev *input_dev;
147 struct resource *res;
148 int irq;
149 int error;
150
151 if (!pdata) {
152 dev_err(&pdev->dev, "Invalid platform data\n");
153 return -EINVAL;
154 }
155
156 keymap = pdata->keymap;
157 if (!keymap) {
158 dev_err(&pdev->dev, "no keymap defined\n");
159 return -EINVAL;
160 }
161
162 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
163 if (!res) {
164 dev_err(&pdev->dev, "no keyboard resource defined\n");
165 return -EBUSY;
166 }
167
168 irq = platform_get_irq(pdev, 0);
169 if (irq < 0) {
170 dev_err(&pdev->dev, "not able to get irq for the device\n");
171 return irq;
172 }
173
174 kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
175 input_dev = input_allocate_device();
176 if (!kbd || !input_dev) {
177 dev_err(&pdev->dev, "out of memory\n");
178 error = -ENOMEM;
179 goto err_free_mem;
180 }
181
182 kbd->input = input_dev;
183 kbd->irq = irq;
Rajeev Kumarf8354c62012-02-24 00:51:40 -0800184 kbd->mode = pdata->mode;
185
Rajeev Kumarbc95df72010-11-19 12:41:19 -0800186 kbd->res = request_mem_region(res->start, resource_size(res),
187 pdev->name);
188 if (!kbd->res) {
189 dev_err(&pdev->dev, "keyboard region already claimed\n");
190 error = -EBUSY;
191 goto err_free_mem;
192 }
193
194 kbd->io_base = ioremap(res->start, resource_size(res));
195 if (!kbd->io_base) {
196 dev_err(&pdev->dev, "ioremap failed for kbd_region\n");
197 error = -ENOMEM;
198 goto err_release_mem_region;
199 }
200
201 kbd->clk = clk_get(&pdev->dev, NULL);
202 if (IS_ERR(kbd->clk)) {
203 error = PTR_ERR(kbd->clk);
204 goto err_iounmap;
205 }
206
207 input_dev->name = "Spear Keyboard";
208 input_dev->phys = "keyboard/input0";
209 input_dev->dev.parent = &pdev->dev;
210 input_dev->id.bustype = BUS_HOST;
211 input_dev->id.vendor = 0x0001;
212 input_dev->id.product = 0x0001;
213 input_dev->id.version = 0x0100;
214 input_dev->open = spear_kbd_open;
215 input_dev->close = spear_kbd_close;
216
Dmitry Torokhov19328112012-05-10 22:37:08 -0700217 error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS,
218 kbd->keycodes, input_dev);
219 if (error) {
220 dev_err(&pdev->dev, "Failed to build keymap\n");
221 goto err_put_clk;
222 }
223
Rajeev Kumarbc95df72010-11-19 12:41:19 -0800224 if (pdata->rep)
225 __set_bit(EV_REP, input_dev->evbit);
226 input_set_capability(input_dev, EV_MSC, MSC_SCAN);
227
Rajeev Kumarbc95df72010-11-19 12:41:19 -0800228 input_set_drvdata(input_dev, kbd);
229
230 error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd);
231 if (error) {
232 dev_err(&pdev->dev, "request_irq fail\n");
233 goto err_put_clk;
234 }
235
236 error = input_register_device(input_dev);
237 if (error) {
238 dev_err(&pdev->dev, "Unable to register keyboard device\n");
239 goto err_free_irq;
240 }
241
242 device_init_wakeup(&pdev->dev, 1);
243 platform_set_drvdata(pdev, kbd);
244
245 return 0;
246
247err_free_irq:
248 free_irq(kbd->irq, kbd);
249err_put_clk:
250 clk_put(kbd->clk);
251err_iounmap:
252 iounmap(kbd->io_base);
253err_release_mem_region:
254 release_mem_region(res->start, resource_size(res));
255err_free_mem:
256 input_free_device(input_dev);
257 kfree(kbd);
258
259 return error;
260}
261
262static int __devexit spear_kbd_remove(struct platform_device *pdev)
263{
264 struct spear_kbd *kbd = platform_get_drvdata(pdev);
265
266 free_irq(kbd->irq, kbd);
267 input_unregister_device(kbd->input);
268 clk_put(kbd->clk);
269 iounmap(kbd->io_base);
270 release_mem_region(kbd->res->start, resource_size(kbd->res));
271 kfree(kbd);
272
273 device_init_wakeup(&pdev->dev, 1);
274 platform_set_drvdata(pdev, NULL);
275
276 return 0;
277}
278
279#ifdef CONFIG_PM
280static int spear_kbd_suspend(struct device *dev)
281{
282 struct platform_device *pdev = to_platform_device(dev);
283 struct spear_kbd *kbd = platform_get_drvdata(pdev);
284 struct input_dev *input_dev = kbd->input;
285
286 mutex_lock(&input_dev->mutex);
287
288 if (input_dev->users)
289 clk_enable(kbd->clk);
290
291 if (device_may_wakeup(&pdev->dev))
292 enable_irq_wake(kbd->irq);
293
294 mutex_unlock(&input_dev->mutex);
295
296 return 0;
297}
298
299static int spear_kbd_resume(struct device *dev)
300{
301 struct platform_device *pdev = to_platform_device(dev);
302 struct spear_kbd *kbd = platform_get_drvdata(pdev);
303 struct input_dev *input_dev = kbd->input;
304
305 mutex_lock(&input_dev->mutex);
306
307 if (device_may_wakeup(&pdev->dev))
308 disable_irq_wake(kbd->irq);
309
310 if (input_dev->users)
311 clk_enable(kbd->clk);
312
313 mutex_unlock(&input_dev->mutex);
314
315 return 0;
316}
Rajeev Kumarbc95df72010-11-19 12:41:19 -0800317#endif
318
Viresh Kumarf79e30a2012-02-24 00:51:37 -0800319static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume);
320
Rajeev Kumarbc95df72010-11-19 12:41:19 -0800321static struct platform_driver spear_kbd_driver = {
322 .probe = spear_kbd_probe,
323 .remove = __devexit_p(spear_kbd_remove),
324 .driver = {
325 .name = "keyboard",
326 .owner = THIS_MODULE,
Rajeev Kumarbc95df72010-11-19 12:41:19 -0800327 .pm = &spear_kbd_pm_ops,
Rajeev Kumarbc95df72010-11-19 12:41:19 -0800328 },
329};
JJ Ding5146c842011-11-29 11:08:39 -0800330module_platform_driver(spear_kbd_driver);
Rajeev Kumarbc95df72010-11-19 12:41:19 -0800331
332MODULE_AUTHOR("Rajeev Kumar");
333MODULE_DESCRIPTION("SPEAr Keyboard Driver");
334MODULE_LICENSE("GPL");