[PATCH 1/2] OZ99x I2C button and led support driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <[email protected]>
---
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 <[email protected]>
+
+    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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/leds.h>
+#include <linux/list.h>
+#include <asm/bitops.h>
+
+#include <linux/oz99x.h>
+
+/* 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 <[email protected]>
+
+    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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux