Skip to content

Commit 0bf25a4

Browse files
Wolfram Sangdtor
authored andcommitted
Input: add support for LM8333 keypads
This driver adds support for the keypad part of the LM8333 and is prepared for possible GPIO/PWM drivers. Note that this is not a MFD because you cannot disable the keypad functionality which, thus, has to be handled by the core anyhow. Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
1 parent 5a96626 commit 0bf25a4

4 files changed

Lines changed: 271 additions & 0 deletions

File tree

drivers/input/keyboard/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,16 @@ config KEYBOARD_LM8323
309309
To compile this driver as a module, choose M here: the
310310
module will be called lm8323.
311311

312+
config KEYBOARD_LM8333
313+
tristate "LM8333 keypad chip"
314+
depends on I2C
315+
help
316+
If you say yes here you get support for the National Semiconductor
317+
LM8333 keypad controller.
318+
319+
To compile this driver as a module, choose M here: the
320+
module will be called lm8333.
321+
312322
config KEYBOARD_LOCOMO
313323
tristate "LoCoMo Keyboard Support"
314324
depends on SHARP_LOCOMO

drivers/input/keyboard/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
2424
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
2525
obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
2626
obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o
27+
obj-$(CONFIG_KEYBOARD_LM8333) += lm8333.o
2728
obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
2829
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
2930
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o

