Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755683AbXKSV5X (ORCPT ); Mon, 19 Nov 2007 16:57:23 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1754897AbXKSV4Z (ORCPT ); Mon, 19 Nov 2007 16:56:25 -0500 Received: from v1539.ncsrv.de ([89.110.150.23]:57715 "EHLO v1539.ncsrv.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754830AbXKSV4W (ORCPT ); Mon, 19 Nov 2007 16:56:22 -0500 Message-Id: <20071119211913.744380921@hendrik-sattler.de> References: <20071119211318.046166499@hendrik-sattler.de> User-Agent: quilt/0.46-1 Date: Mon, 19 Nov 2007 22:13:19 +0100 From: post@hendrik-sattler.de To: linux-kernel@vger.kernel.org Cc: i2c@lm-sensors.org, Hendrik Sattler Subject: [PATCH 1/2] OZ99x I2C button and led support driver Content-Disposition: inline; filename=oz99x.patch X-Spam-Score: -3.5 (---) Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 21947 Lines: 785 This adds new-style I2c device driver for O2 Micro/ETC OZ990 devices. Button support was tested with a ETC OZ992S in a Fujitsu-Siemens C-6637. Signed-off-by: Hendrik Sattler --- This new-style I2C driver supports buttons and LEDs attached to an OZ990 or OZ992 device. Index: git-linville/drivers/i2c/chips/oz99x.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ git-linville/drivers/i2c/chips/oz99x.c 2007-11-18 14:16:46.804244460 +0100 @@ -0,0 +1,694 @@ +/* + oz99x.c - O2 Micro/ETC OZ990/OZ992 driver + + Copyright (C) 2006-2007 Hendrik Sattler + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +/* How often we poll keys - msecs */ +static unsigned int oz99x_poll = 1000; +module_param(oz99x_poll, uint, 0444); +MODULE_PARM_DESC(oz99x_poll, "poll interval in miliseconds [1000]"); + +/* enable reading registers via SysFS */ +static int oz99x_debug; +module_param(oz99x_debug, bool, 0444); +MODULE_PARM_DESC(oz99x_debug, "add debug features [0]"); + +struct oz99x_data { + struct i2c_client *client; + struct input_polled_dev *ipdev; + + unsigned int keymap[OZ99X_KEYMAP_ENTRIES]; + struct list_head leds; + + struct { + u8 min; + u8 max; + } range; +}; + +struct oz99x_led_data { + struct oz99x_data *data; + + int gpio; + char name[8]; + struct led_classdev cdev; + + struct list_head list; +}; +#define oz99x_from_led_cdev(c) container_of(c, struct oz99x_led_data, cdev) + +#define oz99x_reg_write(client, reg, value) \ + i2c_smbus_write_word_data(client, reg, value) +#define oz99x_reg_read(client, reg) i2c_smbus_read_word_data(client, reg) + +static +ssize_t reg_minmax_store(const char *buf, + size_t count, + u8 *minmax) +{ + long val = simple_strtol(buf, NULL, 16); + if (val > 0xFF) + val = 0xFF; + else if (val < 0x00) + val = 0x00; + *minmax = val & 0xFF; + return count; +} + +static +ssize_t reg_min_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct oz99x_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return snprintf(buf, PAGE_SIZE, "%02x\n", (int)data->range.min); +} + +static +ssize_t reg_min_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct oz99x_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return reg_minmax_store(buf, count, &data->range.min); +} + +static +ssize_t reg_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct oz99x_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return snprintf(buf, PAGE_SIZE, "%02x\n", (int)data->range.max); +} + +static +ssize_t reg_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct oz99x_data *data = i2c_get_clientdata(to_i2c_client(dev)); + return reg_minmax_store(buf, count, &data->range.max); +} + +static +ssize_t regs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct oz99x_data *data = i2c_get_clientdata(to_i2c_client(dev)); + u16 i = data->range.min; + u8 max = data->range.max; + size_t count = 0; + int reg; + + for (; i <= max; ++i) { + if (i) { + if ((i%8) == 0) + count += snprintf(buf+count, PAGE_SIZE-count, + "\n"); + else if ((i%4) == 0) + count += snprintf(buf+count, PAGE_SIZE-count, + " "); + else + count += snprintf(buf+count, PAGE_SIZE-count, + " "); + } + if ((i%8) == 0) + count += snprintf(buf+count, PAGE_SIZE-count, + "0x%02x: ", i); + + reg = oz99x_reg_read(data->client, i); + if (reg < 0) + count += snprintf(buf+count, PAGE_SIZE-count, "____"); + else + count += snprintf(buf+count, PAGE_SIZE-count, "%04x", + reg); + } + count += snprintf(buf+count, PAGE_SIZE-count, "\n"); + return count; +} + +static DEVICE_ATTR(regs_min, 0644, reg_min_show, reg_min_store); +static DEVICE_ATTR(regs_max, 0644, reg_max_show, reg_max_store); +static DEVICE_ATTR(regs, 0444, regs_show, NULL); + +#define OZ99X_GPC_IN 0 /* input */ +#define OZ99X_GPC_IN_DEB 1 /* debounced input (buttons) */ +#define OZ99X_GPC_OUT 2 /* output */ +#define OZ99X_GPC_ALF 3 /* auto led flash */ + +#define OZ99X_GPC_MASK 0x3 +#define OZ99X_GPC_SHIFT(gpio) (((gpio) % 8) << 1) +#define OZ99X_GPC(gpio, gpc) \ + (((gpc) >> OZ99X_GPC_SHIFT(gpio)) & OZ99X_GPC_MASK) + +#define OZ99X_REG_INPUT 0x00 +#define OZ99X_REG_OUTPUT 0x01 +#define OZ99X_REG_GPC_LOW 0x02 /* GPIO port control GPIO[0..7] */ +#define OZ99X_REG_GPC_HIGH 0x03 /* GPIO port control GPIO[8..15] */ +#define OZ99X_REG_SUSP_TRI 0x04 /* suspend tri-state */ +#define OZ99X_REG_INT_EN 0x05 /* interrupt trigger enable */ +#define OZ99X_REG_WAKE_EN 0x06 /* wakeup enable */ +#define OZ99X_REG_SUSP_EN 0x07 /* suspend enable GPIO[8..15] */ +#define OZ99X_REG_STATUS 0x08 /* interrupt/wakeup status */ +#define OZ99X_REG_SUSP_ST 0x09 /* suspend status */ +#define OZ99X_REG_SMICFG 0x0a /* SMI configuration */ +#define OZ99X_REG_SUSP_WAKE 0x0b /* suspend/wake */ +#define OZ99X_REG_PCC 0x0c /* power control */ +#define OZ99X_REG_WAKE_DIS 0x0d /* wakeup disable */ +#define OZ99X_REG_PWRON 0x0e /* power-on */ +#define OZ99X_REG_ALF 0x0f /* auto led flash */ +#define OZ99X_REG_ALF_DATA 0x10 /* ALF data */ +#define OZ99X_REG_ALF_FREQH 0x11 /* ALF frequency control GPIO[4..7] */ +#define OZ99X_REG_ALF_FREQL 0x12 /* ALF frequency control GPIO[0..3] */ +#define OZ99X_REG_ID 0x13 /* bus ID */ +#define OZ99X_REG_CHIPID 0x14 /* chip ID */ +/* OZ992 also has registers 0x15..0x1F */ + +/* for wakeup and interrupt trigger enable + * gpio defines the bit positions for each GPIO + * (only 8-15 are used) + */ +#define OZ99X_EN_RE(gpio) (((gpio) >> 8) & 0x00FF) /* rising edge */ +#define OZ99X_EN_FE(gpio) ((gpio) & 0xFF00) /* falling edge */ + +/* these return GPIOs as bit positions */ +#define OZ99X_STATUS_INT(status) (((status) & 0x00FF) << 8) +#define OZ99X_STATUS_WAKE(status) ((status) & 0xFF00) +#define OZ99X_STATUS_SUSP(status) (((status) & 0x00FF) << 8) + +/* special GPIO alternate functions: + * GPIO<0>: SMIEVENT + * GPIO<1>: WAKE output + * GPIO<2>: SMBALERT# + */ +#define OZ99X_SMICFG_WAKE_LEVEL (1 << 10) +#define OZ99X_SMICFG_SMI_LEVEL (1 << 8) +#define OZ99X_SMICFG_WAKE2PWR (1 << 7) +#define OZ99X_SMICFG_WAKE2WAKE (1 << 6) +#define OZ99X_SMICFG_WAKE2SMB (1 << 5) +#define OZ99X_SMICFG_WAKE2SMI (1 << 4) +#define OZ99X_SMICFG_INT2SMB (1 << 1) +#define OZ99X_SMICFG_INT2SMI (1 << 0) + +#define OZ99X_ALF_DISABLE (1 << 8) +#define OZ99X_ALF_DATA_A(n) (((n) & 0xFF) << 8) +#define OZ99X_ALF_DATA_B(n) ((n) & 0xFF) + +#define OZ99X_ID_SMBADDR(n) (((n) >> 9) & 0x007F) +#define OZ99X_ID_ID(n) ((n) & 0x00FF) +#define OZ99X_CHIPID_ID(n) (((n) >> 8) & 0x00FF) +#define OZ99X_CHIPID_REV(n) ((n) & 0x00FF) + +static +int oz99x_find_leds(struct i2c_client *client) +{ + int i; + int leds = 0; + int gpc = oz99x_reg_read(client, OZ99X_REG_GPC_LOW); + + if (gpc <= 0) + return gpc; + + for (i = OZ99X_LED_MIN; i <= OZ99X_LED_MAX; ++i) + if (OZ99X_GPC(i, gpc) == OZ99X_GPC_ALF) + leds |= (1 << i); + return leds; +} + +static +void oz99x_leds_on(struct i2c_client *client, int leds) +{ + int data; + + if (!leds) + return; + + data = oz99x_reg_read(client, OZ99X_REG_ALF_DATA); + /* blinks if A != B but we want A == B */ + data |= (OZ99X_ALF_DATA_A(leds) | OZ99X_ALF_DATA_B(leds)); + oz99x_reg_write(client, OZ99X_REG_ALF_DATA, data); + + data = oz99x_reg_read(client, OZ99X_REG_ALF); + data &= ~OZ99X_ALF_DISABLE; + oz99x_reg_write(client, OZ99X_REG_ALF, data); +} + +static +void oz99x_leds_off(struct i2c_client *client, int leds) +{ + int data; + + if (!leds) { + data = oz99x_reg_read(client, OZ99X_REG_ALF_DATA); + data &= ~(OZ99X_ALF_DATA_A(leds) | OZ99X_ALF_DATA_B(leds)); + oz99x_reg_write(client, OZ99X_REG_ALF_DATA, data); + } + + data = oz99x_reg_read(client, OZ99X_REG_ALF); + if (!(data & OZ99X_ALF_DISABLE)) { + data |= OZ99X_ALF_DISABLE; + oz99x_reg_write(client, OZ99X_REG_ALF, data); + } +} + +static +void oz99x_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct oz99x_led_data *ldata = oz99x_from_led_cdev(led_cdev); + if (brightness) + oz99x_leds_on(ldata->data->client, ldata->gpio); + else + oz99x_leds_off(ldata->data->client, ldata->gpio); +} + +static +void oz99x_configure_leds(struct oz99x_data *data, int leds) +{ + int i = OZ99X_LED_MIN; + for (; i <= OZ99X_LED_MAX; ++i) + if (leds & (1 << i)) { + struct oz99x_led_data *ldata = kzalloc(sizeof(*ldata), + GFP_KERNEL); + if (ldata) { + ldata->data = data; + ldata->gpio = i; + snprintf(ldata->name, sizeof(ldata->name), + "oz99x:%d", i); + ldata->cdev.name = ldata->name; + ldata->cdev.brightness_set = + oz99x_led_brightness_set; + list_add(&ldata->list, &data->leds); + led_classdev_register(&data->client->dev, + &ldata->cdev); + } + } +} + +static +int oz99x_find_buttons(struct i2c_client *client) +{ + int i; + int buttons = 0; + int gpc = oz99x_reg_read(client, OZ99X_REG_GPC_HIGH); + + if (gpc <= 0) + return gpc; + + for (i = OZ99X_BUTTON_MIN; i <= OZ99X_BUTTON_MAX; ++i) + if (OZ99X_GPC(i, gpc) == OZ99X_GPC_IN_DEB) + buttons |= (1 << i); + return buttons; +} + +static +int oz99x_configure_buttons(struct i2c_client *client, int buttons) +{ + int status; + u16 wake_en = OZ99X_EN_FE(buttons); + u16 susp_en = OZ99X_EN_RE(buttons); + u16 int_en = susp_en; + u16 smicfg = 0; + +#if 0 /* not sure how this could be used */ + smicfg |= (OZ99X_SMICFG_WAKE2SMI | OZ99X_SMICFG_INT2SMI); +#endif +#if 0 /* I2C framework doesn't support SMB-ALERT */ + /* route all events to SMBALERTs */ + smicfg |= (OZ99X_SMICFG_WAKE2SMB | OZ99X_SMICFG_INT2SMB); +#endif + + /* suspend on rising edge: button up event */ + status = oz99x_reg_write(client, OZ99X_REG_SUSP_EN, susp_en); + if (status < 0) + return status; + status = oz99x_reg_read(client, OZ99X_REG_SUSP_EN); + if (status < 0) + return status; + if (status != susp_en) { + if (oz99x_debug) + dev_err(&client->dev, + "SUSP_EN register is write-protected\n"); + susp_en = 0; + } + + /* wakeup on falling edge: button down event */ + status = oz99x_reg_write(client, OZ99X_REG_WAKE_EN, wake_en); + if (status < 0) + return status; + status = oz99x_reg_read(client, OZ99X_REG_WAKE_EN); + if (status < 0) + return status; + if (status != wake_en) { + if (oz99x_debug) + dev_err(&client->dev, + "WAKE_EN register is write-protected\n"); + smicfg &= ~(OZ99X_SMICFG_WAKE2SMI | OZ99X_SMICFG_WAKE2SMB); + wake_en = 0; + } + + status = oz99x_reg_write(client, OZ99X_REG_INT_EN, int_en); + if (status < 0) + return status; + status = oz99x_reg_read(client, OZ99X_REG_INT_EN); + if (status < 0) + return status; + if (status != int_en) { + if (oz99x_debug) + dev_err(&client->dev, + "INT_EN register is write-protected\n"); + smicfg &= ~(OZ99X_SMICFG_INT2SMI | OZ99X_SMICFG_INT2SMB); + } + + status = oz99x_reg_write(client, OZ99X_REG_SMICFG, smicfg); + if (status < 0) { + dev_err(&client->dev, + "Writing to SMI configuration register failed\n"); + return status; + } + status = oz99x_reg_read(client, OZ99X_REG_SMICFG); + if (status < 0) + return status; + if (status != smicfg) + dev_err(&client->dev, + "SMI configuration register is write-protected\n"); + + return 0; +} + +static +int oz99x_get_button_states(struct i2c_client *client, + int *pressed, + int *released) +{ + int status = oz99x_reg_read(client, OZ99X_REG_STATUS); + if (status < 0) + return status; + if (pressed) + *pressed = OZ99X_STATUS_WAKE(status); + if (released) + *released = OZ99X_STATUS_INT(status); + status = oz99x_reg_read(client, OZ99X_REG_SUSP_ST); + if (released) + *released |= OZ99X_STATUS_SUSP(status); + return 0; +} + +static +void oz99x_buttons_report(struct input_polled_dev *ipdev) +{ + struct oz99x_data *data = ipdev->private; + int i = 0; + int pressed = 0; + int released = 0; + + if (oz99x_get_button_states(data->client, &pressed, &released)) + return; + for (; i < ipdev->input->keycodemax; ++i) { + if ((pressed | released) & (1 << (i+8))) { + input_report_key(ipdev->input, data->keymap[i], 1); + input_sync(ipdev->input); + } + if (released & (1 << (i+8))) { + input_report_key(ipdev->input, data->keymap[i], 0); + input_sync(ipdev->input); + } + } +} + +static +int oz99x_buttons_keycode_get(struct input_dev *idev, int scancode, + int *keycode) +{ + struct oz99x_data *data = idev->private; + if (scancode < 0 || scancode >= ARRAY_SIZE(data->keymap)) + return -EINVAL; + + *keycode = data->keymap[scancode]; + return 0; +} + +static +int oz99x_buttons_keycode_set(struct input_dev *idev, int scancode, int keycode) +{ + struct oz99x_data *data = idev->private; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + if (scancode < 0 || scancode >= ARRAY_SIZE(data->keymap)) + return -EINVAL; + + clear_bit(data->keymap[scancode], idev->keybit); + data->keymap[scancode] = keycode; + set_bit(keycode, idev->keybit); + return 0; +} + +static +int oz99x_buttons_poll_setup(struct oz99x_data *data, int buttons) +{ + static unsigned int keycodes[sizeof(data->keymap)] = { + KEY_PROG1, KEY_PROG2, KEY_PROG3, KEY_PROG4, + KEY_MAIL, KEY_WWW, KEY_CALC, KEY_MSDOS + }; + struct input_polled_dev *ipdev; + struct input_dev *input; + int i = 0; + int n = 0; + struct oz99x_platform_data *pdata = data->client->dev.platform_data; + + if (buttons == 0) + return -ENODEV; + + buttons >>= 8; + ipdev = input_allocate_polled_device(); + if (!ipdev) + return -ENOMEM; + + ipdev->poll = oz99x_buttons_report; + ipdev->poll_interval = oz99x_poll; + ipdev->private = data; + + input = ipdev->input; + input->name = "oz99x buttons"; + input->phys = "oz99x/input0"; + input->private = data; + input->id.bustype = BUS_HOST; + input->dev.parent = &data->client->dev; + input->getkeycode = oz99x_buttons_keycode_get; + input->setkeycode = oz99x_buttons_keycode_set; + input->keycodemax = fls(buttons); + + if (!pdata) { + for (i = 0; i < input->keycodemax; ++i) + if (buttons & (1 << i)) + data->keymap[i] = keycodes[n++]; + } else { + for (i = 0; i < input->keycodemax; ++i) + if (buttons & (1 << i)) + data->keymap[i] = pdata->keymap[n++]; + } + + set_bit(EV_KEY, input->evbit); + for (i = 0; i < input->keycodemax; ++i) + if ((buttons & (1 << i)) && data->keymap[i]) + set_bit(data->keymap[i], input->keybit); + + i = input_register_polled_device(ipdev); + if (!i) + data->ipdev = ipdev; + return i; +} + +static +int oz99x_i2c_addr_check(struct i2c_client *client) +{ + int status = oz99x_reg_read(client, OZ99X_REG_ID); + + if (status < 0) + return status; + return (OZ99X_ID_SMBADDR(status) == client->addr); +} + +static +int oz99x_check(struct i2c_client *client) +{ + int id; + int rev; + int status = oz99x_i2c_addr_check(client); + + if (status <= 0) + return status; + + status = oz99x_reg_read(client, OZ99X_REG_CHIPID); + if (status < 0) + return status; + + id = OZ99X_CHIPID_ID(status); + rev = OZ99X_CHIPID_REV(status); + switch (id) { + case 0x90: + case 0x92: + dev_info(&client->dev, + "found OZ9%02X (revision %d) @ I2C address 0x%02x\n", + id, rev, client->addr); + return id; + + default: + return 0; + } +} + +static +int __devinit oz99x_probe(struct i2c_client *client) +{ + struct oz99x_data *data; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int id; + int buttons; + int leds; + int status = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EIO; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + INIT_LIST_HEAD(&data->leds); + + id = oz99x_check(client); + if (!id) { + status = -ENODEV; + goto out; + } + + buttons = oz99x_find_buttons(client); + if (!buttons) { + dev_info(&client->dev, "no buttons configured.\n"); + } else { + status = oz99x_configure_buttons(client, buttons); + if (!status) + status = oz99x_buttons_poll_setup(data, buttons); + if (status) + goto out; + } + + leds = oz99x_find_leds(client); + if (!leds) + dev_info(&client->dev, "no LEDs configured.\n"); + else + oz99x_configure_leds(data, leds); + + switch (id) { + case 0x90: + data->range.max = 0x14; + break; + + case 0x92: + data->range.max = 0x1F; + break; + } + if (oz99x_debug) { + device_create_file(&client->dev, &dev_attr_regs_max); + device_create_file(&client->dev, &dev_attr_regs_min); + device_create_file(&client->dev, &dev_attr_regs); + } + + i2c_set_clientdata(client, data); + return 0; + +out: + kfree(data); + data = NULL; + return status; +} + +static +int __devexit oz99x_remove(struct i2c_client *client) +{ + struct list_head *pos; + struct oz99x_data *data = i2c_get_clientdata(client); + + if (data->ipdev) + input_unregister_polled_device(data->ipdev); + + if (oz99x_debug) { + device_remove_file(&client->dev, &dev_attr_regs); + device_remove_file(&client->dev, &dev_attr_regs_min); + device_remove_file(&client->dev, &dev_attr_regs_max); + } + + list_for_each(pos, &data->leds) { + struct oz99x_led_data *ldata = + list_entry(pos, struct oz99x_led_data, list); + led_classdev_unregister(&ldata->cdev); + kfree(ldata); + } + + kfree(data); + return 0; +} + +static +struct i2c_driver oz99x_driver = { + .driver = { + .name = "oz99x", + }, + .probe = oz99x_probe, + .remove = __devexit_p(oz99x_remove), +}; + +static +int __init oz99x_module_init(void) +{ + return i2c_add_driver(&oz99x_driver); +} + +static +void __exit oz99x_module_exit(void) +{ + i2c_del_driver(&oz99x_driver); +} + +module_init(oz99x_module_init); +module_exit(oz99x_module_exit); + +MODULE_DESCRIPTION("O2 Micro OZ99x SMBus devices"); +MODULE_AUTHOR("Hendrik Sattler"); +MODULE_LICENSE("GPL"); Index: git-linville/include/linux/oz99x.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ git-linville/include/linux/oz99x.h 2007-11-18 10:34:43.592253651 +0100 @@ -0,0 +1,31 @@ +/* + oz99x.h - O2 Micro/ETC OZ990/OZ992 driver + + Copyright (C) 2006-2007 Hendrik Sattler + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#define OZ99X_BUTTON_MAX 15 +#define OZ99X_BUTTON_MIN 8 +#define OZ99X_KEYMAP_ENTRIES (OZ99X_BUTTON_MAX-OZ99X_BUTTON_MIN+1) + +#define OZ99X_LED_MAX 7 +#define OZ99X_LED_MIN 0 +#define OZ99X_LED_COUNT (OZ99X_LED_MAX-OZ99X_LED_MIN+1) + +struct oz99x_platform_data { + int keymap[OZ99X_KEYMAP_ENTRIES]; +}; Index: git-linville/drivers/i2c/chips/Kconfig =================================================================== --- git-linville.orig/drivers/i2c/chips/Kconfig 2007-11-16 23:19:59.776639899 +0100 +++ git-linville/drivers/i2c/chips/Kconfig 2007-11-16 23:51:19.496446659 +0100 @@ -163,4 +163,17 @@ and other features that are often used in portable devices like cell phones and PDAs. +config OZ99X + tristate "O2 Micro/ETC OZ990/OZ992 SMBus chip" + depends on I2C + select INPUT_POLLDEV + select LEDS_CLASS + help + If you say Y here, you get support for the OZ990 and OZ992 chip + from O2 Micro. This driver provides support for buttons and + LEDs according to the preconfigured GPIO setup. + + This driver can also be built as a module. If so, the module + will be called oz99x. + endmenu Index: git-linville/drivers/i2c/chips/Makefile =================================================================== --- git-linville.orig/drivers/i2c/chips/Makefile 2007-11-16 23:19:59.866636902 +0100 +++ git-linville/drivers/i2c/chips/Makefile 2007-11-16 23:48:30.023118159 +0100 @@ -15,6 +15,7 @@ obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o +obj-$(CONFIG_OZ99X) += oz99x.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG -- - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/