Root/target/linux/xburst/patches-2.6.36/800-n516-lpc.patch

1From b3c2e7322c09f0ce2f7ba89b7c6fceb6e00c2da0 Mon Sep 17 00:00:00 2001
2From: Lars-Peter Clausen <lars@metafoo.de>
3Date: Wed, 12 May 2010 14:22:36 +0200
4Subject: [PATCH] Add n516 lpc driver
5
6---
7 drivers/misc/Kconfig | 8 +
8 drivers/misc/Makefile | 1 +
9 drivers/misc/n516-lpc.c | 471 +++++++++++++++++++++++++++++++++++++++++++++++
10 3 files changed, 480 insertions(+), 0 deletions(-)
11 create mode 100644 drivers/misc/n516-lpc.c
12
13--- a/drivers/misc/Kconfig
14+++ b/drivers/misc/Kconfig
15@@ -390,6 +390,14 @@ config BMP085
16       To compile this driver as a module, choose M here: the
17       module will be called bmp085.
18 
19+config N516_LPC
20+ tristate "N516 keys & power controller"
21+ depends on I2C
22+ depends on INPUT
23+ depends on POWER_SUPPLY
24+ help
25+ N516 keyboard & power controller driver
26+
27 source "drivers/misc/c2port/Kconfig"
28 source "drivers/misc/eeprom/Kconfig"
29 source "drivers/misc/cb710/Kconfig"
30--- a/drivers/misc/Makefile
31+++ b/drivers/misc/Makefile
32@@ -35,3 +35,4 @@ obj-y += eeprom/
33 obj-y += cb710/
34 obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o
35 obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
36+obj-$(CONFIG_N516_LPC) += n516-lpc.o
37--- /dev/null
38+++ b/drivers/misc/n516-lpc.c
39@@ -0,0 +1,471 @@
40+#include <linux/module.h>
41+#include <linux/version.h>
42+#include <linux/init.h>
43+#include <linux/fs.h>
44+#include <linux/interrupt.h>
45+#include <linux/irq.h>
46+#include <linux/sched.h>
47+#include <linux/pm.h>
48+#include <linux/sysctl.h>
49+#include <linux/proc_fs.h>
50+#include <linux/delay.h>
51+#include <linux/platform_device.h>
52+#include <linux/input.h>
53+#include <linux/power_supply.h>
54+#include <linux/suspend.h>
55+
56+#include <linux/i2c.h>
57+
58+#include <asm/mach-jz4740/irq.h>
59+#include <asm/mach-jz4740/gpio.h>
60+#include <asm/mach-jz4740/board-n516.h>
61+
62+static int batt_level=0;
63+module_param(batt_level, int, 0);
64+
65+struct n516_lpc_chip {
66+ struct i2c_client *i2c_client;
67+ struct input_dev *input;
68+ unsigned int battery_level;
69+ unsigned int suspending:1, can_sleep:1;
70+};
71+
72+static struct n516_lpc_chip *the_lpc;
73+
74+struct i2c_device_id n516_lpc_i2c_ids[] = {
75+ {"LPC524", 0},
76+ {},
77+};
78+
79+MODULE_DEVICE_TABLE(i2c, n516_lpc_i2c_ids);
80+
81+static const unsigned short normal_i2c[] = I2C_ADDRS(0x54);
82+
83+static const unsigned int n516_lpc_keymap[] = {
84+ [0x01] = KEY_4,
85+ [0x02] = KEY_3,
86+ [0x03] = KEY_2,
87+ [0x04] = KEY_1,
88+ [0x05] = KEY_0,
89+ [0x07] = KEY_9,
90+ [0x08] = KEY_8,
91+ [0x09] = KEY_7,
92+ [0x0a] = KEY_6,
93+ [0x0b] = KEY_5,
94+ [0x0d] = KEY_PLAYPAUSE,
95+ [0x0e] = KEY_MENU,
96+ [0x0f] = KEY_SEARCH,
97+ [0x10] = KEY_DIRECTION,
98+ [0x11] = KEY_SPACE,
99+ [0x13] = KEY_ENTER,
100+ [0x14] = KEY_UP,
101+ [0x15] = KEY_DOWN,
102+ [0x16] = KEY_RIGHT,
103+ [0x17] = KEY_LEFT,
104+ [0x19] = KEY_PAGEDOWN,
105+ [0x1a] = KEY_PAGEUP,
106+ [0x1c] = KEY_POWER,
107+ [0x1d] = KEY_ESC,
108+ [0x1e] = KEY_SLEEP,
109+ [0x1f] = KEY_WAKEUP,
110+};
111+
112+static const unsigned int batt_charge[] = {0, 7, 20, 45, 65, 80, 100};
113+#define MAX_BAT_LEVEL 6
114+
115+static inline int n516_bat_charging(void)
116+{
117+ return !gpio_get_value(GPIO_CHARG_STAT_N);
118+}
119+
120+static int n516_bat_get_status(struct power_supply *b)
121+{
122+ if (power_supply_am_i_supplied(b)) {
123+ if (n516_bat_charging())
124+ return POWER_SUPPLY_STATUS_CHARGING;
125+ else
126+ return POWER_SUPPLY_STATUS_FULL;
127+ } else {
128+ return POWER_SUPPLY_STATUS_DISCHARGING;
129+ }
130+}
131+
132+static int n516_bat_get_charge(struct power_supply *b)
133+{
134+ return batt_charge[the_lpc->battery_level];
135+}
136+
137+static int n516_bat_get_property(struct power_supply *b,
138+ enum power_supply_property psp,
139+ union power_supply_propval *val)
140+{
141+ switch (psp) {
142+ case POWER_SUPPLY_PROP_STATUS:
143+ val->intval = n516_bat_get_status(b);
144+ break;
145+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
146+ val->intval = 100;
147+ break;
148+ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
149+ val->intval = 0;
150+ break;
151+ case POWER_SUPPLY_PROP_CHARGE_NOW:
152+ val->intval = n516_bat_get_charge(b);
153+ break;
154+ default:
155+ return -EINVAL;
156+ }
157+ return 0;
158+}
159+
160+static void n516_bat_power_changed(struct power_supply *p)
161+{
162+ if (power_supply_am_i_supplied(p) && !n516_bat_charging())
163+ the_lpc->battery_level = MAX_BAT_LEVEL;
164+
165+ power_supply_changed(p);
166+}
167+
168+static enum power_supply_property n516_bat_properties[] = {
169+ POWER_SUPPLY_PROP_STATUS,
170+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
171+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
172+ POWER_SUPPLY_PROP_CHARGE_NOW,
173+};
174+
175+static struct power_supply n516_battery = {
176+ .name = "n516-battery",
177+ .get_property = n516_bat_get_property,
178+ .properties = n516_bat_properties,
179+ .num_properties = ARRAY_SIZE(n516_bat_properties),
180+ .external_power_changed = n516_bat_power_changed,
181+};
182+
183+static irqreturn_t n516_bat_charge_irq(int irq, void *dev)
184+{
185+ struct power_supply *psy = dev;
186+
187+ dev_dbg(psy->dev, "Battery charging IRQ\n");
188+
189+ if (power_supply_am_i_supplied(psy) && !n516_bat_charging())
190+ the_lpc->battery_level = MAX_BAT_LEVEL;
191+
192+ power_supply_changed(psy);
193+
194+ return IRQ_HANDLED;
195+}
196+
197+static int n516_lpc_send_message(struct n516_lpc_chip *chip, unsigned char val)
198+{
199+ struct i2c_client *client = chip->i2c_client;
200+ struct i2c_msg msg = {client->addr, client->flags, 1, &val};
201+ int ret = 0;
202+
203+ ret = i2c_transfer(client->adapter, &msg, 1);
204+ return ret > 0 ? 0 : ret;
205+}
206+
207+static void n516_key_event(struct n516_lpc_chip *chip, unsigned char keycode)
208+{
209+ struct i2c_client *client = chip->i2c_client;
210+ bool long_press = false;
211+
212+ if (keycode & 0x40) {
213+ keycode &= ~0x40;
214+ long_press = true;
215+ }
216+
217+ dev_dbg(&client->dev, "keycode: 0x%02x, long_press: 0x%02x\n", keycode, (unsigned int)long_press);
218+
219+ if (keycode >= ARRAY_SIZE(n516_lpc_keymap) || n516_lpc_keymap[keycode] == 0)
220+ return;
221+
222+ if (long_press)
223+ input_report_key(chip->input, KEY_LEFTALT, 1);
224+
225+ input_report_key(chip->input, n516_lpc_keymap[keycode], 1);
226+ input_sync(chip->input);
227+ input_report_key(chip->input, n516_lpc_keymap[keycode], 0);
228+
229+ if (long_press)
230+ input_report_key(chip->input, KEY_LEFTALT, 0);
231+ input_sync(chip->input);
232+}
233+
234+static void n516_battery_event(struct n516_lpc_chip *chip, unsigned char battery_level)
235+{
236+ if (battery_level != chip->battery_level) {
237+ chip->battery_level = battery_level;
238+ power_supply_changed(&n516_battery);
239+ }
240+}
241+
242+static irqreturn_t n516_lpc_irq_thread(int irq, void *devid)
243+{
244+ struct n516_lpc_chip *chip = (struct n516_lpc_chip*)devid;
245+ int ret;
246+ unsigned char raw_msg;
247+ struct i2c_client *client = chip->i2c_client;
248+ struct i2c_msg msg = {client->addr, client->flags | I2C_M_RD, 1, &raw_msg};
249+
250+ if (client->dev.power.status >= DPM_OFF)
251+ return IRQ_HANDLED;
252+
253+ ret = i2c_transfer(client->adapter, &msg, 1);
254+ if (ret != 1) {
255+ dev_dbg(&client->dev, "I2C error: %d\n", ret);
256+ return IRQ_HANDLED;
257+ }
258+
259+ dev_dbg(&client->dev, "msg: 0x%02x\n", raw_msg);
260+
261+ /* Ack wakeup event */
262+ if ((raw_msg & ~0x40) < ARRAY_SIZE(n516_lpc_keymap))
263+ n516_key_event(chip, raw_msg);
264+ else if ((raw_msg >= 0x81) && (raw_msg <= 0x87))
265+ n516_battery_event(chip, raw_msg - 0x81);
266+ else if (raw_msg == 0x7e)
267+ n516_lpc_send_message(chip, 0x00);
268+ else
269+ dev_warn(&client->dev, "Unknown message: %x\n", raw_msg);
270+
271+ if (chip->suspending)
272+ chip->can_sleep = 0;
273+
274+ return IRQ_HANDLED;
275+}
276+
277+static void n516_lpc_power_off(void)
278+{
279+ struct i2c_client *client = the_lpc->i2c_client;
280+ unsigned char val = 0x01;
281+ struct i2c_msg msg = {client->addr, client->flags, 1, &val};
282+
283+ printk("Issue LPC POWEROFF command...\n");
284+ while (1)
285+ i2c_transfer(client->adapter, &msg, 1);
286+}
287+
288+static int n516_lpc_detect(struct i2c_client *client, struct i2c_board_info *info)
289+{
290+ return 0;
291+}
292+
293+static int n516_lpc_suspend_notifier(struct notifier_block *nb,
294+ unsigned long event,
295+ void *dummy)
296+{
297+ switch(event) {
298+ case PM_SUSPEND_PREPARE:
299+ the_lpc->suspending = 1;
300+ the_lpc->can_sleep = 1;
301+ break;
302+ case PM_POST_SUSPEND:
303+ the_lpc->suspending = 0;
304+ the_lpc->can_sleep = 1;
305+ break;
306+ default:
307+ return NOTIFY_DONE;
308+ }
309+ return NOTIFY_OK;
310+}
311+
312+static struct notifier_block n516_lpc_notif_block = {
313+ .notifier_call = n516_lpc_suspend_notifier,
314+};
315+
316+static int __devinit n516_lpc_probe(struct i2c_client *client, const struct i2c_device_id *id)
317+{
318+ struct n516_lpc_chip *chip;
319+ struct input_dev *input;
320+ int ret = 0;
321+ int i;
322+
323+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
324+ if (!chip)
325+ return -ENOMEM;
326+
327+ the_lpc = chip;
328+ chip->i2c_client = client;
329+ if ((batt_level > 0) && (batt_level < ARRAY_SIZE(batt_charge)))
330+ chip->battery_level = batt_level;
331+ else
332+ chip->battery_level = 1;
333+
334+ i2c_set_clientdata(client, chip);
335+
336+ ret = gpio_request(GPIO_LPC_INT, "LPC interrupt request");
337+ if (ret) {
338+ dev_err(&client->dev, "Unable to reguest LPC INT GPIO\n");
339+ goto err_gpio_req_lpcint;
340+ }
341+
342+ ret = gpio_request(GPIO_CHARG_STAT_N, "LPC charging status");
343+ if (ret) {
344+ dev_err(&client->dev, "Unable to reguest CHARG STAT GPIO\n");
345+ goto err_gpio_req_chargstat;
346+ }
347+
348+ /* Enter normal mode */
349+ n516_lpc_send_message(chip, 0x2);
350+
351+ input = input_allocate_device();
352+ if (!input) {
353+ dev_err(&client->dev, "Unable to allocate input device\n");
354+ ret = -ENOMEM;
355+ goto err_input_alloc;
356+ }
357+
358+ chip->input = input;
359+
360+ __set_bit(EV_KEY, input->evbit);
361+
362+ for (i = 0; i < ARRAY_SIZE(n516_lpc_keymap); i++)
363+ __set_bit(n516_lpc_keymap[i], input->keybit);
364+
365+ __set_bit(KEY_LEFTALT, input->keybit);
366+
367+ input->name = "n516-keys";
368+ input->phys = "n516-keys/input0";
369+ input->dev.parent = &client->dev;
370+ input->id.bustype = BUS_I2C;
371+ input->id.vendor = 0x0001;
372+ input->id.product = 0x0001;
373+ input->id.version = 0x0100;
374+
375+ ret = input_register_device(input);
376+ if (ret < 0) {
377+ dev_err(&client->dev, "Unable to register input device\n");
378+ goto err_input_register;
379+ }
380+
381+ ret = power_supply_register(NULL, &n516_battery);
382+ if (ret) {
383+ dev_err(&client->dev, "Unable to register N516 battery\n");
384+ goto err_bat_reg;
385+ }
386+
387+ ret = request_threaded_irq(gpio_to_irq(GPIO_LPC_INT), NULL,
388+ n516_lpc_irq_thread,
389+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
390+ "lpc", chip);
391+ if (ret) {
392+ dev_err(&client->dev, "request_irq failed: %d\n", ret);
393+ goto err_request_lpc_irq;
394+ }
395+
396+ ret = request_irq(gpio_to_irq(GPIO_CHARG_STAT_N), n516_bat_charge_irq,
397+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
398+ "battery charging", &n516_battery);
399+ if (ret) {
400+ dev_err(&client->dev, "Unable to claim battery charging IRQ\n");
401+ goto err_request_chrg_irq;
402+ }
403+
404+ pm_power_off = n516_lpc_power_off;
405+ ret = register_pm_notifier(&n516_lpc_notif_block);
406+ if (ret) {
407+ dev_err(&client->dev, "Unable to register PM notify block\n");
408+ goto err_reg_pm_notifier;
409+ }
410+
411+ device_init_wakeup(&client->dev, 1);
412+
413+ return 0;
414+
415+ unregister_pm_notifier(&n516_lpc_notif_block);
416+err_reg_pm_notifier:
417+ free_irq(gpio_to_irq(GPIO_CHARG_STAT_N), &n516_battery);
418+err_request_chrg_irq:
419+ free_irq(gpio_to_irq(GPIO_LPC_INT), chip);
420+err_request_lpc_irq:
421+ power_supply_unregister(&n516_battery);
422+err_bat_reg:
423+ input_unregister_device(input);
424+err_input_register:
425+ input_free_device(input);
426+err_input_alloc:
427+ gpio_free(GPIO_CHARG_STAT_N);
428+err_gpio_req_chargstat:
429+ gpio_free(GPIO_LPC_INT);
430+err_gpio_req_lpcint:
431+ i2c_set_clientdata(client, NULL);
432+ kfree(chip);
433+
434+ return ret;
435+}
436+
437+static int __devexit n516_lpc_remove(struct i2c_client *client)
438+{
439+ struct n516_lpc_chip *chip = i2c_get_clientdata(client);
440+
441+ unregister_pm_notifier(&n516_lpc_notif_block);
442+ pm_power_off = NULL;
443+ free_irq(gpio_to_irq(GPIO_CHARG_STAT_N), &n516_battery);
444+ free_irq(gpio_to_irq(GPIO_LPC_INT), chip);
445+ power_supply_unregister(&n516_battery);
446+ input_unregister_device(chip->input);
447+ gpio_free(GPIO_CHARG_STAT_N);
448+ gpio_free(GPIO_LPC_INT);
449+ i2c_set_clientdata(client, NULL);
450+ kfree(chip);
451+
452+ return 0;
453+}
454+
455+#if CONFIG_PM
456+static int n516_lpc_suspend(struct i2c_client *client, pm_message_t msg)
457+{
458+ if (!the_lpc->can_sleep)
459+ return -EBUSY;
460+
461+ if (device_may_wakeup(&client->dev))
462+ enable_irq_wake(gpio_to_irq(GPIO_LPC_INT));
463+
464+ return 0;
465+}
466+
467+static int n516_lpc_resume(struct i2c_client *client)
468+{
469+ if (device_may_wakeup(&client->dev))
470+ disable_irq_wake(gpio_to_irq(GPIO_LPC_INT));
471+
472+ return 0;
473+}
474+#else
475+#define n516_lpc_suspend NULL
476+#define n516_lpc_resume NULL
477+#endif
478+
479+
480+static struct i2c_driver n516_lpc_driver = {
481+ .class = I2C_CLASS_HWMON,
482+ .driver = {
483+ .name = "n516-keys",
484+ .owner = THIS_MODULE,
485+ },
486+ .probe = n516_lpc_probe,
487+ .remove = __devexit_p(n516_lpc_remove),
488+ .detect = n516_lpc_detect,
489+ .id_table = n516_lpc_i2c_ids,
490+ .address_list = normal_i2c,
491+ .suspend = n516_lpc_suspend,
492+ .resume = n516_lpc_resume,
493+};
494+
495+static int __init n516_lpc_init(void)
496+{
497+ return i2c_add_driver(&n516_lpc_driver);
498+}
499+module_init(n516_lpc_init);
500+
501+static void __exit n516_lpc_exit(void)
502+{
503+ i2c_del_driver(&n516_lpc_driver);
504+}
505+module_exit(n516_lpc_exit);
506+
507+MODULE_AUTHOR("Yauhen Kharuzhy");
508+MODULE_LICENSE("GPL");
509+MODULE_DESCRIPTION("Keys and power controller driver for N516");
510+MODULE_ALIAS("platform:n516-keys");
511

Archive Download this file



interactive