drivers/input/keyboard/lm8333.c

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/*
2+
* LM8333 keypad driver
3+
* Copyright (C) 2012 Wolfram Sang, Pengutronix <w.sang@pengutronix.de>
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation; either version 2 of the License.
8+
*/
9+
10+
#include <linux/module.h>
11+
#include <linux/slab.h>
12+
#include <linux/irq.h>
13+
#include <linux/i2c.h>
14+
#include <linux/interrupt.h>
15+
#include <linux/input/matrix_keypad.h>
16+
#include <linux/input/lm8333.h>
17+
18+
#define LM8333_FIFO_READ 0x20
19+
#define LM8333_DEBOUNCE 0x22
20+
#define LM8333_READ_INT 0xD0
21+
#define LM8333_ACTIVE 0xE4
22+
#define LM8333_READ_ERROR 0xF0
23+
24+
#define LM8333_KEYPAD_IRQ (1 << 0)
25+
#define LM8333_ERROR_IRQ (1 << 3)
26+
27+
#define LM8333_ERROR_KEYOVR 0x04
28+
#define LM8333_ERROR_FIFOOVR 0x40
29+
30+
#define LM8333_FIFO_TRANSFER_SIZE 16
31+
32+
#define LM8333_ROW_SHIFT 4
33+
#define LM8333_NUM_ROWS 8
34+
35+
36+
struct lm8333 {
37+
struct i2c_client *client;
38+
struct input_dev *input;
39+
unsigned short keycodes[LM8333_NUM_ROWS << LM8333_ROW_SHIFT];
40+
};
41+
42+
/* The accessors try twice because the first access may be needed for wakeup */
43+
#define LM8333_READ_RETRIES 2
44+
45+
int lm8333_read8(struct lm8333 *lm8333, u8 cmd)
46+
{
47+
int retries = 0, ret;
48+
49+
do {
50+
ret = i2c_smbus_read_byte_data(lm8333->client, cmd);
51+
} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
52+
53+
return ret;
54+
}
55+
56+
int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val)
57+
{
58+
int retries = 0, ret;
59+
60+
do {
61+
ret = i2c_smbus_write_byte_data(lm8333->client, cmd, val);
62+
} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
63+
64+
return ret;
65+
}
66+
67+
int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf)
68+
{
69+
int retries = 0, ret;
70+
71+
do {
72+
ret = i2c_smbus_read_i2c_block_data(lm8333->client,
73+
cmd, len, buf);
74+
} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
75+
76+
return ret;
77+
}
78+
79+
static void lm8333_key_handler(struct lm8333 *lm8333)
80+
{
81+
struct input_dev *input = lm8333->input;
82+
u8 keys[LM8333_FIFO_TRANSFER_SIZE];
83+
u8 code, pressed;
84+
int i, ret;
85+
86+
ret = lm8333_read_block(lm8333, LM8333_FIFO_READ,
87+
LM8333_FIFO_TRANSFER_SIZE, keys);
88+
if (ret != LM8333_FIFO_TRANSFER_SIZE) {
89+
dev_err(&lm8333->client->dev,
90+
"Error %d while reading FIFO\n", ret);
91+
return;
92+
}
93+
94+
for (i = 0; keys[i] && i < LM8333_FIFO_TRANSFER_SIZE; i++) {
95+
pressed = keys[i] & 0x80;
96+
code = keys[i] & 0x7f;
97+
98+
input_event(input, EV_MSC, MSC_SCAN, code);
99+
input_report_key(input, lm8333->keycodes[code], pressed);
100+
}
101+
102+
input_sync(input);
103+
}
104+
105+
static irqreturn_t lm8333_irq_thread(int irq, void *data)
106+
{
107+
struct lm8333 *lm8333 = data;
108+
u8 status = lm8333_read8(lm8333, LM8333_READ_INT);
109+
110+
if (!status)
111+
return IRQ_NONE;
112+
113+
if (status & LM8333_ERROR_IRQ) {
114+
u8 err = lm8333_read8(lm8333, LM8333_READ_ERROR);
115+
116+
if (err & (LM8333_ERROR_KEYOVR | LM8333_ERROR_FIFOOVR)) {
117+
u8 dummy[LM8333_FIFO_TRANSFER_SIZE];
118+
119+
lm8333_read_block(lm8333, LM8333_FIFO_READ,
120+
LM8333_FIFO_TRANSFER_SIZE, dummy);
121+
}
122+
dev_err(&lm8333->client->dev, "Got error %02x\n", err);
123+
}
124+
125+
if (status & LM8333_KEYPAD_IRQ)
126+
lm8333_key_handler(lm8333);
127+
128+
return IRQ_HANDLED;
129+
}
130+
131+
static int __devinit lm8333_probe(struct i2c_client *client,
132+
const struct i2c_device_id *id)
133+
{
134+
const struct lm8333_platform_data *pdata = client->dev.platform_data;
135+
struct lm8333 *lm8333;
136+
struct input_dev *input;
137+
int err, active_time;
138+
139+
if (!pdata)
140+
return -EINVAL;
141+
142+
active_time = pdata->active_time ?: 500;
143+
if (active_time / 3 <= pdata->debounce_time / 3) {
144+
dev_err(&client->dev, "Active time not big enough!\n");
145+
return -EINVAL;
146+
}
147+
148+
lm8333 = kzalloc(sizeof(*lm8333), GFP_KERNEL);
149+
input = input_allocate_device();
150+
if (!lm8333 || !input) {
151+
err = -ENOMEM;
152+
goto free_mem;
153+
}
154+
155+
lm8333->client = client;
156+
lm8333->input = input;
157+
158+
input->name = client->name;
159+
input->dev.parent = &client->dev;
160+
input->id.bustype = BUS_I2C;
161+
162+
input->keycode = lm8333->keycodes;
163+
input->keycodesize = sizeof(lm8333->keycodes[0]);
164+
input->keycodemax = ARRAY_SIZE(lm8333->keycodes);
165+
input->evbit[0] = BIT_MASK(EV_KEY);
166+
input_set_capability(input, EV_MSC, MSC_SCAN);
167+
168+
matrix_keypad_build_keymap(pdata->matrix_data, LM8333_ROW_SHIFT,
169+
input->keycode, input->keybit);
170+
171+
if (pdata->debounce_time) {
172+
err = lm8333_write8(lm8333, LM8333_DEBOUNCE,
173+
pdata->debounce_time / 3);
174+
if (err)
175+
dev_warn(&client->dev, "Unable to set debounce time\n");
176+
}
177+
178+
if (pdata->active_time) {
179+
err = lm8333_write8(lm8333, LM8333_ACTIVE,
180+
pdata->active_time / 3);
181+
if (err)
182+
dev_warn(&client->dev, "Unable to set active time\n");
183+
}
184+
185+
err = request_threaded_irq(client->irq, NULL, lm8333_irq_thread,
186+
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
187+
"lm8333", lm8333);
188+
if (err)
189+
goto free_mem;
190+
191+
err = input_register_device(input);
192+
if (err)
193+
goto free_irq;
194+
195+
i2c_set_clientdata(client, lm8333);
196+
return 0;
197+
198+
free_irq:
199+
free_irq(client->irq, lm8333);
200+
free_mem:
201+
input_free_device(input);
202+
kfree(lm8333);
203+
return err;
204+
}
205+
206+
static int __devexit lm8333_remove(struct i2c_client *client)
207+
{
208+
struct lm8333 *lm8333 = i2c_get_clientdata(client);
209+
210+
free_irq(client->irq, lm8333);
211+
input_unregister_device(lm8333->input);
212+
kfree(lm8333);
213+
214+
return 0;
215+
}
216+
217+
static const struct i2c_device_id lm8333_id[] = {
218+
{ "lm8333", 0 },
219+
{ }
220+
};
221+
MODULE_DEVICE_TABLE(i2c, lm8333_id);
222+
223+
static struct i2c_driver lm8333_driver = {
224+
.driver = {
225+
.name = "lm8333",
226+
.owner = THIS_MODULE,
227+
},
228+
.probe = lm8333_probe,
229+
.remove = __devexit_p(lm8333_remove),
230+
.id_table = lm8333_id,
231+
};
232+
module_i2c_driver(lm8333_driver);
233+
234+
MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
235+
MODULE_DESCRIPTION("LM8333 keyboard driver");
236+
MODULE_LICENSE("GPL v2");

include/linux/input/lm8333.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* public include for LM8333 keypad driver - same license as driver
3+
* Copyright (C) 2012 Wolfram Sang, Pengutronix <w.sang@pengutronix.de>
4+
*/
5+
6+
#ifndef _LM8333_H
7+
#define _LM8333_H
8+
9+
struct lm8333;
10+
11+
struct lm8333_platform_data {
12+
/* Keymap data */
13+
const struct matrix_keymap_data *matrix_data;
14+
/* Active timeout before enter HALT mode in microseconds */
15+
unsigned active_time;
16+
/* Debounce interval in microseconds */
17+
unsigned debounce_time;
18+
};
19+
20+
extern int lm8333_read8(struct lm8333 *lm8333, u8 cmd);
21+
extern int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val);
22+
extern int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf);
23+
24+
#endif /* _LM8333_H */

0 commit comments

Comments
 (0)