diff options
Diffstat (limited to 'target/linux/xburst/patches-2.6.37/800-n516-lpc.patch')
-rw-r--r-- | target/linux/xburst/patches-2.6.37/800-n516-lpc.patch | 510 |
1 files changed, 0 insertions, 510 deletions
diff --git a/target/linux/xburst/patches-2.6.37/800-n516-lpc.patch b/target/linux/xburst/patches-2.6.37/800-n516-lpc.patch deleted file mode 100644 index 9f908eb87c..0000000000 --- a/target/linux/xburst/patches-2.6.37/800-n516-lpc.patch +++ /dev/null @@ -1,510 +0,0 @@ -From 9d67bbbef07b0568d0541f79e3cd664d71d2d96c Mon Sep 17 00:00:00 2001 -From: Lars-Peter Clausen <lars@metafoo.de> -Date: Wed, 12 May 2010 14:22:36 +0200 -Subject: [PATCH 4/5] Add n516 lpc driver - ---- - drivers/misc/Kconfig | 8 + - drivers/misc/Makefile | 1 + - drivers/misc/n516-lpc.c | 471 +++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 480 insertions(+), 0 deletions(-) - create mode 100644 drivers/misc/n516-lpc.c - ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -452,6 +452,14 @@ config PCH_PHUB - To compile this driver as a module, choose M here: the module will - be called pch_phub. - -+config N516_LPC -+ tristate "N516 keys & power controller" -+ depends on I2C -+ depends on INPUT -+ depends on POWER_SUPPLY -+ help -+ N516 keyboard & power controller driver -+ - source "drivers/misc/c2port/Kconfig" - source "drivers/misc/eeprom/Kconfig" - source "drivers/misc/cb710/Kconfig" ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -42,3 +42,4 @@ obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd - obj-$(CONFIG_PCH_PHUB) += pch_phub.o - obj-y += ti-st/ - obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o -+obj-$(CONFIG_N516_LPC) += n516-lpc.o ---- /dev/null -+++ b/drivers/misc/n516-lpc.c -@@ -0,0 +1,471 @@ -+#include <linux/module.h> -+#include <linux/version.h> -+#include <linux/init.h> -+#include <linux/fs.h> -+#include <linux/interrupt.h> -+#include <linux/irq.h> -+#include <linux/sched.h> -+#include <linux/pm.h> -+#include <linux/sysctl.h> -+#include <linux/proc_fs.h> -+#include <linux/delay.h> -+#include <linux/platform_device.h> -+#include <linux/input.h> -+#include <linux/power_supply.h> -+#include <linux/suspend.h> -+ -+#include <linux/i2c.h> -+ -+#include <asm/mach-jz4740/irq.h> -+#include <asm/mach-jz4740/gpio.h> -+#include <asm/mach-jz4740/board-n516.h> -+ -+static int batt_level=0; -+module_param(batt_level, int, 0); -+ -+struct n516_lpc_chip { -+ struct i2c_client *i2c_client; -+ struct input_dev *input; -+ unsigned int battery_level; -+ unsigned int suspending:1, can_sleep:1; -+}; -+ -+static struct n516_lpc_chip *the_lpc; -+ -+struct i2c_device_id n516_lpc_i2c_ids[] = { -+ {"LPC524", 0}, -+ {}, -+}; -+ -+MODULE_DEVICE_TABLE(i2c, n516_lpc_i2c_ids); -+ -+static const unsigned short normal_i2c[] = I2C_ADDRS(0x54); -+ -+static const unsigned int n516_lpc_keymap[] = { -+ [0x01] = KEY_4, -+ [0x02] = KEY_3, -+ [0x03] = KEY_2, -+ [0x04] = KEY_1, -+ [0x05] = KEY_0, -+ [0x07] = KEY_9, -+ [0x08] = KEY_8, -+ [0x09] = KEY_7, -+ [0x0a] = KEY_6, -+ [0x0b] = KEY_5, -+ [0x0d] = KEY_PLAYPAUSE, -+ [0x0e] = KEY_MENU, -+ [0x0f] = KEY_SEARCH, -+ [0x10] = KEY_DIRECTION, -+ [0x11] = KEY_SPACE, -+ [0x13] = KEY_ENTER, -+ [0x14] = KEY_UP, -+ [0x15] = KEY_DOWN, -+ [0x16] = KEY_RIGHT, -+ [0x17] = KEY_LEFT, -+ [0x19] = KEY_PAGEDOWN, -+ [0x1a] = KEY_PAGEUP, -+ [0x1c] = KEY_POWER, -+ [0x1d] = KEY_ESC, -+ [0x1e] = KEY_SLEEP, -+ [0x1f] = KEY_WAKEUP, -+}; -+ -+static const unsigned int batt_charge[] = {0, 7, 20, 45, 65, 80, 100}; -+#define MAX_BAT_LEVEL 6 -+ -+static inline int n516_bat_charging(void) -+{ -+ return !gpio_get_value(GPIO_CHARG_STAT_N); -+} -+ -+static int n516_bat_get_status(struct power_supply *b) -+{ -+ if (power_supply_am_i_supplied(b)) { -+ if (n516_bat_charging()) -+ return POWER_SUPPLY_STATUS_CHARGING; -+ else -+ return POWER_SUPPLY_STATUS_FULL; -+ } else { -+ return POWER_SUPPLY_STATUS_DISCHARGING; -+ } -+} -+ -+static int n516_bat_get_charge(struct power_supply *b) -+{ -+ return batt_charge[the_lpc->battery_level]; -+} -+ -+static int n516_bat_get_property(struct power_supply *b, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ val->intval = n516_bat_get_status(b); -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: -+ val->intval = 100; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: -+ val->intval = 0; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_NOW: -+ val->intval = n516_bat_get_charge(b); -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static void n516_bat_power_changed(struct power_supply *p) -+{ -+ if (power_supply_am_i_supplied(p) && !n516_bat_charging()) -+ the_lpc->battery_level = MAX_BAT_LEVEL; -+ -+ power_supply_changed(p); -+} -+ -+static enum power_supply_property n516_bat_properties[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_NOW, -+}; -+ -+static struct power_supply n516_battery = { -+ .name = "n516-battery", -+ .get_property = n516_bat_get_property, -+ .properties = n516_bat_properties, -+ .num_properties = ARRAY_SIZE(n516_bat_properties), -+ .external_power_changed = n516_bat_power_changed, -+}; -+ -+static irqreturn_t n516_bat_charge_irq(int irq, void *dev) -+{ -+ struct power_supply *psy = dev; -+ -+ dev_dbg(psy->dev, "Battery charging IRQ\n"); -+ -+ if (power_supply_am_i_supplied(psy) && !n516_bat_charging()) -+ the_lpc->battery_level = MAX_BAT_LEVEL; -+ -+ power_supply_changed(psy); -+ -+ return IRQ_HANDLED; -+} -+ -+static int n516_lpc_send_message(struct n516_lpc_chip *chip, unsigned char val) -+{ -+ struct i2c_client *client = chip->i2c_client; -+ struct i2c_msg msg = {client->addr, client->flags, 1, &val}; -+ int ret = 0; -+ -+ ret = i2c_transfer(client->adapter, &msg, 1); -+ return ret > 0 ? 0 : ret; -+} -+ -+static void n516_key_event(struct n516_lpc_chip *chip, unsigned char keycode) -+{ -+ struct i2c_client *client = chip->i2c_client; -+ bool long_press = false; -+ -+ if (keycode & 0x40) { -+ keycode &= ~0x40; -+ long_press = true; -+ } -+ -+ dev_dbg(&client->dev, "keycode: 0x%02x, long_press: 0x%02x\n", keycode, (unsigned int)long_press); -+ -+ if (keycode >= ARRAY_SIZE(n516_lpc_keymap) || n516_lpc_keymap[keycode] == 0) -+ return; -+ -+ if (long_press) -+ input_report_key(chip->input, KEY_LEFTALT, 1); -+ -+ input_report_key(chip->input, n516_lpc_keymap[keycode], 1); -+ input_sync(chip->input); -+ input_report_key(chip->input, n516_lpc_keymap[keycode], 0); -+ -+ if (long_press) -+ input_report_key(chip->input, KEY_LEFTALT, 0); -+ input_sync(chip->input); -+} -+ -+static void n516_battery_event(struct n516_lpc_chip *chip, unsigned char battery_level) -+{ -+ if (battery_level != chip->battery_level) { -+ chip->battery_level = battery_level; -+ power_supply_changed(&n516_battery); -+ } -+} -+ -+static irqreturn_t n516_lpc_irq_thread(int irq, void *devid) -+{ -+ struct n516_lpc_chip *chip = (struct n516_lpc_chip*)devid; -+ int ret; -+ unsigned char raw_msg; -+ struct i2c_client *client = chip->i2c_client; -+ struct i2c_msg msg = {client->addr, client->flags | I2C_M_RD, 1, &raw_msg}; -+ -+ if (client->dev.power.status >= DPM_OFF) -+ return IRQ_HANDLED; -+ -+ ret = i2c_transfer(client->adapter, &msg, 1); -+ if (ret != 1) { -+ dev_dbg(&client->dev, "I2C error: %d\n", ret); -+ return IRQ_HANDLED; -+ } -+ -+ dev_dbg(&client->dev, "msg: 0x%02x\n", raw_msg); -+ -+ /* Ack wakeup event */ -+ if ((raw_msg & ~0x40) < ARRAY_SIZE(n516_lpc_keymap)) -+ n516_key_event(chip, raw_msg); -+ else if ((raw_msg >= 0x81) && (raw_msg <= 0x87)) -+ n516_battery_event(chip, raw_msg - 0x81); -+ else if (raw_msg == 0x7e) -+ n516_lpc_send_message(chip, 0x00); -+ else -+ dev_warn(&client->dev, "Unknown message: %x\n", raw_msg); -+ -+ if (chip->suspending) -+ chip->can_sleep = 0; -+ -+ return IRQ_HANDLED; -+} -+ -+static void n516_lpc_power_off(void) -+{ -+ struct i2c_client *client = the_lpc->i2c_client; -+ unsigned char val = 0x01; -+ struct i2c_msg msg = {client->addr, client->flags, 1, &val}; -+ -+ printk("Issue LPC POWEROFF command...\n"); -+ while (1) -+ i2c_transfer(client->adapter, &msg, 1); -+} -+ -+static int n516_lpc_detect(struct i2c_client *client, struct i2c_board_info *info) -+{ -+ return 0; -+} -+ -+static int n516_lpc_suspend_notifier(struct notifier_block *nb, -+ unsigned long event, -+ void *dummy) -+{ -+ switch(event) { -+ case PM_SUSPEND_PREPARE: -+ the_lpc->suspending = 1; -+ the_lpc->can_sleep = 1; -+ break; -+ case PM_POST_SUSPEND: -+ the_lpc->suspending = 0; -+ the_lpc->can_sleep = 1; -+ break; -+ default: -+ return NOTIFY_DONE; -+ } -+ return NOTIFY_OK; -+} -+ -+static struct notifier_block n516_lpc_notif_block = { -+ .notifier_call = n516_lpc_suspend_notifier, -+}; -+ -+static int __devinit n516_lpc_probe(struct i2c_client *client, const struct i2c_device_id *id) -+{ -+ struct n516_lpc_chip *chip; -+ struct input_dev *input; -+ int ret = 0; -+ int i; -+ -+ chip = kzalloc(sizeof(*chip), GFP_KERNEL); -+ if (!chip) -+ return -ENOMEM; -+ -+ the_lpc = chip; -+ chip->i2c_client = client; -+ if ((batt_level > 0) && (batt_level < ARRAY_SIZE(batt_charge))) -+ chip->battery_level = batt_level; -+ else -+ chip->battery_level = 1; -+ -+ i2c_set_clientdata(client, chip); -+ -+ ret = gpio_request(GPIO_LPC_INT, "LPC interrupt request"); -+ if (ret) { -+ dev_err(&client->dev, "Unable to reguest LPC INT GPIO\n"); -+ goto err_gpio_req_lpcint; -+ } -+ -+ ret = gpio_request(GPIO_CHARG_STAT_N, "LPC charging status"); -+ if (ret) { -+ dev_err(&client->dev, "Unable to reguest CHARG STAT GPIO\n"); -+ goto err_gpio_req_chargstat; -+ } -+ -+ /* Enter normal mode */ -+ n516_lpc_send_message(chip, 0x2); -+ -+ input = input_allocate_device(); -+ if (!input) { -+ dev_err(&client->dev, "Unable to allocate input device\n"); -+ ret = -ENOMEM; -+ goto err_input_alloc; -+ } -+ -+ chip->input = input; -+ -+ __set_bit(EV_KEY, input->evbit); -+ -+ for (i = 0; i < ARRAY_SIZE(n516_lpc_keymap); i++) -+ __set_bit(n516_lpc_keymap[i], input->keybit); -+ -+ __set_bit(KEY_LEFTALT, input->keybit); -+ -+ input->name = "n516-keys"; -+ input->phys = "n516-keys/input0"; -+ input->dev.parent = &client->dev; -+ input->id.bustype = BUS_I2C; -+ input->id.vendor = 0x0001; -+ input->id.product = 0x0001; -+ input->id.version = 0x0100; -+ -+ ret = input_register_device(input); -+ if (ret < 0) { -+ dev_err(&client->dev, "Unable to register input device\n"); -+ goto err_input_register; -+ } -+ -+ ret = power_supply_register(NULL, &n516_battery); -+ if (ret) { -+ dev_err(&client->dev, "Unable to register N516 battery\n"); -+ goto err_bat_reg; -+ } -+ -+ ret = request_threaded_irq(gpio_to_irq(GPIO_LPC_INT), NULL, -+ n516_lpc_irq_thread, -+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, -+ "lpc", chip); -+ if (ret) { -+ dev_err(&client->dev, "request_irq failed: %d\n", ret); -+ goto err_request_lpc_irq; -+ } -+ -+ ret = request_irq(gpio_to_irq(GPIO_CHARG_STAT_N), n516_bat_charge_irq, -+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, -+ "battery charging", &n516_battery); -+ if (ret) { -+ dev_err(&client->dev, "Unable to claim battery charging IRQ\n"); -+ goto err_request_chrg_irq; -+ } -+ -+ pm_power_off = n516_lpc_power_off; -+ ret = register_pm_notifier(&n516_lpc_notif_block); -+ if (ret) { -+ dev_err(&client->dev, "Unable to register PM notify block\n"); -+ goto err_reg_pm_notifier; -+ } -+ -+ device_init_wakeup(&client->dev, 1); -+ -+ return 0; -+ -+ unregister_pm_notifier(&n516_lpc_notif_block); -+err_reg_pm_notifier: -+ free_irq(gpio_to_irq(GPIO_CHARG_STAT_N), &n516_battery); -+err_request_chrg_irq: -+ free_irq(gpio_to_irq(GPIO_LPC_INT), chip); -+err_request_lpc_irq: -+ power_supply_unregister(&n516_battery); -+err_bat_reg: -+ input_unregister_device(input); -+err_input_register: -+ input_free_device(input); -+err_input_alloc: -+ gpio_free(GPIO_CHARG_STAT_N); -+err_gpio_req_chargstat: -+ gpio_free(GPIO_LPC_INT); -+err_gpio_req_lpcint: -+ i2c_set_clientdata(client, NULL); -+ kfree(chip); -+ -+ return ret; -+} -+ -+static int __devexit n516_lpc_remove(struct i2c_client *client) -+{ -+ struct n516_lpc_chip *chip = i2c_get_clientdata(client); -+ -+ unregister_pm_notifier(&n516_lpc_notif_block); -+ pm_power_off = NULL; -+ free_irq(gpio_to_irq(GPIO_CHARG_STAT_N), &n516_battery); -+ free_irq(gpio_to_irq(GPIO_LPC_INT), chip); -+ power_supply_unregister(&n516_battery); -+ input_unregister_device(chip->input); -+ gpio_free(GPIO_CHARG_STAT_N); -+ gpio_free(GPIO_LPC_INT); -+ i2c_set_clientdata(client, NULL); -+ kfree(chip); -+ -+ return 0; -+} -+ -+#if CONFIG_PM -+static int n516_lpc_suspend(struct i2c_client *client, pm_message_t msg) -+{ -+ if (!the_lpc->can_sleep) -+ return -EBUSY; -+ -+ if (device_may_wakeup(&client->dev)) -+ enable_irq_wake(gpio_to_irq(GPIO_LPC_INT)); -+ -+ return 0; -+} -+ -+static int n516_lpc_resume(struct i2c_client *client) -+{ -+ if (device_may_wakeup(&client->dev)) -+ disable_irq_wake(gpio_to_irq(GPIO_LPC_INT)); -+ -+ return 0; -+} -+#else -+#define n516_lpc_suspend NULL -+#define n516_lpc_resume NULL -+#endif -+ -+ -+static struct i2c_driver n516_lpc_driver = { -+ .class = I2C_CLASS_HWMON, -+ .driver = { -+ .name = "n516-keys", -+ .owner = THIS_MODULE, -+ }, -+ .probe = n516_lpc_probe, -+ .remove = __devexit_p(n516_lpc_remove), -+ .detect = n516_lpc_detect, -+ .id_table = n516_lpc_i2c_ids, -+ .address_list = normal_i2c, -+ .suspend = n516_lpc_suspend, -+ .resume = n516_lpc_resume, -+}; -+ -+static int __init n516_lpc_init(void) -+{ -+ return i2c_add_driver(&n516_lpc_driver); -+} -+module_init(n516_lpc_init); -+ -+static void __exit n516_lpc_exit(void) -+{ -+ i2c_del_driver(&n516_lpc_driver); -+} -+module_exit(n516_lpc_exit); -+ -+MODULE_AUTHOR("Yauhen Kharuzhy"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Keys and power controller driver for N516"); -+MODULE_ALIAS("platform:n516-keys"); |