summaryrefslogtreecommitdiffstats
path: root/drivers/i2c/chips
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/chips')
-rw-r--r--drivers/i2c/chips/Kconfig188
-rw-r--r--drivers/i2c/chips/Makefile30
-rw-r--r--drivers/i2c/chips/at24.c582
-rw-r--r--drivers/i2c/chips/ds1682.c267
-rw-r--r--drivers/i2c/chips/eeprom.c257
-rw-r--r--drivers/i2c/chips/isp1301_omap.c1683
-rw-r--r--drivers/i2c/chips/max6875.c246
-rw-r--r--drivers/i2c/chips/mcu_mpc8349emitx.c209
-rw-r--r--drivers/i2c/chips/menelaus.c1285
-rw-r--r--drivers/i2c/chips/pca9539.c152
-rw-r--r--drivers/i2c/chips/pcf8574.c215
-rw-r--r--drivers/i2c/chips/pcf8575.c198
-rw-r--r--drivers/i2c/chips/pcf8591.c325
-rw-r--r--drivers/i2c/chips/tps65010.c1072
-rw-r--r--drivers/i2c/chips/tsl2550.c489
15 files changed, 7198 insertions, 0 deletions
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
new file mode 100644
index 0000000..4c35702
--- /dev/null
+++ b/drivers/i2c/chips/Kconfig
@@ -0,0 +1,188 @@
+#
+# Miscellaneous I2C chip drivers configuration
+#
+# *** DEPRECATED! Do not add new entries! See Makefile ***
+#
+
+menu "Miscellaneous I2C Chip support"
+
+config DS1682
+ tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm"
+ depends on EXPERIMENTAL
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS1682 Total Elapsed Time Recorder.
+
+ This driver can also be built as a module. If so, the module
+ will be called ds1682.
+
+config AT24
+ tristate "EEPROMs from most vendors"
+ depends on SYSFS && EXPERIMENTAL
+ help
+ Enable this driver to get read/write support to most I2C EEPROMs,
+ after you configure the driver to know about each EEPROM on
+ your target board. Use these generic chip names, instead of
+ vendor-specific ones like at24c64 or 24lc02:
+
+ 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08,
+ 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024
+
+ Unless you like data loss puzzles, always be sure that any chip
+ you configure as a 24c32 (32 kbit) or larger is NOT really a
+ 24c16 (16 kbit) or smaller, and vice versa. Marking the chip
+ as read-only won't help recover from this. Also, if your chip
+ has any software write-protect mechanism you may want to review the
+ code to make sure this driver won't turn it on by accident.
+
+ If you use this with an SMBus adapter instead of an I2C adapter,
+ full functionality is not available. Only smaller devices are
+ supported (24c16 and below, max 4 kByte).
+
+ This driver can also be built as a module. If so, the module
+ will be called at24.
+
+config SENSORS_EEPROM
+ tristate "EEPROM reader"
+ depends on EXPERIMENTAL
+ help
+ If you say yes here you get read-only access to the EEPROM data
+ available on modern memory DIMMs and Sony Vaio laptops. Such
+ EEPROMs could theoretically be available on other devices as well.
+
+ This driver can also be built as a module. If so, the module
+ will be called eeprom.
+
+config SENSORS_PCF8574
+ tristate "Philips PCF8574 and PCF8574A (DEPRECATED)"
+ depends on EXPERIMENTAL && GPIO_PCF857X = "n"
+ default n
+ help
+ If you say yes here you get support for Philips PCF8574 and
+ PCF8574A chips. These chips are 8-bit I/O expanders for the I2C bus.
+
+ This driver can also be built as a module. If so, the module
+ will be called pcf8574.
+
+ This driver is deprecated and will be dropped soon. Use
+ drivers/gpio/pcf857x.c instead.
+
+ These devices are hard to detect and rarely found on mainstream
+ hardware. If unsure, say N.
+
+config PCF8575
+ tristate "Philips PCF8575 (DEPRECATED)"
+ default n
+ depends on GPIO_PCF857X = "n"
+ help
+ If you say yes here you get support for Philips PCF8575 chip.
+ This chip is a 16-bit I/O expander for the I2C bus. Several other
+ chip manufacturers sell equivalent chips, e.g. Texas Instruments.
+
+ This driver can also be built as a module. If so, the module
+ will be called pcf8575.
+
+ This driver is deprecated and will be dropped soon. Use
+ drivers/gpio/pcf857x.c instead.
+
+ This device is hard to detect and is rarely found on mainstream
+ hardware. If unsure, say N.
+
+config SENSORS_PCA9539
+ tristate "Philips PCA9539 16-bit I/O port (DEPRECATED)"
+ depends on EXPERIMENTAL && GPIO_PCA953X = "n"
+ help
+ If you say yes here you get support for the Philips PCA9539
+ 16-bit I/O port.
+
+ This driver can also be built as a module. If so, the module
+ will be called pca9539.
+
+ This driver is deprecated and will be dropped soon. Use
+ drivers/gpio/pca953x.c instead.
+
+config SENSORS_PCF8591
+ tristate "Philips PCF8591"
+ depends on EXPERIMENTAL
+ default n
+ help
+ If you say yes here you get support for Philips PCF8591 chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called pcf8591.
+
+ These devices are hard to detect and rarely found on mainstream
+ hardware. If unsure, say N.
+
+config ISP1301_OMAP
+ tristate "Philips ISP1301 with OMAP OTG"
+ depends on ARCH_OMAP_OTG
+ help
+ If you say yes here you get support for the Philips ISP1301
+ USB-On-The-Go transceiver working with the OMAP OTG controller.
+ The ISP1301 is used in products including H2 and H3 development
+ boards for Texas Instruments OMAP processors.
+
+ This driver can also be built as a module. If so, the module
+ will be called isp1301_omap.
+
+config TPS65010
+ tristate "TPS6501x Power Management chips"
+ depends on GPIOLIB
+ default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
+ help
+ If you say yes here you get support for the TPS6501x series of
+ Power Management chips. These include voltage regulators,
+ lithium ion/polymer battery charging, and other features that
+ are often used in portable devices like cell phones and cameras.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps65010.
+
+config SENSORS_MAX6875
+ tristate "Maxim MAX6875 Power supply supervisor"
+ depends on EXPERIMENTAL
+ help
+ If you say yes here you get support for the Maxim MAX6875
+ EEPROM-programmable, quad power-supply sequencer/supervisor.
+
+ This provides an interface to program the EEPROM and reset the chip.
+
+ This driver also supports the Maxim MAX6874 hex power-supply
+ sequencer/supervisor if found at a compatible address.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6875.
+
+config SENSORS_TSL2550
+ tristate "Taos TSL2550 ambient light sensor"
+ depends on EXPERIMENTAL
+ help
+ If you say yes here you get support for the Taos TSL2550
+ ambient light sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called tsl2550.
+
+config MENELAUS
+ bool "TWL92330/Menelaus PM chip"
+ depends on I2C=y && ARCH_OMAP24XX
+ help
+ If you say yes here you get support for the Texas Instruments
+ TWL92330/Menelaus Power Management chip. This include voltage
+ regulators, Dual slot memory card tranceivers, real-time clock
+ and other features that are often used in portable devices like
+ cell phones and PDAs.
+
+config MCU_MPC8349EMITX
+ tristate "MPC8349E-mITX MCU driver"
+ depends on I2C && PPC_83xx
+ select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
+ help
+ Say Y here to enable soft power-off functionality on the Freescale
+ boards with the MPC8349E-mITX-compatible MCU chips. This driver will
+ also register MCU GPIOs with the generic GPIO API, so you'll able
+ to use MCU pins as GPIOs.
+
+endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
new file mode 100644
index 0000000..23d2a31
--- /dev/null
+++ b/drivers/i2c/chips/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for miscellaneous I2C chip drivers.
+#
+# Do not add new drivers to this directory! It is DEPRECATED.
+#
+# Device drivers are better grouped according to the functionality they
+# implement rather than to the bus they are connected to. In particular:
+# * Hardware monitoring chip drivers go to drivers/hwmon
+# * RTC chip drivers go to drivers/rtc
+# * I/O expander drivers go to drivers/gpio
+#
+
+obj-$(CONFIG_DS1682) += ds1682.o
+obj-$(CONFIG_AT24) += at24.o
+obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
+obj-$(CONFIG_SENSORS_MAX6875) += max6875.o
+obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
+obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
+obj-$(CONFIG_PCF8575) += pcf8575.o
+obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
+obj-$(CONFIG_TPS65010) += tps65010.o
+obj-$(CONFIG_MENELAUS) += menelaus.o
+obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
+obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o
+
+ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/drivers/i2c/chips/at24.c b/drivers/i2c/chips/at24.c
new file mode 100644
index 0000000..d477552
--- /dev/null
+++ b/drivers/i2c/chips/at24.c
@@ -0,0 +1,582 @@
+/*
+ * at24.c - handle most I2C EEPROMs
+ *
+ * Copyright (C) 2005-2007 David Brownell
+ * Copyright (C) 2008 Wolfram Sang, Pengutronix
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/mod_devicetable.h>
+#include <linux/log2.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/i2c/at24.h>
+
+/*
+ * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
+ * Differences between different vendor product lines (like Atmel AT24C or
+ * MicroChip 24LC, etc) won't much matter for typical read/write access.
+ * There are also I2C RAM chips, likewise interchangeable. One example
+ * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
+ *
+ * However, misconfiguration can lose data. "Set 16-bit memory address"
+ * to a part with 8-bit addressing will overwrite data. Writing with too
+ * big a page size also loses data. And it's not safe to assume that the
+ * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
+ * uses 0x51, for just one example.
+ *
+ * Accordingly, explicit board-specific configuration data should be used
+ * in almost all cases. (One partial exception is an SMBus used to access
+ * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
+ *
+ * So this driver uses "new style" I2C driver binding, expecting to be
+ * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
+ * similar kernel-resident tables; or, configuration data coming from
+ * a bootloader.
+ *
+ * Other than binding model, current differences from "eeprom" driver are
+ * that this one handles write access and isn't restricted to 24c02 devices.
+ * It also handles larger devices (32 kbit and up) with two-byte addresses,
+ * which won't work on pure SMBus systems.
+ */
+
+struct at24_data {
+ struct at24_platform_data chip;
+ bool use_smbus;
+
+ /*
+ * Lock protects against activities from other Linux tasks,
+ * but not from changes by other I2C masters.
+ */
+ struct mutex lock;
+ struct bin_attribute bin;
+
+ u8 *writebuf;
+ unsigned write_max;
+ unsigned num_addresses;
+
+ /*
+ * Some chips tie up multiple I2C addresses; dummy devices reserve
+ * them for us, and we'll use them with SMBus calls.
+ */
+ struct i2c_client *client[];
+};
+
+/*
+ * This parameter is to help this driver avoid blocking other drivers out
+ * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
+ * clock, one 256 byte read takes about 1/43 second which is excessive;
+ * but the 1/170 second it takes at 400 kHz may be quite reasonable; and
+ * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
+ *
+ * This value is forced to be a power of two so that writes align on pages.
+ */
+static unsigned io_limit = 128;
+module_param(io_limit, uint, 0);
+MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)");
+
+/*
+ * Specs often allow 5 msec for a page write, sometimes 20 msec;
+ * it's important to recover from write timeouts.
+ */
+static unsigned write_timeout = 25;
+module_param(write_timeout, uint, 0);
+MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
+
+#define AT24_SIZE_BYTELEN 5
+#define AT24_SIZE_FLAGS 8
+
+#define AT24_BITMASK(x) (BIT(x) - 1)
+
+/* create non-zero magic value for given eeprom parameters */
+#define AT24_DEVICE_MAGIC(_len, _flags) \
+ ((1 << AT24_SIZE_FLAGS | (_flags)) \
+ << AT24_SIZE_BYTELEN | ilog2(_len))
+
+static const struct i2c_device_id at24_ids[] = {
+ /* needs 8 addresses as A0-A2 are ignored */
+ { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
+ /* old variants can't be handled with this generic entry! */
+ { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
+ { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
+ /* spd is a 24c02 in memory DIMMs */
+ { "spd", AT24_DEVICE_MAGIC(2048 / 8,
+ AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
+ { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
+ /* 24rf08 quirk is handled at i2c-core */
+ { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
+ { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
+ { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
+ { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
+ { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
+ { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
+ { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
+ { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
+ { "at24", 0 },
+ { /* END OF LIST */ }
+};
+MODULE_DEVICE_TABLE(i2c, at24_ids);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * This routine supports chips which consume multiple I2C addresses. It
+ * computes the addressing information to be used for a given r/w request.
+ * Assumes that sanity checks for offset happened at sysfs-layer.
+ */
+static struct i2c_client *at24_translate_offset(struct at24_data *at24,
+ unsigned *offset)
+{
+ unsigned i;
+
+ if (at24->chip.flags & AT24_FLAG_ADDR16) {
+ i = *offset >> 16;
+ *offset &= 0xffff;
+ } else {
+ i = *offset >> 8;
+ *offset &= 0xff;
+ }
+
+ return at24->client[i];
+}
+
+static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
+ unsigned offset, size_t count)
+{
+ struct i2c_msg msg[2];
+ u8 msgbuf[2];
+ struct i2c_client *client;
+ int status, i;
+
+ memset(msg, 0, sizeof(msg));
+
+ /*
+ * REVISIT some multi-address chips don't rollover page reads to
+ * the next slave address, so we may need to truncate the count.
+ * Those chips might need another quirk flag.
+ *
+ * If the real hardware used four adjacent 24c02 chips and that
+ * were misconfigured as one 24c08, that would be a similar effect:
+ * one "eeprom" file not four, but larger reads would fail when
+ * they crossed certain pages.
+ */
+
+ /*
+ * Slave address and byte offset derive from the offset. Always
+ * set the byte address; on a multi-master board, another master
+ * may have changed the chip's "current" address pointer.
+ */
+ client = at24_translate_offset(at24, &offset);
+
+ if (count > io_limit)
+ count = io_limit;
+
+ /* Smaller eeproms can work given some SMBus extension calls */
+ if (at24->use_smbus) {
+ if (count > I2C_SMBUS_BLOCK_MAX)
+ count = I2C_SMBUS_BLOCK_MAX;
+ status = i2c_smbus_read_i2c_block_data(client, offset,
+ count, buf);
+ dev_dbg(&client->dev, "smbus read %zu@%d --> %d\n",
+ count, offset, status);
+ return (status < 0) ? -EIO : status;
+ }
+
+ /*
+ * When we have a better choice than SMBus calls, use a combined
+ * I2C message. Write address; then read up to io_limit data bytes.
+ * Note that read page rollover helps us here (unlike writes).
+ * msgbuf is u8 and will cast to our needs.
+ */
+ i = 0;
+ if (at24->chip.flags & AT24_FLAG_ADDR16)
+ msgbuf[i++] = offset >> 8;
+ msgbuf[i++] = offset;
+
+ msg[0].addr = client->addr;
+ msg[0].buf = msgbuf;
+ msg[0].len = i;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = buf;
+ msg[1].len = count;
+
+ status = i2c_transfer(client->adapter, msg, 2);
+ dev_dbg(&client->dev, "i2c read %zu@%d --> %d\n",
+ count, offset, status);
+
+ if (status == 2)
+ return count;
+ else if (status >= 0)
+ return -EIO;
+ else
+ return status;
+}
+
+static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct at24_data *at24;
+ ssize_t retval = 0;
+
+ at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
+
+ if (unlikely(!count))
+ return count;
+
+ /*
+ * Read data from chip, protecting against concurrent updates
+ * from this host, but not from other I2C masters.
+ */
+ mutex_lock(&at24->lock);
+
+ while (count) {
+ ssize_t status;
+
+ status = at24_eeprom_read(at24, buf, off, count);
+ if (status <= 0) {
+ if (retval == 0)
+ retval = status;
+ break;
+ }
+ buf += status;
+ off += status;
+ count -= status;
+ retval += status;
+ }
+
+ mutex_unlock(&at24->lock);
+
+ return retval;
+}
+
+
+/*
+ * REVISIT: export at24_bin{read,write}() to let other kernel code use
+ * eeprom data. For example, it might hold a board's Ethernet address, or
+ * board-specific calibration data generated on the manufacturing floor.
+ */
+
+
+/*
+ * Note that if the hardware write-protect pin is pulled high, the whole
+ * chip is normally write protected. But there are plenty of product
+ * variants here, including OTP fuses and partial chip protect.
+ *
+ * We only use page mode writes; the alternative is sloooow. This routine
+ * writes at most one page.
+ */
+static ssize_t at24_eeprom_write(struct at24_data *at24, char *buf,
+ unsigned offset, size_t count)
+{
+ struct i2c_client *client;
+ struct i2c_msg msg;
+ ssize_t status;
+ unsigned long timeout, write_time;
+ unsigned next_page;
+
+ /* Get corresponding I2C address and adjust offset */
+ client = at24_translate_offset(at24, &offset);
+
+ /* write_max is at most a page */
+ if (count > at24->write_max)
+ count = at24->write_max;
+
+ /* Never roll over backwards, to the start of this page */
+ next_page = roundup(offset + 1, at24->chip.page_size);
+ if (offset + count > next_page)
+ count = next_page - offset;
+
+ /* If we'll use I2C calls for I/O, set up the message */
+ if (!at24->use_smbus) {
+ int i = 0;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+
+ /* msg.buf is u8 and casts will mask the values */
+ msg.buf = at24->writebuf;
+ if (at24->chip.flags & AT24_FLAG_ADDR16)
+ msg.buf[i++] = offset >> 8;
+
+ msg.buf[i++] = offset;
+ memcpy(&msg.buf[i], buf, count);
+ msg.len = i + count;
+ }
+
+ /*
+ * Writes fail if the previous one didn't complete yet. We may
+ * loop a few times until this one succeeds, waiting at least
+ * long enough for one entire page write to work.
+ */
+ timeout = jiffies + msecs_to_jiffies(write_timeout);
+ do {
+ write_time = jiffies;
+ if (at24->use_smbus) {
+ status = i2c_smbus_write_i2c_block_data(client,
+ offset, count, buf);
+ if (status == 0)
+ status = count;
+ } else {
+ status = i2c_transfer(client->adapter, &msg, 1);
+ if (status == 1)
+ status = count;
+ }
+ dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
+ count, offset, status, jiffies);
+
+ if (status == count)
+ return count;
+
+ /* REVISIT: at HZ=100, this is sloooow */
+ msleep(1);
+ } while (time_before(write_time, timeout));
+
+ return -ETIMEDOUT;
+}
+
+static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct at24_data *at24;
+ ssize_t retval = 0;
+
+ at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
+
+ if (unlikely(!count))
+ return count;
+
+ /*
+ * Write data to chip, protecting against concurrent updates
+ * from this host, but not from other I2C masters.
+ */
+ mutex_lock(&at24->lock);
+
+ while (count) {
+ ssize_t status;
+
+ status = at24_eeprom_write(at24, buf, off, count);
+ if (status <= 0) {
+ if (retval == 0)
+ retval = status;
+ break;
+ }
+ buf += status;
+ off += status;
+ count -= status;
+ retval += status;
+ }
+
+ mutex_unlock(&at24->lock);
+
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct at24_platform_data chip;
+ bool writable;
+ bool use_smbus = false;
+ struct at24_data *at24;
+ int err;
+ unsigned i, num_addresses;
+ kernel_ulong_t magic;
+
+ if (client->dev.platform_data) {
+ chip = *(struct at24_platform_data *)client->dev.platform_data;
+ } else {
+ if (!id->driver_data) {
+ err = -ENODEV;
+ goto err_out;
+ }
+ magic = id->driver_data;
+ chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
+ magic >>= AT24_SIZE_BYTELEN;
+ chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
+ /*
+ * This is slow, but we can't know all eeproms, so we better
+ * play safe. Specifying custom eeprom-types via platform_data
+ * is recommended anyhow.
+ */
+ chip.page_size = 1;
+ }
+
+ if (!is_power_of_2(chip.byte_len))
+ dev_warn(&client->dev,
+ "byte_len looks suspicious (no power of 2)!\n");
+ if (!is_power_of_2(chip.page_size))
+ dev_warn(&client->dev,
+ "page_size looks suspicious (no power of 2)!\n");
+
+ /* Use I2C operations unless we're stuck with SMBus extensions. */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ if (chip.flags & AT24_FLAG_ADDR16) {
+ err = -EPFNOSUPPORT;
+ goto err_out;
+ }
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ err = -EPFNOSUPPORT;
+ goto err_out;
+ }
+ use_smbus = true;
+ }
+
+ if (chip.flags & AT24_FLAG_TAKE8ADDR)
+ num_addresses = 8;
+ else
+ num_addresses = DIV_ROUND_UP(chip.byte_len,
+ (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
+
+ at24 = kzalloc(sizeof(struct at24_data) +
+ num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
+ if (!at24) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ mutex_init(&at24->lock);
+ at24->use_smbus = use_smbus;
+ at24->chip = chip;
+ at24->num_addresses = num_addresses;
+
+ /*
+ * Export the EEPROM bytes through sysfs, since that's convenient.
+ * By default, only root should see the data (maybe passwords etc)
+ */
+ at24->bin.attr.name = "eeprom";
+ at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
+ at24->bin.read = at24_bin_read;
+ at24->bin.size = chip.byte_len;
+
+ writable = !(chip.flags & AT24_FLAG_READONLY);
+ if (writable) {
+ if (!use_smbus || i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
+
+ unsigned write_max = chip.page_size;
+
+ at24->bin.write = at24_bin_write;
+ at24->bin.attr.mode |= S_IWUSR;
+
+ if (write_max > io_limit)
+ write_max = io_limit;
+ if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
+ write_max = I2C_SMBUS_BLOCK_MAX;
+ at24->write_max = write_max;
+
+ /* buffer (data + address at the beginning) */
+ at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
+ if (!at24->writebuf) {
+ err = -ENOMEM;
+ goto err_struct;
+ }
+ } else {
+ dev_warn(&client->dev,
+ "cannot write due to controller restrictions.");
+ }
+ }
+
+ at24->client[0] = client;
+
+ /* use dummy devices for multiple-address chips */
+ for (i = 1; i < num_addresses; i++) {
+ at24->client[i] = i2c_new_dummy(client->adapter,
+ client->addr + i);
+ if (!at24->client[i]) {
+ dev_err(&client->dev, "address 0x%02x unavailable\n",
+ client->addr + i);
+ err = -EADDRINUSE;
+ goto err_clients;
+ }
+ }
+
+ err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
+ if (err)
+ goto err_clients;
+
+ i2c_set_clientdata(client, at24);
+
+ dev_info(&client->dev, "%zu byte %s EEPROM %s\n",
+ at24->bin.size, client->name,
+ writable ? "(writable)" : "(read-only)");
+ dev_dbg(&client->dev,
+ "page_size %d, num_addresses %d, write_max %d%s\n",
+ chip.page_size, num_addresses,
+ at24->write_max,
+ use_smbus ? ", use_smbus" : "");
+
+ return 0;
+
+err_clients:
+ for (i = 1; i < num_addresses; i++)
+ if (at24->client[i])
+ i2c_unregister_device(at24->client[i]);
+
+ kfree(at24->writebuf);
+err_struct:
+ kfree(at24);
+err_out:
+ dev_dbg(&client->dev, "probe error %d\n", err);
+ return err;
+}
+
+static int __devexit at24_remove(struct i2c_client *client)
+{
+ struct at24_data *at24;
+ int i;
+
+ at24 = i2c_get_clientdata(client);
+ sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);
+
+ for (i = 1; i < at24->num_addresses; i++)
+ i2c_unregister_device(at24->client[i]);
+
+ kfree(at24->writebuf);
+ kfree(at24);
+ i2c_set_clientdata(client, NULL);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct i2c_driver at24_driver = {
+ .driver = {
+ .name = "at24",
+ .owner = THIS_MODULE,
+ },
+ .probe = at24_probe,
+ .remove = __devexit_p(at24_remove),
+ .id_table = at24_ids,
+};
+
+static int __init at24_init(void)
+{
+ io_limit = rounddown_pow_of_two(io_limit);
+ return i2c_add_driver(&at24_driver);
+}
+module_init(at24_init);
+
+static void __exit at24_exit(void)
+{
+ i2c_del_driver(&at24_driver);
+}
+module_exit(at24_exit);
+
+MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
+MODULE_AUTHOR("David Brownell and Wolfram Sang");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/chips/ds1682.c b/drivers/i2c/chips/ds1682.c
new file mode 100644
index 0000000..f3ee4a1
--- /dev/null
+++ b/drivers/i2c/chips/ds1682.c
@@ -0,0 +1,267 @@
+/*
+ * Dallas Semiconductor DS1682 Elapsed Time Recorder device driver
+ *
+ * Written by: Grant Likely <grant.likely@secretlab.ca>
+ *
+ * Copyright (C) 2007 Secret Lab Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * The DS1682 elapsed timer recorder is a simple device that implements
+ * one elapsed time counter, one event counter, an alarm signal and 10
+ * bytes of general purpose EEPROM.
+ *
+ * This driver provides access to the DS1682 counters and user data via
+ * the sysfs. The following attributes are added to the device node:
+ * elapsed_time (u32): Total elapsed event time in ms resolution
+ * alarm_time (u32): When elapsed time exceeds the value in alarm_time,
+ * then the alarm pin is asserted.
+ * event_count (u16): number of times the event pin has gone low.
+ * eeprom (u8[10]): general purpose EEPROM
+ *
+ * Counter registers and user data are both read/write unless the device
+ * has been write protected. This driver does not support turning off write
+ * protection. Once write protection is turned on, it is impossible to
+ * turn it off again, so I have left the feature out of this driver to avoid
+ * accidental enabling, but it is trivial to add write protect support.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <linux/hwmon-sysfs.h>
+
+/* Device registers */
+#define DS1682_REG_CONFIG 0x00
+#define DS1682_REG_ALARM 0x01
+#define DS1682_REG_ELAPSED 0x05
+#define DS1682_REG_EVT_CNTR 0x09
+#define DS1682_REG_EEPROM 0x0b
+#define DS1682_REG_RESET 0x1d
+#define DS1682_REG_WRITE_DISABLE 0x1e
+#define DS1682_REG_WRITE_MEM_DISABLE 0x1f
+
+#define DS1682_EEPROM_SIZE 10
+
+/*
+ * Generic counter attributes
+ */
+static ssize_t ds1682_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ __le32 val = 0;
+ int rc;
+
+ dev_dbg(dev, "ds1682_show() called on %s\n", attr->attr.name);
+
+ /* Read the register */
+ rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr,
+ (u8 *) & val);
+ if (rc < 0)
+ return -EIO;
+
+ /* Special case: the 32 bit regs are time values with 1/4s
+ * resolution, scale them up to milliseconds */
+ if (sattr->nr == 4)
+ return sprintf(buf, "%llu\n",
+ ((unsigned long long)le32_to_cpu(val)) * 250);
+
+ /* Format the output string and return # of bytes */
+ return sprintf(buf, "%li\n", (long)le32_to_cpu(val));
+}
+
+static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ char *endp;
+ u64 val;
+ __le32 val_le;
+ int rc;
+
+ dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name);
+
+ /* Decode input */
+ val = simple_strtoull(buf, &endp, 0);
+ if (buf == endp) {
+ dev_dbg(dev, "input string not a number\n");
+ return -EINVAL;
+ }
+
+ /* Special case: the 32 bit regs are time values with 1/4s
+ * resolution, scale input down to quarter-seconds */
+ if (sattr->nr == 4)
+ do_div(val, 250);
+
+ /* write out the value */
+ val_le = cpu_to_le32(val);
+ rc = i2c_smbus_write_i2c_block_data(client, sattr->index, sattr->nr,
+ (u8 *) & val_le);
+ if (rc < 0) {
+ dev_err(dev, "register write failed; reg=0x%x, size=%i\n",
+ sattr->index, sattr->nr);
+ return -EIO;
+ }
+
+ return count;
+}
+
+/*
+ * Simple register attributes
+ */
+static SENSOR_DEVICE_ATTR_2(elapsed_time, S_IRUGO | S_IWUSR, ds1682_show,
+ ds1682_store, 4, DS1682_REG_ELAPSED);
+static SENSOR_DEVICE_ATTR_2(alarm_time, S_IRUGO | S_IWUSR, ds1682_show,
+ ds1682_store, 4, DS1682_REG_ALARM);
+static SENSOR_DEVICE_ATTR_2(event_count, S_IRUGO | S_IWUSR, ds1682_show,
+ ds1682_store, 2, DS1682_REG_EVT_CNTR);
+
+static const struct attribute_group ds1682_group = {
+ .attrs = (struct attribute *[]) {
+ &sensor_dev_attr_elapsed_time.dev_attr.attr,
+ &sensor_dev_attr_alarm_time.dev_attr.attr,
+ &sensor_dev_attr_event_count.dev_attr.attr,
+ NULL,
+ },
+};
+
+/*
+ * User data attribute
+ */
+static ssize_t ds1682_eeprom_read(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct i2c_client *client = kobj_to_i2c_client(kobj);
+ int rc;
+
+ dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)\n",
+ buf, off, count);
+
+ if (off >= DS1682_EEPROM_SIZE)
+ return 0;
+
+ if (off + count > DS1682_EEPROM_SIZE)
+ count = DS1682_EEPROM_SIZE - off;
+
+ rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off,
+ count, buf);
+ if (rc < 0)
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t ds1682_eeprom_write(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct i2c_client *client = kobj_to_i2c_client(kobj);
+
+ dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)\n",
+ buf, off, count);
+
+ if (off >= DS1682_EEPROM_SIZE)
+ return -ENOSPC;
+
+ if (off + count > DS1682_EEPROM_SIZE)
+ count = DS1682_EEPROM_SIZE - off;
+
+ /* Write out to the device */
+ if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off,
+ count, buf) < 0)
+ return -EIO;
+
+ return count;
+}
+
+static struct bin_attribute ds1682_eeprom_attr = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = DS1682_EEPROM_SIZE,
+ .read = ds1682_eeprom_read,
+ .write = ds1682_eeprom_write,
+};
+
+/*
+ * Called when a ds1682 device is matched with this driver
+ */
+static int ds1682_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ dev_err(&client->dev, "i2c bus does not support the ds1682\n");
+ rc = -ENODEV;
+ goto exit;
+ }
+
+ rc = sysfs_create_group(&client->dev.kobj, &ds1682_group);
+ if (rc)
+ goto exit;
+
+ rc = sysfs_create_bin_file(&client->dev.kobj, &ds1682_eeprom_attr);
+ if (rc)
+ goto exit_bin_attr;
+
+ return 0;
+
+ exit_bin_attr:
+ sysfs_remove_group(&client->dev.kobj, &ds1682_group);
+ exit:
+ return rc;
+}
+
+static int ds1682_remove(struct i2c_client *client)
+{
+ sysfs_remove_bin_file(&client->dev.kobj, &ds1682_eeprom_attr);
+ sysfs_remove_group(&client->dev.kobj, &ds1682_group);
+ return 0;
+}
+
+static const struct i2c_device_id ds1682_id[] = {
+ { "ds1682", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds1682_id);
+
+static struct i2c_driver ds1682_driver = {
+ .driver = {
+ .name = "ds1682",
+ },
+ .probe = ds1682_probe,
+ .remove = ds1682_remove,
+ .id_table = ds1682_id,
+};
+
+static int __init ds1682_init(void)
+{
+ return i2c_add_driver(&ds1682_driver);
+}
+
+static void __exit ds1682_exit(void)
+{
+ i2c_del_driver(&ds1682_driver);
+}
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("DS1682 Elapsed Time Indicator driver");
+MODULE_LICENSE("GPL");
+
+module_init(ds1682_init);
+module_exit(ds1682_exit);
diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c
new file mode 100644
index 0000000..2c27193
--- /dev/null
+++ b/drivers/i2c/chips/eeprom.c
@@ -0,0 +1,257 @@
+/*
+ Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
+ Philip Edelbrock <phil@netroedge.com>
+ Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
+ Copyright (C) 2003 IBM Corp.
+ Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54,
+ 0x55, 0x56, 0x57, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_1(eeprom);
+
+
+/* Size of EEPROM in bytes */
+#define EEPROM_SIZE 256
+
+/* possible types of eeprom devices */
+enum eeprom_nature {
+ UNKNOWN,
+ VAIO,
+};
+
+/* Each client has this additional data */
+struct eeprom_data {
+ struct mutex update_lock;
+ u8 valid; /* bitfield, bit!=0 if slice is valid */
+ unsigned long last_updated[8]; /* In jiffies, 8 slices */
+ u8 data[EEPROM_SIZE]; /* Register values */
+ enum eeprom_nature nature;
+};
+
+
+static void eeprom_update_client(struct i2c_client *client, u8 slice)
+{
+ struct eeprom_data *data = i2c_get_clientdata(client);
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (!(data->valid & (1 << slice)) ||
+ time_after(jiffies, data->last_updated[slice] + 300 * HZ)) {
+ dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ for (i = slice << 5; i < (slice + 1) << 5; i += 32)
+ if (i2c_smbus_read_i2c_block_data(client, i,
+ 32, data->data + i)
+ != 32)
+ goto exit;
+ } else {
+ for (i = slice << 5; i < (slice + 1) << 5; i += 2) {
+ int word = i2c_smbus_read_word_data(client, i);
+ if (word < 0)
+ goto exit;
+ data->data[i] = word & 0xff;
+ data->data[i + 1] = word >> 8;
+ }
+ }
+ data->last_updated[slice] = jiffies;
+ data->valid |= (1 << slice);
+ }
+exit:
+ mutex_unlock(&data->update_lock);
+}
+
+static ssize_t eeprom_read(struct kobject *kobj, struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
+ struct eeprom_data *data = i2c_get_clientdata(client);
+ u8 slice;
+
+ if (off > EEPROM_SIZE)
+ return 0;
+ if (off + count > EEPROM_SIZE)
+ count = EEPROM_SIZE - off;
+
+ /* Only refresh slices which contain requested bytes */
+ for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++)
+ eeprom_update_client(client, slice);
+
+ /* Hide Vaio private settings to regular users:
+ - BIOS passwords: bytes 0x00 to 0x0f
+ - UUID: bytes 0x10 to 0x1f
+ - Serial number: 0xc0 to 0xdf */
+ if (data->nature == VAIO && !capable(CAP_SYS_ADMIN)) {
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if ((off + i <= 0x1f) ||
+ (off + i >= 0xc0 && off + i <= 0xdf))
+ buf[i] = 0;
+ else
+ buf[i] = data->data[off + i];
+ }
+ } else {
+ memcpy(buf, &data->data[off], count);
+ }
+
+ return count;
+}
+
+static struct bin_attribute eeprom_attr = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IRUGO,
+ },
+ .size = EEPROM_SIZE,
+ .read = eeprom_read,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int eeprom_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all
+ addresses 0x50-0x57, but we only care about 0x50. So decline
+ attaching to addresses >= 0x51 on DDC buses */
+ if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x51)
+ return -ENODEV;
+
+ /* There are four ways we can read the EEPROM data:
+ (1) I2C block reads (faster, but unsupported by most adapters)
+ (2) Word reads (128% overhead)
+ (3) Consecutive byte reads (88% overhead, unsafe)
+ (4) Regular byte data reads (265% overhead)
+ The third and fourth methods are not implemented by this driver
+ because all known adapters support one of the first two. */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)
+ && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+ return -ENODEV;
+
+ strlcpy(info->type, "eeprom", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int eeprom_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct eeprom_data *data;
+ int err;
+
+ if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ memset(data->data, 0xff, EEPROM_SIZE);
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+ data->nature = UNKNOWN;
+
+ /* Detect the Vaio nature of EEPROMs.
+ We use the "PCG-" or "VGN-" prefix as the signature. */
+ if (client->addr == 0x57
+ && i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
+ char name[4];
+
+ name[0] = i2c_smbus_read_byte_data(client, 0x80);
+ name[1] = i2c_smbus_read_byte_data(client, 0x81);
+ name[2] = i2c_smbus_read_byte_data(client, 0x82);
+ name[3] = i2c_smbus_read_byte_data(client, 0x83);
+
+ if (!memcmp(name, "PCG-", 4) || !memcmp(name, "VGN-", 4)) {
+ dev_info(&client->dev, "Vaio EEPROM detected, "
+ "enabling privacy protection\n");
+ data->nature = VAIO;
+ }
+ }
+
+ /* create the sysfs eeprom file */
+ err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr);
+ if (err)
+ goto exit_kfree;
+
+ return 0;
+
+exit_kfree:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int eeprom_remove(struct i2c_client *client)
+{
+ sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr);
+ kfree(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static const struct i2c_device_id eeprom_id[] = {
+ { "eeprom", 0 },
+ { }
+};
+
+static struct i2c_driver eeprom_driver = {
+ .driver = {
+ .name = "eeprom",
+ },
+ .probe = eeprom_probe,
+ .remove = eeprom_remove,
+ .id_table = eeprom_id,
+
+ .class = I2C_CLASS_DDC | I2C_CLASS_SPD,
+ .detect = eeprom_detect,
+ .address_data = &addr_data,
+};
+
+static int __init eeprom_init(void)
+{
+ return i2c_add_driver(&eeprom_driver);
+}
+
+static void __exit eeprom_exit(void)
+{
+ i2c_del_driver(&eeprom_driver);
+}
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
+ "Philip Edelbrock <phil@netroedge.com> and "
+ "Greg Kroah-Hartman <greg@kroah.com>");
+MODULE_DESCRIPTION("I2C EEPROM driver");
+MODULE_LICENSE("GPL");
+
+module_init(eeprom_init);
+module_exit(eeprom_exit);
diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/i2c/chips/isp1301_omap.c
new file mode 100644
index 0000000..e0d56ef
--- /dev/null
+++ b/drivers/i2c/chips/isp1301_omap.c
@@ -0,0 +1,1683 @@
+/*
+ * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller
+ *
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004 David Brownell
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <mach/usb.h>
+#include <mach/mux.h>
+
+
+#ifndef DEBUG
+#undef VERBOSE
+#endif
+
+
+#define DRIVER_VERSION "24 August 2004"
+#define DRIVER_NAME (isp1301_driver.driver.name)
+
+MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver");
+MODULE_LICENSE("GPL");
+
+struct isp1301 {
+ struct otg_transceiver otg;
+ struct i2c_client *client;
+ void (*i2c_release)(struct device *dev);
+
+ int irq_type;
+
+ u32 last_otg_ctrl;
+ unsigned working:1;
+
+ struct timer_list timer;
+
+ /* use keventd context to change the state for us */
+ struct work_struct work;
+
+ unsigned long todo;
+# define WORK_UPDATE_ISP 0 /* update ISP from OTG */
+# define WORK_UPDATE_OTG 1 /* update OTG from ISP */
+# define WORK_HOST_RESUME 4 /* resume host */
+# define WORK_TIMER 6 /* timer fired */
+# define WORK_STOP 7 /* don't resubmit */
+};
+
+
+/* bits in OTG_CTRL */
+
+#define OTG_XCEIV_OUTPUTS \
+ (OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID)
+#define OTG_XCEIV_INPUTS \
+ (OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID)
+#define OTG_CTRL_BITS \
+ (OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP)
+ /* and OTG_PULLUP is sometimes written */
+
+#define OTG_CTRL_MASK (OTG_DRIVER_SEL| \
+ OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \
+ OTG_CTRL_BITS)
+
+
+/*-------------------------------------------------------------------------*/
+
+/* board-specific PM hooks */
+
+#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3)
+
+#if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE)
+
+#include <linux/i2c/tps65010.h>
+
+#else
+
+static inline int tps65010_set_vbus_draw(unsigned mA)
+{
+ pr_debug("tps65010: draw %d mA (STUB)\n", mA);
+ return 0;
+}
+
+#endif
+
+static void enable_vbus_draw(struct isp1301 *isp, unsigned mA)
+{
+ int status = tps65010_set_vbus_draw(mA);
+ if (status < 0)
+ pr_debug(" VBUS %d mA error %d\n", mA, status);
+}
+
+static void enable_vbus_source(struct isp1301 *isp)
+{
+ /* this board won't supply more than 8mA vbus power.
+ * some boards can switch a 100ma "unit load" (or more).
+ */
+}
+
+
+/* products will deliver OTG messages with LEDs, GUI, etc */
+static inline void notresponding(struct isp1301 *isp)
+{
+ printk(KERN_NOTICE "OTG device not responding.\n");
+}
+
+
+#endif
+
+#if defined(CONFIG_MACH_OMAP_H4)
+
+static void enable_vbus_draw(struct isp1301 *isp, unsigned mA)
+{
+ /* H4 controls this by DIP switch S2.4; no soft control.
+ * ON means the charger is always enabled. Leave it OFF
+ * unless the OTG port is used only in B-peripheral mode.
+ */
+}
+
+static void enable_vbus_source(struct isp1301 *isp)
+{
+ /* this board won't supply more than 8mA vbus power.
+ * some boards can switch a 100ma "unit load" (or more).
+ */
+}
+
+
+/* products will deliver OTG messages with LEDs, GUI, etc */
+static inline void notresponding(struct isp1301 *isp)
+{
+ printk(KERN_NOTICE "OTG device not responding.\n");
+}
+
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static struct i2c_driver isp1301_driver;
+
+/* smbus apis are used for portability */
+
+static inline u8
+isp1301_get_u8(struct isp1301 *isp, u8 reg)
+{
+ return i2c_smbus_read_byte_data(isp->client, reg + 0);
+}
+
+static inline int
+isp1301_get_u16(struct isp1301 *isp, u8 reg)
+{
+ return i2c_smbus_read_word_data(isp->client, reg);
+}
+
+static inline int
+isp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits)
+{
+ return i2c_smbus_write_byte_data(isp->client, reg + 0, bits);
+}
+
+static inline int
+isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits)
+{
+ return i2c_smbus_write_byte_data(isp->client, reg + 1, bits);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* identification */
+#define ISP1301_VENDOR_ID 0x00 /* u16 read */
+#define ISP1301_PRODUCT_ID 0x02 /* u16 read */
+#define ISP1301_BCD_DEVICE 0x14 /* u16 read */
+
+#define I2C_VENDOR_ID_PHILIPS 0x04cc
+#define I2C_PRODUCT_ID_PHILIPS_1301 0x1301
+
+/* operational registers */
+#define ISP1301_MODE_CONTROL_1 0x04 /* u8 read, set, +1 clear */
+# define MC1_SPEED (1 << 0)
+# define MC1_SUSPEND (1 << 1)
+# define MC1_DAT_SE0 (1 << 2)
+# define MC1_TRANSPARENT (1 << 3)
+# define MC1_BDIS_ACON_EN (1 << 4)
+# define MC1_OE_INT_EN (1 << 5)
+# define MC1_UART_EN (1 << 6)
+# define MC1_MASK 0x7f
+#define ISP1301_MODE_CONTROL_2 0x12 /* u8 read, set, +1 clear */
+# define MC2_GLOBAL_PWR_DN (1 << 0)
+# define MC2_SPD_SUSP_CTRL (1 << 1)
+# define MC2_BI_DI (1 << 2)
+# define MC2_TRANSP_BDIR0 (1 << 3)
+# define MC2_TRANSP_BDIR1 (1 << 4)
+# define MC2_AUDIO_EN (1 << 5)
+# define MC2_PSW_EN (1 << 6)
+# define MC2_EN2V7 (1 << 7)
+#define ISP1301_OTG_CONTROL_1 0x06 /* u8 read, set, +1 clear */
+# define OTG1_DP_PULLUP (1 << 0)
+# define OTG1_DM_PULLUP (1 << 1)
+# define OTG1_DP_PULLDOWN (1 << 2)
+# define OTG1_DM_PULLDOWN (1 << 3)
+# define OTG1_ID_PULLDOWN (1 << 4)
+# define OTG1_VBUS_DRV (1 << 5)
+# define OTG1_VBUS_DISCHRG (1 << 6)
+# define OTG1_VBUS_CHRG (1 << 7)
+#define ISP1301_OTG_STATUS 0x10 /* u8 readonly */
+# define OTG_B_SESS_END (1 << 6)
+# define OTG_B_SESS_VLD (1 << 7)
+
+#define ISP1301_INTERRUPT_SOURCE 0x08 /* u8 read */
+#define ISP1301_INTERRUPT_LATCH 0x0A /* u8 read, set, +1 clear */
+
+#define ISP1301_INTERRUPT_FALLING 0x0C /* u8 read, set, +1 clear */
+#define ISP1301_INTERRUPT_RISING 0x0E /* u8 read, set, +1 clear */
+
+/* same bitfields in all interrupt registers */
+# define INTR_VBUS_VLD (1 << 0)
+# define INTR_SESS_VLD (1 << 1)
+# define INTR_DP_HI (1 << 2)
+# define INTR_ID_GND (1 << 3)
+# define INTR_DM_HI (1 << 4)
+# define INTR_ID_FLOAT (1 << 5)
+# define INTR_BDIS_ACON (1 << 6)
+# define INTR_CR_INT (1 << 7)
+
+/*-------------------------------------------------------------------------*/
+
+static const char *state_string(enum usb_otg_state state)
+{
+ switch (state) {
+ case OTG_STATE_A_IDLE: return "a_idle";
+ case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise";
+ case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon";
+ case OTG_STATE_A_HOST: return "a_host";
+ case OTG_STATE_A_SUSPEND: return "a_suspend";
+ case OTG_STATE_A_PERIPHERAL: return "a_peripheral";
+ case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall";
+ case OTG_STATE_A_VBUS_ERR: return "a_vbus_err";
+ case OTG_STATE_B_IDLE: return "b_idle";
+ case OTG_STATE_B_SRP_INIT: return "b_srp_init";
+ case OTG_STATE_B_PERIPHERAL: return "b_peripheral";
+ case OTG_STATE_B_WAIT_ACON: return "b_wait_acon";
+ case OTG_STATE_B_HOST: return "b_host";
+ default: return "UNDEFINED";
+ }
+}
+
+static inline const char *state_name(struct isp1301 *isp)
+{
+ return state_string(isp->otg.state);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* NOTE: some of this ISP1301 setup is specific to H2 boards;
+ * not everything is guarded by board-specific checks, or even using
+ * omap_usb_config data to deduce MC1_DAT_SE0 and MC2_BI_DI.
+ *
+ * ALSO: this currently doesn't use ISP1301 low-power modes
+ * while OTG is running.
+ */
+
+static void power_down(struct isp1301 *isp)
+{
+ isp->otg.state = OTG_STATE_UNDEFINED;
+
+ // isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);
+ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND);
+
+ isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_ID_PULLDOWN);
+ isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+}
+
+static void power_up(struct isp1301 *isp)
+{
+ // isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);
+ isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND);
+
+ /* do this only when cpu is driving transceiver,
+ * so host won't see a low speed device...
+ */
+ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+}
+
+#define NO_HOST_SUSPEND
+
+static int host_suspend(struct isp1301 *isp)
+{
+#ifdef NO_HOST_SUSPEND
+ return 0;
+#else
+ struct device *dev;
+
+ if (!isp->otg.host)
+ return -ENODEV;
+
+ /* Currently ASSUMES only the OTG port matters;
+ * other ports could be active...
+ */
+ dev = isp->otg.host->controller;
+ return dev->driver->suspend(dev, 3, 0);
+#endif
+}
+
+static int host_resume(struct isp1301 *isp)
+{
+#ifdef NO_HOST_SUSPEND
+ return 0;
+#else
+ struct device *dev;
+
+ if (!isp->otg.host)
+ return -ENODEV;
+
+ dev = isp->otg.host->controller;
+ return dev->driver->resume(dev, 0);
+#endif
+}
+
+static int gadget_suspend(struct isp1301 *isp)
+{
+ isp->otg.gadget->b_hnp_enable = 0;
+ isp->otg.gadget->a_hnp_support = 0;
+ isp->otg.gadget->a_alt_hnp_support = 0;
+ return usb_gadget_vbus_disconnect(isp->otg.gadget);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define TIMER_MINUTES 10
+#define TIMER_JIFFIES (TIMER_MINUTES * 60 * HZ)
+
+/* Almost all our I2C messaging comes from a work queue's task context.
+ * NOTE: guaranteeing certain response times might mean we shouldn't
+ * share keventd's work queue; a realtime task might be safest.
+ */
+static void isp1301_defer_work(struct isp1301 *isp, int work)
+{
+ int status;
+
+ if (isp && !test_and_set_bit(work, &isp->todo)) {
+ (void) get_device(&isp->client->dev);
+ status = schedule_work(&isp->work);
+ if (!status && !isp->working)
+ dev_vdbg(&isp->client->dev,
+ "work item %d may be lost\n", work);
+ }
+}
+
+/* called from irq handlers */
+static void a_idle(struct isp1301 *isp, const char *tag)
+{
+ u32 l;
+
+ if (isp->otg.state == OTG_STATE_A_IDLE)
+ return;
+
+ isp->otg.default_a = 1;
+ if (isp->otg.host) {
+ isp->otg.host->is_b_host = 0;
+ host_suspend(isp);
+ }
+ if (isp->otg.gadget) {
+ isp->otg.gadget->is_a_peripheral = 1;
+ gadget_suspend(isp);
+ }
+ isp->otg.state = OTG_STATE_A_IDLE;
+ l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+ omap_writel(l, OTG_CTRL);
+ isp->last_otg_ctrl = l;
+ pr_debug(" --> %s/%s\n", state_name(isp), tag);
+}
+
+/* called from irq handlers */
+static void b_idle(struct isp1301 *isp, const char *tag)
+{
+ u32 l;
+
+ if (isp->otg.state == OTG_STATE_B_IDLE)
+ return;
+
+ isp->otg.default_a = 0;
+ if (isp->otg.host) {
+ isp->otg.host->is_b_host = 1;
+ host_suspend(isp);
+ }
+ if (isp->otg.gadget) {
+ isp->otg.gadget->is_a_peripheral = 0;
+ gadget_suspend(isp);
+ }
+ isp->otg.state = OTG_STATE_B_IDLE;
+ l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+ omap_writel(l, OTG_CTRL);
+ isp->last_otg_ctrl = l;
+ pr_debug(" --> %s/%s\n", state_name(isp), tag);
+}
+
+static void
+dump_regs(struct isp1301 *isp, const char *label)
+{
+#ifdef DEBUG
+ u8 ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1);
+ u8 status = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
+ u8 src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE);
+
+ pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n",
+ omap_readl(OTG_CTRL), label, state_name(isp),
+ ctrl, status, src);
+ /* mode control and irq enables don't change much */
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_OTG
+
+/*
+ * The OMAP OTG controller handles most of the OTG state transitions.
+ *
+ * We translate isp1301 outputs (mostly voltage comparator status) into
+ * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state
+ * flags into isp1301 inputs ... and infer state transitions.
+ */
+
+#ifdef VERBOSE
+
+static void check_state(struct isp1301 *isp, const char *tag)
+{
+ enum usb_otg_state state = OTG_STATE_UNDEFINED;
+ u8 fsm = omap_readw(OTG_TEST) & 0x0ff;
+ unsigned extra = 0;
+
+ switch (fsm) {
+
+ /* default-b */
+ case 0x0:
+ state = OTG_STATE_B_IDLE;
+ break;
+ case 0x3:
+ case 0x7:
+ extra = 1;
+ case 0x1:
+ state = OTG_STATE_B_PERIPHERAL;
+ break;
+ case 0x11:
+ state = OTG_STATE_B_SRP_INIT;
+ break;
+
+ /* extra dual-role default-b states */
+ case 0x12:
+ case 0x13:
+ case 0x16:
+ extra = 1;
+ case 0x17:
+ state = OTG_STATE_B_WAIT_ACON;
+ break;
+ case 0x34:
+ state = OTG_STATE_B_HOST;
+ break;
+
+ /* default-a */
+ case 0x36:
+ state = OTG_STATE_A_IDLE;
+ break;
+ case 0x3c:
+ state = OTG_STATE_A_WAIT_VFALL;
+ break;
+ case 0x7d:
+ state = OTG_STATE_A_VBUS_ERR;
+ break;
+ case 0x9e:
+ case 0x9f:
+ extra = 1;
+ case 0x89:
+ state = OTG_STATE_A_PERIPHERAL;
+ break;
+ case 0xb7:
+ state = OTG_STATE_A_WAIT_VRISE;
+ break;
+ case 0xb8:
+ state = OTG_STATE_A_WAIT_BCON;
+ break;
+ case 0xb9:
+ state = OTG_STATE_A_HOST;
+ break;
+ case 0xba:
+ state = OTG_STATE_A_SUSPEND;
+ break;
+ default:
+ break;
+ }
+ if (isp->otg.state == state && !extra)
+ return;
+ pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag,
+ state_string(state), fsm, state_name(isp),
+ omap_readl(OTG_CTRL));
+}
+
+#else
+
+static inline void check_state(struct isp1301 *isp, const char *tag) { }
+
+#endif
+
+/* outputs from ISP1301_INTERRUPT_SOURCE */
+static void update_otg1(struct isp1301 *isp, u8 int_src)
+{
+ u32 otg_ctrl;
+
+ otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+ otg_ctrl &= ~OTG_XCEIV_INPUTS;
+ otg_ctrl &= ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD);
+
+ if (int_src & INTR_SESS_VLD)
+ otg_ctrl |= OTG_ASESSVLD;
+ else if (isp->otg.state == OTG_STATE_A_WAIT_VFALL) {
+ a_idle(isp, "vfall");
+ otg_ctrl &= ~OTG_CTRL_BITS;
+ }
+ if (int_src & INTR_VBUS_VLD)
+ otg_ctrl |= OTG_VBUSVLD;
+ if (int_src & INTR_ID_GND) { /* default-A */
+ if (isp->otg.state == OTG_STATE_B_IDLE
+ || isp->otg.state == OTG_STATE_UNDEFINED) {
+ a_idle(isp, "init");
+ return;
+ }
+ } else { /* default-B */
+ otg_ctrl |= OTG_ID;
+ if (isp->otg.state == OTG_STATE_A_IDLE
+ || isp->otg.state == OTG_STATE_UNDEFINED) {
+ b_idle(isp, "init");
+ return;
+ }
+ }
+ omap_writel(otg_ctrl, OTG_CTRL);
+}
+
+/* outputs from ISP1301_OTG_STATUS */
+static void update_otg2(struct isp1301 *isp, u8 otg_status)
+{
+ u32 otg_ctrl;
+
+ otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+ otg_ctrl &= ~OTG_XCEIV_INPUTS;
+ otg_ctrl &= ~(OTG_BSESSVLD | OTG_BSESSEND);
+ if (otg_status & OTG_B_SESS_VLD)
+ otg_ctrl |= OTG_BSESSVLD;
+ else if (otg_status & OTG_B_SESS_END)
+ otg_ctrl |= OTG_BSESSEND;
+ omap_writel(otg_ctrl, OTG_CTRL);
+}
+
+/* inputs going to ISP1301 */
+static void otg_update_isp(struct isp1301 *isp)
+{
+ u32 otg_ctrl, otg_change;
+ u8 set = OTG1_DM_PULLDOWN, clr = OTG1_DM_PULLUP;
+
+ otg_ctrl = omap_readl(OTG_CTRL);
+ otg_change = otg_ctrl ^ isp->last_otg_ctrl;
+ isp->last_otg_ctrl = otg_ctrl;
+ otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS;
+
+ switch (isp->otg.state) {
+ case OTG_STATE_B_IDLE:
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_SRP_INIT:
+ if (!(otg_ctrl & OTG_PULLUP)) {
+ // if (otg_ctrl & OTG_B_HNPEN) {
+ if (isp->otg.gadget->b_hnp_enable) {
+ isp->otg.state = OTG_STATE_B_WAIT_ACON;
+ pr_debug(" --> b_wait_acon\n");
+ }
+ goto pulldown;
+ }
+pullup:
+ set |= OTG1_DP_PULLUP;
+ clr |= OTG1_DP_PULLDOWN;
+ break;
+ case OTG_STATE_A_SUSPEND:
+ case OTG_STATE_A_PERIPHERAL:
+ if (otg_ctrl & OTG_PULLUP)
+ goto pullup;
+ /* FALLTHROUGH */
+ // case OTG_STATE_B_WAIT_ACON:
+ default:
+pulldown:
+ set |= OTG1_DP_PULLDOWN;
+ clr |= OTG1_DP_PULLUP;
+ break;
+ }
+
+# define toggle(OTG,ISP) do { \
+ if (otg_ctrl & OTG) set |= ISP; \
+ else clr |= ISP; \
+ } while (0)
+
+ if (!(isp->otg.host))
+ otg_ctrl &= ~OTG_DRV_VBUS;
+
+ switch (isp->otg.state) {
+ case OTG_STATE_A_SUSPEND:
+ if (otg_ctrl & OTG_DRV_VBUS) {
+ set |= OTG1_VBUS_DRV;
+ break;
+ }
+ /* HNP failed for some reason (A_AIDL_BDIS timeout) */
+ notresponding(isp);
+
+ /* FALLTHROUGH */
+ case OTG_STATE_A_VBUS_ERR:
+ isp->otg.state = OTG_STATE_A_WAIT_VFALL;
+ pr_debug(" --> a_wait_vfall\n");
+ /* FALLTHROUGH */
+ case OTG_STATE_A_WAIT_VFALL:
+ /* FIXME usbcore thinks port power is still on ... */
+ clr |= OTG1_VBUS_DRV;
+ break;
+ case OTG_STATE_A_IDLE:
+ if (otg_ctrl & OTG_DRV_VBUS) {
+ isp->otg.state = OTG_STATE_A_WAIT_VRISE;
+ pr_debug(" --> a_wait_vrise\n");
+ }
+ /* FALLTHROUGH */
+ default:
+ toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV);
+ }
+
+ toggle(OTG_PU_VBUS, OTG1_VBUS_CHRG);
+ toggle(OTG_PD_VBUS, OTG1_VBUS_DISCHRG);
+
+# undef toggle
+
+ isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, set);
+ isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, clr);
+
+ /* HNP switch to host or peripheral; and SRP */
+ if (otg_change & OTG_PULLUP) {
+ u32 l;
+
+ switch (isp->otg.state) {
+ case OTG_STATE_B_IDLE:
+ if (clr & OTG1_DP_PULLUP)
+ break;
+ isp->otg.state = OTG_STATE_B_PERIPHERAL;
+ pr_debug(" --> b_peripheral\n");
+ break;
+ case OTG_STATE_A_SUSPEND:
+ if (clr & OTG1_DP_PULLUP)
+ break;
+ isp->otg.state = OTG_STATE_A_PERIPHERAL;
+ pr_debug(" --> a_peripheral\n");
+ break;
+ default:
+ break;
+ }
+ l = omap_readl(OTG_CTRL);
+ l |= OTG_PULLUP;
+ omap_writel(l, OTG_CTRL);
+ }
+
+ check_state(isp, __func__);
+ dump_regs(isp, "otg->isp1301");
+}
+
+static irqreturn_t omap_otg_irq(int irq, void *_isp)
+{
+ u16 otg_irq = omap_readw(OTG_IRQ_SRC);
+ u32 otg_ctrl;
+ int ret = IRQ_NONE;
+ struct isp1301 *isp = _isp;
+
+ /* update ISP1301 transciever from OTG controller */
+ if (otg_irq & OPRT_CHG) {
+ omap_writew(OPRT_CHG, OTG_IRQ_SRC);
+ isp1301_defer_work(isp, WORK_UPDATE_ISP);
+ ret = IRQ_HANDLED;
+
+ /* SRP to become b_peripheral failed */
+ } else if (otg_irq & B_SRP_TMROUT) {
+ pr_debug("otg: B_SRP_TIMEOUT, %06x\n", omap_readl(OTG_CTRL));
+ notresponding(isp);
+
+ /* gadget drivers that care should monitor all kinds of
+ * remote wakeup (SRP, normal) using their own timer
+ * to give "check cable and A-device" messages.
+ */
+ if (isp->otg.state == OTG_STATE_B_SRP_INIT)
+ b_idle(isp, "srp_timeout");
+
+ omap_writew(B_SRP_TMROUT, OTG_IRQ_SRC);
+ ret = IRQ_HANDLED;
+
+ /* HNP to become b_host failed */
+ } else if (otg_irq & B_HNP_FAIL) {
+ pr_debug("otg: %s B_HNP_FAIL, %06x\n",
+ state_name(isp), omap_readl(OTG_CTRL));
+ notresponding(isp);
+
+ otg_ctrl = omap_readl(OTG_CTRL);
+ otg_ctrl |= OTG_BUSDROP;
+ otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+ omap_writel(otg_ctrl, OTG_CTRL);
+
+ /* subset of b_peripheral()... */
+ isp->otg.state = OTG_STATE_B_PERIPHERAL;
+ pr_debug(" --> b_peripheral\n");
+
+ omap_writew(B_HNP_FAIL, OTG_IRQ_SRC);
+ ret = IRQ_HANDLED;
+
+ /* detect SRP from B-device ... */
+ } else if (otg_irq & A_SRP_DETECT) {
+ pr_debug("otg: %s SRP_DETECT, %06x\n",
+ state_name(isp), omap_readl(OTG_CTRL));
+
+ isp1301_defer_work(isp, WORK_UPDATE_OTG);
+ switch (isp->otg.state) {
+ case OTG_STATE_A_IDLE:
+ if (!isp->otg.host)
+ break;
+ isp1301_defer_work(isp, WORK_HOST_RESUME);
+ otg_ctrl = omap_readl(OTG_CTRL);
+ otg_ctrl |= OTG_A_BUSREQ;
+ otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ)
+ & ~OTG_XCEIV_INPUTS
+ & OTG_CTRL_MASK;
+ omap_writel(otg_ctrl, OTG_CTRL);
+ break;
+ default:
+ break;
+ }
+
+ omap_writew(A_SRP_DETECT, OTG_IRQ_SRC);
+ ret = IRQ_HANDLED;
+
+ /* timer expired: T(a_wait_bcon) and maybe T(a_wait_vrise)
+ * we don't track them separately
+ */
+ } else if (otg_irq & A_REQ_TMROUT) {
+ otg_ctrl = omap_readl(OTG_CTRL);
+ pr_info("otg: BCON_TMOUT from %s, %06x\n",
+ state_name(isp), otg_ctrl);
+ notresponding(isp);
+
+ otg_ctrl |= OTG_BUSDROP;
+ otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+ omap_writel(otg_ctrl, OTG_CTRL);
+ isp->otg.state = OTG_STATE_A_WAIT_VFALL;
+
+ omap_writew(A_REQ_TMROUT, OTG_IRQ_SRC);
+ ret = IRQ_HANDLED;
+
+ /* A-supplied voltage fell too low; overcurrent */
+ } else if (otg_irq & A_VBUS_ERR) {
+ otg_ctrl = omap_readl(OTG_CTRL);
+ printk(KERN_ERR "otg: %s, VBUS_ERR %04x ctrl %06x\n",
+ state_name(isp), otg_irq, otg_ctrl);
+
+ otg_ctrl |= OTG_BUSDROP;
+ otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+ omap_writel(otg_ctrl, OTG_CTRL);
+ isp->otg.state = OTG_STATE_A_VBUS_ERR;
+
+ omap_writew(A_VBUS_ERR, OTG_IRQ_SRC);
+ ret = IRQ_HANDLED;
+
+ /* switch driver; the transciever code activates it,
+ * ungating the udc clock or resuming OHCI.
+ */
+ } else if (otg_irq & DRIVER_SWITCH) {
+ int kick = 0;
+
+ otg_ctrl = omap_readl(OTG_CTRL);
+ printk(KERN_NOTICE "otg: %s, SWITCH to %s, ctrl %06x\n",
+ state_name(isp),
+ (otg_ctrl & OTG_DRIVER_SEL)
+ ? "gadget" : "host",
+ otg_ctrl);
+ isp1301_defer_work(isp, WORK_UPDATE_ISP);
+
+ /* role is peripheral */
+ if (otg_ctrl & OTG_DRIVER_SEL) {
+ switch (isp->otg.state) {
+ case OTG_STATE_A_IDLE:
+ b_idle(isp, __func__);
+ break;
+ default:
+ break;
+ }
+ isp1301_defer_work(isp, WORK_UPDATE_ISP);
+
+ /* role is host */
+ } else {
+ if (!(otg_ctrl & OTG_ID)) {
+ otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+ omap_writel(otg_ctrl | OTG_A_BUSREQ, OTG_CTRL);
+ }
+
+ if (isp->otg.host) {
+ switch (isp->otg.state) {
+ case OTG_STATE_B_WAIT_ACON:
+ isp->otg.state = OTG_STATE_B_HOST;
+ pr_debug(" --> b_host\n");
+ kick = 1;
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ isp->otg.state = OTG_STATE_A_HOST;
+ pr_debug(" --> a_host\n");
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ isp->otg.state = OTG_STATE_A_WAIT_BCON;
+ pr_debug(" --> a_wait_bcon\n");
+ break;
+ default:
+ break;
+ }
+ isp1301_defer_work(isp, WORK_HOST_RESUME);
+ }
+ }
+
+ omap_writew(DRIVER_SWITCH, OTG_IRQ_SRC);
+ ret = IRQ_HANDLED;
+
+ if (kick)
+ usb_bus_start_enum(isp->otg.host,
+ isp->otg.host->otg_port);
+ }
+
+ check_state(isp, __func__);
+ return ret;
+}
+
+static struct platform_device *otg_dev;
+
+static int otg_init(struct isp1301 *isp)
+{
+ u32 l;
+
+ if (!otg_dev)
+ return -ENODEV;
+
+ dump_regs(isp, __func__);
+ /* some of these values are board-specific... */
+ l = omap_readl(OTG_SYSCON_2);
+ l |= OTG_EN
+ /* for B-device: */
+ | SRP_GPDATA /* 9msec Bdev D+ pulse */
+ | SRP_GPDVBUS /* discharge after VBUS pulse */
+ // | (3 << 24) /* 2msec VBUS pulse */
+ /* for A-device: */
+ | (0 << 20) /* 200ms nominal A_WAIT_VRISE timer */
+ | SRP_DPW /* detect 167+ns SRP pulses */
+ | SRP_DATA | SRP_VBUS /* accept both kinds of SRP pulse */
+ ;
+ omap_writel(l, OTG_SYSCON_2);
+
+ update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE));
+ update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS));
+
+ check_state(isp, __func__);
+ pr_debug("otg: %s, %s %06x\n",
+ state_name(isp), __func__, omap_readl(OTG_CTRL));
+
+ omap_writew(DRIVER_SWITCH | OPRT_CHG
+ | B_SRP_TMROUT | B_HNP_FAIL
+ | A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT, OTG_IRQ_EN);
+
+ l = omap_readl(OTG_SYSCON_2);
+ l |= OTG_EN;
+ omap_writel(l, OTG_SYSCON_2);
+
+ return 0;
+}
+
+static int otg_probe(struct platform_device *dev)
+{
+ // struct omap_usb_config *config = dev->platform_data;
+
+ otg_dev = dev;
+ return 0;
+}
+
+static int otg_remove(struct platform_device *dev)
+{
+ otg_dev = NULL;
+ return 0;
+}
+
+static struct platform_driver omap_otg_driver = {
+ .probe = otg_probe,
+ .remove = otg_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "omap_otg",
+ },
+};
+
+static int otg_bind(struct isp1301 *isp)
+{
+ int status;
+
+ if (otg_dev)
+ return -EBUSY;
+
+ status = platform_driver_register(&omap_otg_driver);
+ if (status < 0)
+ return status;
+
+ if (otg_dev)
+ status = request_irq(otg_dev->resource[1].start, omap_otg_irq,
+ IRQF_DISABLED, DRIVER_NAME, isp);
+ else
+ status = -ENODEV;
+
+ if (status < 0)
+ platform_driver_unregister(&omap_otg_driver);
+ return status;
+}
+
+static void otg_unbind(struct isp1301 *isp)
+{
+ if (!otg_dev)
+ return;
+ free_irq(otg_dev->resource[1].start, isp);
+}
+
+#else
+
+/* OTG controller isn't clocked */
+
+#endif /* CONFIG_USB_OTG */
+
+/*-------------------------------------------------------------------------*/
+
+static void b_peripheral(struct isp1301 *isp)
+{
+ u32 l;
+
+ l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+ omap_writel(l, OTG_CTRL);
+
+ usb_gadget_vbus_connect(isp->otg.gadget);
+
+#ifdef CONFIG_USB_OTG
+ enable_vbus_draw(isp, 8);
+ otg_update_isp(isp);
+#else
+ enable_vbus_draw(isp, 100);
+ /* UDC driver just set OTG_BSESSVLD */
+ isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP);
+ isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN);
+ isp->otg.state = OTG_STATE_B_PERIPHERAL;
+ pr_debug(" --> b_peripheral\n");
+ dump_regs(isp, "2periph");
+#endif
+}
+
+static void isp_update_otg(struct isp1301 *isp, u8 stat)
+{
+ u8 isp_stat, isp_bstat;
+ enum usb_otg_state state = isp->otg.state;
+
+ if (stat & INTR_BDIS_ACON)
+ pr_debug("OTG: BDIS_ACON, %s\n", state_name(isp));
+
+ /* start certain state transitions right away */
+ isp_stat = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE);
+ if (isp_stat & INTR_ID_GND) {
+ if (isp->otg.default_a) {
+ switch (state) {
+ case OTG_STATE_B_IDLE:
+ a_idle(isp, "idle");
+ /* FALLTHROUGH */
+ case OTG_STATE_A_IDLE:
+ enable_vbus_source(isp);
+ /* FALLTHROUGH */
+ case OTG_STATE_A_WAIT_VRISE:
+ /* we skip over OTG_STATE_A_WAIT_BCON, since
+ * the HC will transition to A_HOST (or
+ * A_SUSPEND!) without our noticing except
+ * when HNP is used.
+ */
+ if (isp_stat & INTR_VBUS_VLD)
+ isp->otg.state = OTG_STATE_A_HOST;
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ if (!(isp_stat & INTR_SESS_VLD))
+ a_idle(isp, "vfell");
+ break;
+ default:
+ if (!(isp_stat & INTR_VBUS_VLD))
+ isp->otg.state = OTG_STATE_A_VBUS_ERR;
+ break;
+ }
+ isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
+ } else {
+ switch (state) {
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_HOST:
+ case OTG_STATE_B_WAIT_ACON:
+ usb_gadget_vbus_disconnect(isp->otg.gadget);
+ break;
+ default:
+ break;
+ }
+ if (state != OTG_STATE_A_IDLE)
+ a_idle(isp, "id");
+ if (isp->otg.host && state == OTG_STATE_A_IDLE)
+ isp1301_defer_work(isp, WORK_HOST_RESUME);
+ isp_bstat = 0;
+ }
+ } else {
+ u32 l;
+
+ /* if user unplugged mini-A end of cable,
+ * don't bypass A_WAIT_VFALL.
+ */
+ if (isp->otg.default_a) {
+ switch (state) {
+ default:
+ isp->otg.state = OTG_STATE_A_WAIT_VFALL;
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ state = OTG_STATE_A_IDLE;
+ /* khubd may take a while to notice and
+ * handle this disconnect, so don't go
+ * to B_IDLE quite yet.
+ */
+ break;
+ case OTG_STATE_A_IDLE:
+ host_suspend(isp);
+ isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1,
+ MC1_BDIS_ACON_EN);
+ isp->otg.state = OTG_STATE_B_IDLE;
+ l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+ l &= ~OTG_CTRL_BITS;
+ omap_writel(l, OTG_CTRL);
+ break;
+ case OTG_STATE_B_IDLE:
+ break;
+ }
+ }
+ isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
+
+ switch (isp->otg.state) {
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_HOST:
+ if (likely(isp_bstat & OTG_B_SESS_VLD))
+ break;
+ enable_vbus_draw(isp, 0);
+#ifndef CONFIG_USB_OTG
+ /* UDC driver will clear OTG_BSESSVLD */
+ isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1,
+ OTG1_DP_PULLDOWN);
+ isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1,
+ OTG1_DP_PULLUP);
+ dump_regs(isp, __func__);
+#endif
+ /* FALLTHROUGH */
+ case OTG_STATE_B_SRP_INIT:
+ b_idle(isp, __func__);
+ l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+ omap_writel(l, OTG_CTRL);
+ /* FALLTHROUGH */
+ case OTG_STATE_B_IDLE:
+ if (isp->otg.gadget && (isp_bstat & OTG_B_SESS_VLD)) {
+#ifdef CONFIG_USB_OTG
+ update_otg1(isp, isp_stat);
+ update_otg2(isp, isp_bstat);
+#endif
+ b_peripheral(isp);
+ } else if (!(isp_stat & (INTR_VBUS_VLD|INTR_SESS_VLD)))
+ isp_bstat |= OTG_B_SESS_END;
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ break;
+ default:
+ pr_debug("otg: unsupported b-device %s\n",
+ state_name(isp));
+ break;
+ }
+ }
+
+ if (state != isp->otg.state)
+ pr_debug(" isp, %s -> %s\n",
+ state_string(state), state_name(isp));
+
+#ifdef CONFIG_USB_OTG
+ /* update the OTG controller state to match the isp1301; may
+ * trigger OPRT_CHG irqs for changes going to the isp1301.
+ */
+ update_otg1(isp, isp_stat);
+ update_otg2(isp, isp_bstat);
+ check_state(isp, __func__);
+#endif
+
+ dump_regs(isp, "isp1301->otg");
+}
+
+/*-------------------------------------------------------------------------*/
+
+static u8 isp1301_clear_latch(struct isp1301 *isp)
+{
+ u8 latch = isp1301_get_u8(isp, ISP1301_INTERRUPT_LATCH);
+ isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, latch);
+ return latch;
+}
+
+static void
+isp1301_work(struct work_struct *work)
+{
+ struct isp1301 *isp = container_of(work, struct isp1301, work);
+ int stop;
+
+ /* implicit lock: we're the only task using this device */
+ isp->working = 1;
+ do {
+ stop = test_bit(WORK_STOP, &isp->todo);
+
+#ifdef CONFIG_USB_OTG
+ /* transfer state from otg engine to isp1301 */
+ if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) {
+ otg_update_isp(isp);
+ put_device(&isp->client->dev);
+ }
+#endif
+ /* transfer state from isp1301 to otg engine */
+ if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) {
+ u8 stat = isp1301_clear_latch(isp);
+
+ isp_update_otg(isp, stat);
+ put_device(&isp->client->dev);
+ }
+
+ if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) {
+ u32 otg_ctrl;
+
+ /*
+ * skip A_WAIT_VRISE; hc transitions invisibly
+ * skip A_WAIT_BCON; same.
+ */
+ switch (isp->otg.state) {
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_WAIT_VRISE:
+ isp->otg.state = OTG_STATE_A_HOST;
+ pr_debug(" --> a_host\n");
+ otg_ctrl = omap_readl(OTG_CTRL);
+ otg_ctrl |= OTG_A_BUSREQ;
+ otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ)
+ & OTG_CTRL_MASK;
+ omap_writel(otg_ctrl, OTG_CTRL);
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ isp->otg.state = OTG_STATE_B_HOST;
+ pr_debug(" --> b_host (acon)\n");
+ break;
+ case OTG_STATE_B_HOST:
+ case OTG_STATE_B_IDLE:
+ case OTG_STATE_A_IDLE:
+ break;
+ default:
+ pr_debug(" host resume in %s\n",
+ state_name(isp));
+ }
+ host_resume(isp);
+ // mdelay(10);
+ put_device(&isp->client->dev);
+ }
+
+ if (test_and_clear_bit(WORK_TIMER, &isp->todo)) {
+#ifdef VERBOSE
+ dump_regs(isp, "timer");
+ if (!stop)
+ mod_timer(&isp->timer, jiffies + TIMER_JIFFIES);
+#endif
+ put_device(&isp->client->dev);
+ }
+
+ if (isp->todo)
+ dev_vdbg(&isp->client->dev,
+ "work done, todo = 0x%lx\n",
+ isp->todo);
+ if (stop) {
+ dev_dbg(&isp->client->dev, "stop\n");
+ break;
+ }
+ } while (isp->todo);
+ isp->working = 0;
+}
+
+static irqreturn_t isp1301_irq(int irq, void *isp)
+{
+ isp1301_defer_work(isp, WORK_UPDATE_OTG);
+ return IRQ_HANDLED;
+}
+
+static void isp1301_timer(unsigned long _isp)
+{
+ isp1301_defer_work((void *)_isp, WORK_TIMER);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void isp1301_release(struct device *dev)
+{
+ struct isp1301 *isp;
+
+ isp = dev_get_drvdata(dev);
+
+ /* FIXME -- not with a "new style" driver, it doesn't!! */
+
+ /* ugly -- i2c hijacks our memory hook to wait_for_completion() */
+ if (isp->i2c_release)
+ isp->i2c_release(dev);
+ kfree (isp);
+}
+
+static struct isp1301 *the_transceiver;
+
+static int __exit isp1301_remove(struct i2c_client *i2c)
+{
+ struct isp1301 *isp;
+
+ isp = i2c_get_clientdata(i2c);
+
+ isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0);
+ isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0);
+ free_irq(i2c->irq, isp);
+#ifdef CONFIG_USB_OTG
+ otg_unbind(isp);
+#endif
+ if (machine_is_omap_h2())
+ gpio_free(2);
+
+ isp->timer.data = 0;
+ set_bit(WORK_STOP, &isp->todo);
+ del_timer_sync(&isp->timer);
+ flush_scheduled_work();
+
+ put_device(&i2c->dev);
+ the_transceiver = NULL;
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* NOTE: three modes are possible here, only one of which
+ * will be standards-conformant on any given system:
+ *
+ * - OTG mode (dual-role), required if there's a Mini-AB connector
+ * - HOST mode, for when there's one or more A (host) connectors
+ * - DEVICE mode, for when there's a B/Mini-B (device) connector
+ *
+ * As a rule, you won't have an isp1301 chip unless it's there to
+ * support the OTG mode. Other modes help testing USB controllers
+ * in isolation from (full) OTG support, or maybe so later board
+ * revisions can help to support those feature.
+ */
+
+#ifdef CONFIG_USB_OTG
+
+static int isp1301_otg_enable(struct isp1301 *isp)
+{
+ power_up(isp);
+ otg_init(isp);
+
+ /* NOTE: since we don't change this, this provides
+ * a few more interrupts than are strictly needed.
+ */
+ isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
+ INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
+ isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
+ INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
+
+ dev_info(&isp->client->dev, "ready for dual-role USB ...\n");
+
+ return 0;
+}
+
+#endif
+
+/* add or disable the host device+driver */
+static int
+isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host)
+{
+ struct isp1301 *isp = container_of(otg, struct isp1301, otg);
+
+ if (!otg || isp != the_transceiver)
+ return -ENODEV;
+
+ if (!host) {
+ omap_writew(0, OTG_IRQ_EN);
+ power_down(isp);
+ isp->otg.host = NULL;
+ return 0;
+ }
+
+#ifdef CONFIG_USB_OTG
+ isp->otg.host = host;
+ dev_dbg(&isp->client->dev, "registered host\n");
+ host_suspend(isp);
+ if (isp->otg.gadget)
+ return isp1301_otg_enable(isp);
+ return 0;
+
+#elif !defined(CONFIG_USB_GADGET_OMAP)
+ // FIXME update its refcount
+ isp->otg.host = host;
+
+ power_up(isp);
+
+ if (machine_is_omap_h2())
+ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+
+ dev_info(&isp->client->dev, "A-Host sessions ok\n");
+ isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
+ INTR_ID_GND);
+ isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
+ INTR_ID_GND);
+
+ /* If this has a Mini-AB connector, this mode is highly
+ * nonstandard ... but can be handy for testing, especially with
+ * the Mini-A end of an OTG cable. (Or something nonstandard
+ * like MiniB-to-StandardB, maybe built with a gender mender.)
+ */
+ isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV);
+
+ dump_regs(isp, __func__);
+
+ return 0;
+
+#else
+ dev_dbg(&isp->client->dev, "host sessions not allowed\n");
+ return -EINVAL;
+#endif
+
+}
+
+static int
+isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget)
+{
+ struct isp1301 *isp = container_of(otg, struct isp1301, otg);
+#ifndef CONFIG_USB_OTG
+ u32 l;
+#endif
+
+ if (!otg || isp != the_transceiver)
+ return -ENODEV;
+
+ if (!gadget) {
+ omap_writew(0, OTG_IRQ_EN);
+ if (!isp->otg.default_a)
+ enable_vbus_draw(isp, 0);
+ usb_gadget_vbus_disconnect(isp->otg.gadget);
+ isp->otg.gadget = NULL;
+ power_down(isp);
+ return 0;
+ }
+
+#ifdef CONFIG_USB_OTG
+ isp->otg.gadget = gadget;
+ dev_dbg(&isp->client->dev, "registered gadget\n");
+ /* gadget driver may be suspended until vbus_connect () */
+ if (isp->otg.host)
+ return isp1301_otg_enable(isp);
+ return 0;
+
+#elif !defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE)
+ isp->otg.gadget = gadget;
+ // FIXME update its refcount
+
+ l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+ l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS);
+ l |= OTG_ID;
+ omap_writel(l, OTG_CTRL);
+
+ power_up(isp);
+ isp->otg.state = OTG_STATE_B_IDLE;
+
+ if (machine_is_omap_h2() || machine_is_omap_h3())
+ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+
+ isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
+ INTR_SESS_VLD);
+ isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
+ INTR_VBUS_VLD);
+ dev_info(&isp->client->dev, "B-Peripheral sessions ok\n");
+ dump_regs(isp, __func__);
+
+ /* If this has a Mini-AB connector, this mode is highly
+ * nonstandard ... but can be handy for testing, so long
+ * as you don't plug a Mini-A cable into the jack.
+ */
+ if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD)
+ b_peripheral(isp);
+
+ return 0;
+
+#else
+ dev_dbg(&isp->client->dev, "peripheral sessions not allowed\n");
+ return -EINVAL;
+#endif
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int
+isp1301_set_power(struct otg_transceiver *dev, unsigned mA)
+{
+ if (!the_transceiver)
+ return -ENODEV;
+ if (dev->state == OTG_STATE_B_PERIPHERAL)
+ enable_vbus_draw(the_transceiver, mA);
+ return 0;
+}
+
+static int
+isp1301_start_srp(struct otg_transceiver *dev)
+{
+ struct isp1301 *isp = container_of(dev, struct isp1301, otg);
+ u32 otg_ctrl;
+
+ if (!dev || isp != the_transceiver
+ || isp->otg.state != OTG_STATE_B_IDLE)
+ return -ENODEV;
+
+ otg_ctrl = omap_readl(OTG_CTRL);
+ if (!(otg_ctrl & OTG_BSESSEND))
+ return -EINVAL;
+
+ otg_ctrl |= OTG_B_BUSREQ;
+ otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK;
+ omap_writel(otg_ctrl, OTG_CTRL);
+ isp->otg.state = OTG_STATE_B_SRP_INIT;
+
+ pr_debug("otg: SRP, %s ... %06x\n", state_name(isp),
+ omap_readl(OTG_CTRL));
+#ifdef CONFIG_USB_OTG
+ check_state(isp, __func__);
+#endif
+ return 0;
+}
+
+static int
+isp1301_start_hnp(struct otg_transceiver *dev)
+{
+#ifdef CONFIG_USB_OTG
+ struct isp1301 *isp = container_of(dev, struct isp1301, otg);
+ u32 l;
+
+ if (!dev || isp != the_transceiver)
+ return -ENODEV;
+ if (isp->otg.default_a && (isp->otg.host == NULL
+ || !isp->otg.host->b_hnp_enable))
+ return -ENOTCONN;
+ if (!isp->otg.default_a && (isp->otg.gadget == NULL
+ || !isp->otg.gadget->b_hnp_enable))
+ return -ENOTCONN;
+
+ /* We want hardware to manage most HNP protocol timings.
+ * So do this part as early as possible...
+ */
+ switch (isp->otg.state) {
+ case OTG_STATE_B_HOST:
+ isp->otg.state = OTG_STATE_B_PERIPHERAL;
+ /* caller will suspend next */
+ break;
+ case OTG_STATE_A_HOST:
+#if 0
+ /* autoconnect mode avoids irq latency bugs */
+ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1,
+ MC1_BDIS_ACON_EN);
+#endif
+ /* caller must suspend then clear A_BUSREQ */
+ usb_gadget_vbus_connect(isp->otg.gadget);
+ l = omap_readl(OTG_CTRL);
+ l |= OTG_A_SETB_HNPEN;
+ omap_writel(l, OTG_CTRL);
+
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ /* initiated by B-Host suspend */
+ break;
+ default:
+ return -EILSEQ;
+ }
+ pr_debug("otg: HNP %s, %06x ...\n",
+ state_name(isp), omap_readl(OTG_CTRL));
+ check_state(isp, __func__);
+ return 0;
+#else
+ /* srp-only */
+ return -EINVAL;
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __init
+isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+ int status;
+ struct isp1301 *isp;
+
+ if (the_transceiver)
+ return 0;
+
+ isp = kzalloc(sizeof *isp, GFP_KERNEL);
+ if (!isp)
+ return 0;
+
+ INIT_WORK(&isp->work, isp1301_work);
+ init_timer(&isp->timer);
+ isp->timer.function = isp1301_timer;
+ isp->timer.data = (unsigned long) isp;
+
+ i2c_set_clientdata(i2c, isp);
+ isp->client = i2c;
+
+ /* verify the chip (shouldn't be necesary) */
+ status = isp1301_get_u16(isp, ISP1301_VENDOR_ID);
+ if (status != I2C_VENDOR_ID_PHILIPS) {
+ dev_dbg(&i2c->dev, "not philips id: %d\n", status);
+ goto fail;
+ }
+ status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID);
+ if (status != I2C_PRODUCT_ID_PHILIPS_1301) {
+ dev_dbg(&i2c->dev, "not isp1301, %d\n", status);
+ goto fail;
+ }
+ isp->i2c_release = i2c->dev.release;
+ i2c->dev.release = isp1301_release;
+
+ /* initial development used chiprev 2.00 */
+ status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE);
+ dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n",
+ status >> 8, status & 0xff);
+
+ /* make like power-on reset */
+ isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK);
+
+ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI);
+ isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI);
+
+ isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1,
+ OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN);
+ isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1,
+ ~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN));
+
+ isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0);
+ isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0);
+ isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0);
+
+#ifdef CONFIG_USB_OTG
+ status = otg_bind(isp);
+ if (status < 0) {
+ dev_dbg(&i2c->dev, "can't bind OTG\n");
+ goto fail;
+ }
+#endif
+
+ if (machine_is_omap_h2()) {
+ /* full speed signaling by default */
+ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1,
+ MC1_SPEED);
+ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2,
+ MC2_SPD_SUSP_CTRL);
+
+ /* IRQ wired at M14 */
+ omap_cfg_reg(M14_1510_GPIO2);
+ if (gpio_request(2, "isp1301") == 0)
+ gpio_direction_input(2);
+ isp->irq_type = IRQF_TRIGGER_FALLING;
+ }
+
+ isp->irq_type |= IRQF_SAMPLE_RANDOM;
+ status = request_irq(i2c->irq, isp1301_irq,
+ isp->irq_type, DRIVER_NAME, isp);
+ if (status < 0) {
+ dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n",
+ i2c->irq, status);
+ goto fail;
+ }
+
+ isp->otg.dev = &i2c->dev;
+ isp->otg.label = DRIVER_NAME;
+
+ isp->otg.set_host = isp1301_set_host,
+ isp->otg.set_peripheral = isp1301_set_peripheral,
+ isp->otg.set_power = isp1301_set_power,
+ isp->otg.start_srp = isp1301_start_srp,
+ isp->otg.start_hnp = isp1301_start_hnp,
+
+ enable_vbus_draw(isp, 0);
+ power_down(isp);
+ the_transceiver = isp;
+
+#ifdef CONFIG_USB_OTG
+ update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE));
+ update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS));
+#endif
+
+ dump_regs(isp, __func__);
+
+#ifdef VERBOSE
+ mod_timer(&isp->timer, jiffies + TIMER_JIFFIES);
+ dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES);
+#endif
+
+ status = otg_set_transceiver(&isp->otg);
+ if (status < 0)
+ dev_err(&i2c->dev, "can't register transceiver, %d\n",
+ status);
+
+ return 0;
+
+fail:
+ kfree(isp);
+ return -ENODEV;
+}
+
+static const struct i2c_device_id isp1301_id[] = {
+ { "isp1301_omap", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isp1301_id);
+
+static struct i2c_driver isp1301_driver = {
+ .driver = {
+ .name = "isp1301_omap",
+ },
+ .probe = isp1301_probe,
+ .remove = __exit_p(isp1301_remove),
+ .id_table = isp1301_id,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init isp_init(void)
+{
+ return i2c_add_driver(&isp1301_driver);
+}
+module_init(isp_init);
+
+static void __exit isp_exit(void)
+{
+ if (the_transceiver)
+ otg_set_transceiver(NULL);
+ i2c_del_driver(&isp1301_driver);
+}
+module_exit(isp_exit);
+
diff --git a/drivers/i2c/chips/max6875.c b/drivers/i2c/chips/max6875.c
new file mode 100644
index 0000000..033d9d8
--- /dev/null
+++ b/drivers/i2c/chips/max6875.c
@@ -0,0 +1,246 @@
+/*
+ max6875.c - driver for MAX6874/MAX6875
+
+ Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
+
+ Based on i2c/chips/eeprom.c
+
+ The MAX6875 has a bank of registers and two banks of EEPROM.
+ Address ranges are defined as follows:
+ * 0x0000 - 0x0046 = configuration registers
+ * 0x8000 - 0x8046 = configuration EEPROM
+ * 0x8100 - 0x82FF = user EEPROM
+
+ This driver makes the user EEPROM available for read.
+
+ The registers & config EEPROM should be accessed via i2c-dev.
+
+ The MAX6875 ignores the lowest address bit, so each chip responds to
+ two addresses - 0x50/0x51 and 0x52/0x53.
+
+ Note that the MAX6875 uses i2c_smbus_write_byte_data() to set the read
+ address, so this driver is destructive if loaded for the wrong EEPROM chip.
+
+ 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; version 2 of the License.
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+/* Do not scan - the MAX6875 access method will write to some EEPROM chips */
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_1(max6875);
+
+/* The MAX6875 can only read/write 16 bytes at a time */
+#define SLICE_SIZE 16
+#define SLICE_BITS 4
+
+/* USER EEPROM is at addresses 0x8100 - 0x82FF */
+#define USER_EEPROM_BASE 0x8100
+#define USER_EEPROM_SIZE 0x0200
+#define USER_EEPROM_SLICES 32
+
+/* MAX6875 commands */
+#define MAX6875_CMD_BLK_READ 0x84
+
+/* Each client has this additional data */
+struct max6875_data {
+ struct i2c_client *fake_client;
+ struct mutex update_lock;
+
+ u32 valid;
+ u8 data[USER_EEPROM_SIZE];
+ unsigned long last_updated[USER_EEPROM_SLICES];
+};
+
+static void max6875_update_slice(struct i2c_client *client, int slice)
+{
+ struct max6875_data *data = i2c_get_clientdata(client);
+ int i, j, addr;
+ u8 *buf;
+
+ if (slice >= USER_EEPROM_SLICES)
+ return;
+
+ mutex_lock(&data->update_lock);
+
+ buf = &data->data[slice << SLICE_BITS];
+
+ if (!(data->valid & (1 << slice)) ||
+ time_after(jiffies, data->last_updated[slice])) {
+
+ dev_dbg(&client->dev, "Starting update of slice %u\n", slice);
+
+ data->valid &= ~(1 << slice);
+
+ addr = USER_EEPROM_BASE + (slice << SLICE_BITS);
+
+ /* select the eeprom address */
+ if (i2c_smbus_write_byte_data(client, addr >> 8, addr & 0xFF)) {
+ dev_err(&client->dev, "address set failed\n");
+ goto exit_up;
+ }
+
+ if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ if (i2c_smbus_read_i2c_block_data(client,
+ MAX6875_CMD_BLK_READ,
+ SLICE_SIZE,
+ buf) != SLICE_SIZE) {
+ goto exit_up;
+ }
+ } else {
+ for (i = 0; i < SLICE_SIZE; i++) {
+ j = i2c_smbus_read_byte(client);
+ if (j < 0) {
+ goto exit_up;
+ }
+ buf[i] = j;
+ }
+ }
+ data->last_updated[slice] = jiffies;
+ data->valid |= (1 << slice);
+ }
+exit_up:
+ mutex_unlock(&data->update_lock);
+}
+
+static ssize_t max6875_read(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct i2c_client *client = kobj_to_i2c_client(kobj);
+ struct max6875_data *data = i2c_get_clientdata(client);
+ int slice, max_slice;
+
+ if (off > USER_EEPROM_SIZE)
+ return 0;
+
+ if (off + count > USER_EEPROM_SIZE)
+ count = USER_EEPROM_SIZE - off;
+
+ /* refresh slices which contain requested bytes */
+ max_slice = (off + count - 1) >> SLICE_BITS;
+ for (slice = (off >> SLICE_BITS); slice <= max_slice; slice++)
+ max6875_update_slice(client, slice);
+
+ memcpy(buf, &data->data[off], count);
+
+ return count;
+}
+
+static struct bin_attribute user_eeprom_attr = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IRUGO,
+ },
+ .size = USER_EEPROM_SIZE,
+ .read = max6875_read,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max6875_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA
+ | I2C_FUNC_SMBUS_READ_BYTE))
+ return -ENODEV;
+
+ /* Only check even addresses */
+ if (client->addr & 1)
+ return -ENODEV;
+
+ strlcpy(info->type, "max6875", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int max6875_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max6875_data *data;
+ int err;
+
+ if (!(data = kzalloc(sizeof(struct max6875_data), GFP_KERNEL)))
+ return -ENOMEM;
+
+ /* A fake client is created on the odd address */
+ data->fake_client = i2c_new_dummy(client->adapter, client->addr + 1);
+ if (!data->fake_client) {
+ err = -ENOMEM;
+ goto exit_kfree;
+ }
+
+ /* Init real i2c_client */
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ err = sysfs_create_bin_file(&client->dev.kobj, &user_eeprom_attr);
+ if (err)
+ goto exit_remove_fake;
+
+ return 0;
+
+exit_remove_fake:
+ i2c_unregister_device(data->fake_client);
+exit_kfree:
+ kfree(data);
+ return err;
+}
+
+static int max6875_remove(struct i2c_client *client)
+{
+ struct max6875_data *data = i2c_get_clientdata(client);
+
+ i2c_unregister_device(data->fake_client);
+
+ sysfs_remove_bin_file(&client->dev.kobj, &user_eeprom_attr);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id max6875_id[] = {
+ { "max6875", 0 },
+ { }
+};
+
+static struct i2c_driver max6875_driver = {
+ .driver = {
+ .name = "max6875",
+ },
+ .probe = max6875_probe,
+ .remove = max6875_remove,
+ .id_table = max6875_id,
+
+ .detect = max6875_detect,
+ .address_data = &addr_data,
+};
+
+static int __init max6875_init(void)
+{
+ return i2c_add_driver(&max6875_driver);
+}
+
+static void __exit max6875_exit(void)
+{
+ i2c_del_driver(&max6875_driver);
+}
+
+
+MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
+MODULE_DESCRIPTION("MAX6875 driver");
+MODULE_LICENSE("GPL");
+
+module_init(max6875_init);
+module_exit(max6875_exit);
diff --git a/drivers/i2c/chips/mcu_mpc8349emitx.c b/drivers/i2c/chips/mcu_mpc8349emitx.c
new file mode 100644
index 0000000..82a9bcb
--- /dev/null
+++ b/drivers/i2c/chips/mcu_mpc8349emitx.c
@@ -0,0 +1,209 @@
+/*
+ * Power Management and GPIO expander driver for MPC8349E-mITX-compatible MCU
+ *
+ * Copyright (c) 2008 MontaVista Software, Inc.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+
+/*
+ * I don't have specifications for the MCU firmware, I found this register
+ * and bits positions by the trial&error method.
+ */
+#define MCU_REG_CTRL 0x20
+#define MCU_CTRL_POFF 0x40
+
+#define MCU_NUM_GPIO 2
+
+struct mcu {
+ struct mutex lock;
+ struct device_node *np;
+ struct i2c_client *client;
+ struct of_gpio_chip of_gc;
+ u8 reg_ctrl;
+};
+
+static struct mcu *glob_mcu;
+
+static void mcu_power_off(void)
+{
+ struct mcu *mcu = glob_mcu;
+
+ pr_info("Sending power-off request to the MCU...\n");
+ mutex_lock(&mcu->lock);
+ i2c_smbus_write_byte_data(glob_mcu->client, MCU_REG_CTRL,
+ mcu->reg_ctrl | MCU_CTRL_POFF);
+ mutex_unlock(&mcu->lock);
+}
+
+static void mcu_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct of_gpio_chip *of_gc = to_of_gpio_chip(gc);
+ struct mcu *mcu = container_of(of_gc, struct mcu, of_gc);
+ u8 bit = 1 << (4 + gpio);
+
+ mutex_lock(&mcu->lock);
+ if (val)
+ mcu->reg_ctrl &= ~bit;
+ else
+ mcu->reg_ctrl |= bit;
+
+ i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, mcu->reg_ctrl);
+ mutex_unlock(&mcu->lock);
+}
+
+static int mcu_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ mcu_gpio_set(gc, gpio, val);
+ return 0;
+}
+
+static int mcu_gpiochip_add(struct mcu *mcu)
+{
+ struct device_node *np;
+ struct of_gpio_chip *of_gc = &mcu->of_gc;
+ struct gpio_chip *gc = &of_gc->gc;
+ int ret;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,mcu-mpc8349emitx");
+ if (!np)
+ return -ENODEV;
+
+ gc->owner = THIS_MODULE;
+ gc->label = np->full_name;
+ gc->can_sleep = 1;
+ gc->ngpio = MCU_NUM_GPIO;
+ gc->base = -1;
+ gc->set = mcu_gpio_set;
+ gc->direction_output = mcu_gpio_dir_out;
+ of_gc->gpio_cells = 2;
+ of_gc->xlate = of_gpio_simple_xlate;
+
+ np->data = of_gc;
+ mcu->np = np;
+
+ /*
+ * We don't want to lose the node, its ->data and ->full_name...
+ * So, if succeeded, we don't put the node here.
+ */
+ ret = gpiochip_add(gc);
+ if (ret)
+ of_node_put(np);
+ return ret;
+}
+
+static int mcu_gpiochip_remove(struct mcu *mcu)
+{
+ int ret;
+
+ ret = gpiochip_remove(&mcu->of_gc.gc);
+ if (ret)
+ return ret;
+ of_node_put(mcu->np);
+
+ return 0;
+}
+
+static int __devinit mcu_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mcu *mcu;
+ int ret;
+
+ mcu = kzalloc(sizeof(*mcu), GFP_KERNEL);
+ if (!mcu)
+ return -ENOMEM;
+
+ mutex_init(&mcu->lock);
+ mcu->client = client;
+ i2c_set_clientdata(client, mcu);
+
+ ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL);
+ if (ret < 0)
+ goto err;
+ mcu->reg_ctrl = ret;
+
+ ret = mcu_gpiochip_add(mcu);
+ if (ret)
+ goto err;
+
+ /* XXX: this is potentially racy, but there is no lock for ppc_md */
+ if (!ppc_md.power_off) {
+ glob_mcu = mcu;
+ ppc_md.power_off = mcu_power_off;
+ dev_info(&client->dev, "will provide power-off service\n");
+ }
+
+ return 0;
+err:
+ kfree(mcu);
+ return ret;
+}
+
+static int __devexit mcu_remove(struct i2c_client *client)
+{
+ struct mcu *mcu = i2c_get_clientdata(client);
+ int ret;
+
+ if (glob_mcu == mcu) {
+ ppc_md.power_off = NULL;
+ glob_mcu = NULL;
+ }
+
+ ret = mcu_gpiochip_remove(mcu);
+ if (ret)
+ return ret;
+ i2c_set_clientdata(client, NULL);
+ kfree(mcu);
+ return 0;
+}
+
+static const struct i2c_device_id mcu_ids[] = {
+ { "mcu-mpc8349emitx", },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mcu_ids);
+
+static struct i2c_driver mcu_driver = {
+ .driver = {
+ .name = "mcu-mpc8349emitx",
+ .owner = THIS_MODULE,
+ },
+ .probe = mcu_probe,
+ .remove = __devexit_p(mcu_remove),
+ .id_table = mcu_ids,
+};
+
+static int __init mcu_init(void)
+{
+ return i2c_add_driver(&mcu_driver);
+}
+module_init(mcu_init);
+
+static void __exit mcu_exit(void)
+{
+ i2c_del_driver(&mcu_driver);
+}
+module_exit(mcu_exit);
+
+MODULE_DESCRIPTION("Power Management and GPIO expander driver for "
+ "MPC8349E-mITX-compatible MCU");
+MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/chips/menelaus.c b/drivers/i2c/chips/menelaus.c
new file mode 100644
index 0000000..4b364ba
--- /dev/null
+++ b/drivers/i2c/chips/menelaus.c
@@ -0,0 +1,1285 @@
+/*
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Some parts based tps65010.c:
+ * Copyright (C) 2004 Texas Instruments and
+ * Copyright (C) 2004-2005 David Brownell
+ *
+ * Some parts based on tlv320aic24.c:
+ * Copyright (C) by Kai Svahn <kai.svahn@nokia.com>
+ *
+ * Changes for interrupt handling and clean-up by
+ * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com>
+ * Cleanup and generalized support for voltage setting by
+ * Juha Yrjola
+ * Added support for controlling VCORE and regulator sleep states,
+ * Amit Kucheria <amit.kucheria@nokia.com>
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/mach/irq.h>
+
+#include <mach/gpio.h>
+#include <mach/menelaus.h>
+
+#define DRIVER_NAME "menelaus"
+
+#define MENELAUS_I2C_ADDRESS 0x72
+
+#define MENELAUS_REV 0x01
+#define MENELAUS_VCORE_CTRL1 0x02
+#define MENELAUS_VCORE_CTRL2 0x03
+#define MENELAUS_VCORE_CTRL3 0x04
+#define MENELAUS_VCORE_CTRL4 0x05
+#define MENELAUS_VCORE_CTRL5 0x06
+#define MENELAUS_DCDC_CTRL1 0x07
+#define MENELAUS_DCDC_CTRL2 0x08
+#define MENELAUS_DCDC_CTRL3 0x09
+#define MENELAUS_LDO_CTRL1 0x0A
+#define MENELAUS_LDO_CTRL2 0x0B
+#define MENELAUS_LDO_CTRL3 0x0C
+#define MENELAUS_LDO_CTRL4 0x0D
+#define MENELAUS_LDO_CTRL5 0x0E
+#define MENELAUS_LDO_CTRL6 0x0F
+#define MENELAUS_LDO_CTRL7 0x10
+#define MENELAUS_LDO_CTRL8 0x11
+#define MENELAUS_SLEEP_CTRL1 0x12
+#define MENELAUS_SLEEP_CTRL2 0x13
+#define MENELAUS_DEVICE_OFF 0x14
+#define MENELAUS_OSC_CTRL 0x15
+#define MENELAUS_DETECT_CTRL 0x16
+#define MENELAUS_INT_MASK1 0x17
+#define MENELAUS_INT_MASK2 0x18
+#define MENELAUS_INT_STATUS1 0x19
+#define MENELAUS_INT_STATUS2 0x1A
+#define MENELAUS_INT_ACK1 0x1B
+#define MENELAUS_INT_ACK2 0x1C
+#define MENELAUS_GPIO_CTRL 0x1D
+#define MENELAUS_GPIO_IN 0x1E
+#define MENELAUS_GPIO_OUT 0x1F
+#define MENELAUS_BBSMS 0x20
+#define MENELAUS_RTC_CTRL 0x21
+#define MENELAUS_RTC_UPDATE 0x22
+#define MENELAUS_RTC_SEC 0x23
+#define MENELAUS_RTC_MIN 0x24
+#define MENELAUS_RTC_HR 0x25
+#define MENELAUS_RTC_DAY 0x26
+#define MENELAUS_RTC_MON 0x27
+#define MENELAUS_RTC_YR 0x28
+#define MENELAUS_RTC_WKDAY 0x29
+#define MENELAUS_RTC_AL_SEC 0x2A
+#define MENELAUS_RTC_AL_MIN 0x2B
+#define MENELAUS_RTC_AL_HR 0x2C
+#define MENELAUS_RTC_AL_DAY 0x2D
+#define MENELAUS_RTC_AL_MON 0x2E
+#define MENELAUS_RTC_AL_YR 0x2F
+#define MENELAUS_RTC_COMP_MSB 0x30
+#define MENELAUS_RTC_COMP_LSB 0x31
+#define MENELAUS_S1_PULL_EN 0x32
+#define MENELAUS_S1_PULL_DIR 0x33
+#define MENELAUS_S2_PULL_EN 0x34
+#define MENELAUS_S2_PULL_DIR 0x35
+#define MENELAUS_MCT_CTRL1 0x36
+#define MENELAUS_MCT_CTRL2 0x37
+#define MENELAUS_MCT_CTRL3 0x38
+#define MENELAUS_MCT_PIN_ST 0x39
+#define MENELAUS_DEBOUNCE1 0x3A
+
+#define IH_MENELAUS_IRQS 12
+#define MENELAUS_MMC_S1CD_IRQ 0 /* MMC slot 1 card change */
+#define MENELAUS_MMC_S2CD_IRQ 1 /* MMC slot 2 card change */
+#define MENELAUS_MMC_S1D1_IRQ 2 /* MMC DAT1 low in slot 1 */
+#define MENELAUS_MMC_S2D1_IRQ 3 /* MMC DAT1 low in slot 2 */
+#define MENELAUS_LOWBAT_IRQ 4 /* Low battery */
+#define MENELAUS_HOTDIE_IRQ 5 /* Hot die detect */
+#define MENELAUS_UVLO_IRQ 6 /* UVLO detect */
+#define MENELAUS_TSHUT_IRQ 7 /* Thermal shutdown */
+#define MENELAUS_RTCTMR_IRQ 8 /* RTC timer */
+#define MENELAUS_RTCALM_IRQ 9 /* RTC alarm */
+#define MENELAUS_RTCERR_IRQ 10 /* RTC error */
+#define MENELAUS_PSHBTN_IRQ 11 /* Push button */
+#define MENELAUS_RESERVED12_IRQ 12 /* Reserved */
+#define MENELAUS_RESERVED13_IRQ 13 /* Reserved */
+#define MENELAUS_RESERVED14_IRQ 14 /* Reserved */
+#define MENELAUS_RESERVED15_IRQ 15 /* Reserved */
+
+static void menelaus_work(struct work_struct *_menelaus);
+
+struct menelaus_chip {
+ struct mutex lock;
+ struct i2c_client *client;
+ struct work_struct work;
+#ifdef CONFIG_RTC_DRV_TWL92330
+ struct rtc_device *rtc;
+ u8 rtc_control;
+ unsigned uie:1;
+#endif
+ unsigned vcore_hw_mode:1;
+ u8 mask1, mask2;
+ void (*handlers[16])(struct menelaus_chip *);
+ void (*mmc_callback)(void *data, u8 mask);
+ void *mmc_callback_data;
+};
+
+static struct menelaus_chip *the_menelaus;
+
+static int menelaus_write_reg(int reg, u8 value)
+{
+ int val = i2c_smbus_write_byte_data(the_menelaus->client, reg, value);
+
+ if (val < 0) {
+ pr_err(DRIVER_NAME ": write error");
+ return val;
+ }
+
+ return 0;
+}
+
+static int menelaus_read_reg(int reg)
+{
+ int val = i2c_smbus_read_byte_data(the_menelaus->client, reg);
+
+ if (val < 0)
+ pr_err(DRIVER_NAME ": read error");
+
+ return val;
+}
+
+static int menelaus_enable_irq(int irq)
+{
+ if (irq > 7) {
+ irq -= 8;
+ the_menelaus->mask2 &= ~(1 << irq);
+ return menelaus_write_reg(MENELAUS_INT_MASK2,
+ the_menelaus->mask2);
+ } else {
+ the_menelaus->mask1 &= ~(1 << irq);
+ return menelaus_write_reg(MENELAUS_INT_MASK1,
+ the_menelaus->mask1);
+ }
+}
+
+static int menelaus_disable_irq(int irq)
+{
+ if (irq > 7) {
+ irq -= 8;
+ the_menelaus->mask2 |= (1 << irq);
+ return menelaus_write_reg(MENELAUS_INT_MASK2,
+ the_menelaus->mask2);
+ } else {
+ the_menelaus->mask1 |= (1 << irq);
+ return menelaus_write_reg(MENELAUS_INT_MASK1,
+ the_menelaus->mask1);
+ }
+}
+
+static int menelaus_ack_irq(int irq)
+{
+ if (irq > 7)
+ return menelaus_write_reg(MENELAUS_INT_ACK2, 1 << (irq - 8));
+ else
+ return menelaus_write_reg(MENELAUS_INT_ACK1, 1 << irq);
+}
+
+/* Adds a handler for an interrupt. Does not run in interrupt context */
+static int menelaus_add_irq_work(int irq,
+ void (*handler)(struct menelaus_chip *))
+{
+ int ret = 0;
+
+ mutex_lock(&the_menelaus->lock);
+ the_menelaus->handlers[irq] = handler;
+ ret = menelaus_enable_irq(irq);
+ mutex_unlock(&the_menelaus->lock);
+
+ return ret;
+}
+
+/* Removes handler for an interrupt */
+static int menelaus_remove_irq_work(int irq)
+{
+ int ret = 0;
+
+ mutex_lock(&the_menelaus->lock);
+ ret = menelaus_disable_irq(irq);
+ the_menelaus->handlers[irq] = NULL;
+ mutex_unlock(&the_menelaus->lock);
+
+ return ret;
+}
+
+/*
+ * Gets scheduled when a card detect interrupt happens. Note that in some cases
+ * this line is wired to card cover switch rather than the card detect switch
+ * in each slot. In this case the cards are not seen by menelaus.
+ * FIXME: Add handling for D1 too
+ */
+static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw)
+{
+ int reg;
+ unsigned char card_mask = 0;
+
+ reg = menelaus_read_reg(MENELAUS_MCT_PIN_ST);
+ if (reg < 0)
+ return;
+
+ if (!(reg & 0x1))
+ card_mask |= (1 << 0);
+
+ if (!(reg & 0x2))
+ card_mask |= (1 << 1);
+
+ if (menelaus_hw->mmc_callback)
+ menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
+ card_mask);
+}
+
+/*
+ * Toggles the MMC slots between open-drain and push-pull mode.
+ */
+int menelaus_set_mmc_opendrain(int slot, int enable)
+{
+ int ret, val;
+
+ if (slot != 1 && slot != 2)
+ return -EINVAL;
+ mutex_lock(&the_menelaus->lock);
+ ret = menelaus_read_reg(MENELAUS_MCT_CTRL1);
+ if (ret < 0) {
+ mutex_unlock(&the_menelaus->lock);
+ return ret;
+ }
+ val = ret;
+ if (slot == 1) {
+ if (enable)
+ val |= 1 << 2;
+ else
+ val &= ~(1 << 2);
+ } else {
+ if (enable)
+ val |= 1 << 3;
+ else
+ val &= ~(1 << 3);
+ }
+ ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
+ mutex_unlock(&the_menelaus->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(menelaus_set_mmc_opendrain);
+
+int menelaus_set_slot_sel(int enable)
+{
+ int ret;
+
+ mutex_lock(&the_menelaus->lock);
+ ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
+ if (ret < 0)
+ goto out;
+ ret |= 0x02;
+ if (enable)
+ ret |= 1 << 5;
+ else
+ ret &= ~(1 << 5);
+ ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
+out:
+ mutex_unlock(&the_menelaus->lock);
+ return ret;
+}
+EXPORT_SYMBOL(menelaus_set_slot_sel);
+
+int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
+{
+ int ret, val;
+
+ if (slot != 1 && slot != 2)
+ return -EINVAL;
+ if (power >= 3)
+ return -EINVAL;
+
+ mutex_lock(&the_menelaus->lock);
+
+ ret = menelaus_read_reg(MENELAUS_MCT_CTRL2);
+ if (ret < 0)
+ goto out;
+ val = ret;
+ if (slot == 1) {
+ if (cd_en)
+ val |= (1 << 4) | (1 << 6);
+ else
+ val &= ~((1 << 4) | (1 << 6));
+ } else {
+ if (cd_en)
+ val |= (1 << 5) | (1 << 7);
+ else
+ val &= ~((1 << 5) | (1 << 7));
+ }
+ ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val);
+ if (ret < 0)
+ goto out;
+
+ ret = menelaus_read_reg(MENELAUS_MCT_CTRL3);
+ if (ret < 0)
+ goto out;
+ val = ret;
+ if (slot == 1) {
+ if (enable)
+ val |= 1 << 0;
+ else
+ val &= ~(1 << 0);
+ } else {
+ int b;
+
+ if (enable)
+ ret |= 1 << 1;
+ else
+ ret &= ~(1 << 1);
+ b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
+ b &= ~0x03;
+ b |= power;
+ ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b);
+ if (ret < 0)
+ goto out;
+ }
+ /* Disable autonomous shutdown */
+ val &= ~(0x03 << 2);
+ ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
+out:
+ mutex_unlock(&the_menelaus->lock);
+ return ret;
+}
+EXPORT_SYMBOL(menelaus_set_mmc_slot);
+
+int menelaus_register_mmc_callback(void (*callback)(void *data, u8 card_mask),
+ void *data)
+{
+ int ret = 0;
+
+ the_menelaus->mmc_callback_data = data;
+ the_menelaus->mmc_callback = callback;
+ ret = menelaus_add_irq_work(MENELAUS_MMC_S1CD_IRQ,
+ menelaus_mmc_cd_work);
+ if (ret < 0)
+ return ret;
+ ret = menelaus_add_irq_work(MENELAUS_MMC_S2CD_IRQ,
+ menelaus_mmc_cd_work);
+ if (ret < 0)
+ return ret;
+ ret = menelaus_add_irq_work(MENELAUS_MMC_S1D1_IRQ,
+ menelaus_mmc_cd_work);
+ if (ret < 0)
+ return ret;
+ ret = menelaus_add_irq_work(MENELAUS_MMC_S2D1_IRQ,
+ menelaus_mmc_cd_work);
+
+ return ret;
+}
+EXPORT_SYMBOL(menelaus_register_mmc_callback);
+
+void menelaus_unregister_mmc_callback(void)
+{
+ menelaus_remove_irq_work(MENELAUS_MMC_S1CD_IRQ);
+ menelaus_remove_irq_work(MENELAUS_MMC_S2CD_IRQ);
+ menelaus_remove_irq_work(MENELAUS_MMC_S1D1_IRQ);
+ menelaus_remove_irq_work(MENELAUS_MMC_S2D1_IRQ);
+
+ the_menelaus->mmc_callback = NULL;
+ the_menelaus->mmc_callback_data = 0;
+}
+EXPORT_SYMBOL(menelaus_unregister_mmc_callback);
+
+struct menelaus_vtg {
+ const char *name;
+ u8 vtg_reg;
+ u8 vtg_shift;
+ u8 vtg_bits;
+ u8 mode_reg;
+};
+
+struct menelaus_vtg_value {
+ u16 vtg;
+ u16 val;
+};
+
+static int menelaus_set_voltage(const struct menelaus_vtg *vtg, int mV,
+ int vtg_val, int mode)
+{
+ int val, ret;
+ struct i2c_client *c = the_menelaus->client;
+
+ mutex_lock(&the_menelaus->lock);
+ if (vtg == 0)
+ goto set_voltage;
+
+ ret = menelaus_read_reg(vtg->vtg_reg);
+ if (ret < 0)
+ goto out;
+ val = ret & ~(((1 << vtg->vtg_bits) - 1) << vtg->vtg_shift);
+ val |= vtg_val << vtg->vtg_shift;
+
+ dev_dbg(&c->dev, "Setting voltage '%s'"
+ "to %d mV (reg 0x%02x, val 0x%02x)\n",
+ vtg->name, mV, vtg->vtg_reg, val);
+
+ ret = menelaus_write_reg(vtg->vtg_reg, val);
+ if (ret < 0)
+ goto out;
+set_voltage:
+ ret = menelaus_write_reg(vtg->mode_reg, mode);
+out:
+ mutex_unlock(&the_menelaus->lock);
+ if (ret == 0) {
+ /* Wait for voltage to stabilize */
+ msleep(1);
+ }
+ return ret;
+}
+
+static int menelaus_get_vtg_value(int vtg, const struct menelaus_vtg_value *tbl,
+ int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++, tbl++)
+ if (tbl->vtg == vtg)
+ return tbl->val;
+ return -EINVAL;
+}
+
+/*
+ * Vcore can be programmed in two ways:
+ * SW-controlled: Required voltage is programmed into VCORE_CTRL1
+ * HW-controlled: Required range (roof-floor) is programmed into VCORE_CTRL3
+ * and VCORE_CTRL4
+ *
+ * Call correct 'set' function accordingly
+ */
+
+static const struct menelaus_vtg_value vcore_values[] = {
+ { 1000, 0 },
+ { 1025, 1 },
+ { 1050, 2 },
+ { 1075, 3 },
+ { 1100, 4 },
+ { 1125, 5 },
+ { 1150, 6 },
+ { 1175, 7 },
+ { 1200, 8 },
+ { 1225, 9 },
+ { 1250, 10 },
+ { 1275, 11 },
+ { 1300, 12 },
+ { 1325, 13 },
+ { 1350, 14 },
+ { 1375, 15 },
+ { 1400, 16 },
+ { 1425, 17 },
+ { 1450, 18 },
+};
+
+int menelaus_set_vcore_sw(unsigned int mV)
+{
+ int val, ret;
+ struct i2c_client *c = the_menelaus->client;
+
+ val = menelaus_get_vtg_value(mV, vcore_values,
+ ARRAY_SIZE(vcore_values));
+ if (val < 0)
+ return -EINVAL;
+
+ dev_dbg(&c->dev, "Setting VCORE to %d mV (val 0x%02x)\n", mV, val);
+
+ /* Set SW mode and the voltage in one go. */
+ mutex_lock(&the_menelaus->lock);
+ ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
+ if (ret == 0)
+ the_menelaus->vcore_hw_mode = 0;
+ mutex_unlock(&the_menelaus->lock);
+ msleep(1);
+
+ return ret;
+}
+
+int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV)
+{
+ int fval, rval, val, ret;
+ struct i2c_client *c = the_menelaus->client;
+
+ rval = menelaus_get_vtg_value(roof_mV, vcore_values,
+ ARRAY_SIZE(vcore_values));
+ if (rval < 0)
+ return -EINVAL;
+ fval = menelaus_get_vtg_value(floor_mV, vcore_values,
+ ARRAY_SIZE(vcore_values));
+ if (fval < 0)
+ return -EINVAL;
+
+ dev_dbg(&c->dev, "Setting VCORE FLOOR to %d mV and ROOF to %d mV\n",
+ floor_mV, roof_mV);
+
+ mutex_lock(&the_menelaus->lock);
+ ret = menelaus_write_reg(MENELAUS_VCORE_CTRL3, fval);
+ if (ret < 0)
+ goto out;
+ ret = menelaus_write_reg(MENELAUS_VCORE_CTRL4, rval);
+ if (ret < 0)
+ goto out;
+ if (!the_menelaus->vcore_hw_mode) {
+ val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
+ /* HW mode, turn OFF byte comparator */
+ val |= ((1 << 7) | (1 << 5));
+ ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
+ the_menelaus->vcore_hw_mode = 1;
+ }
+ msleep(1);
+out:
+ mutex_unlock(&the_menelaus->lock);
+ return ret;
+}
+
+static const struct menelaus_vtg vmem_vtg = {
+ .name = "VMEM",
+ .vtg_reg = MENELAUS_LDO_CTRL1,
+ .vtg_shift = 0,
+ .vtg_bits = 2,
+ .mode_reg = MENELAUS_LDO_CTRL3,
+};
+
+static const struct menelaus_vtg_value vmem_values[] = {
+ { 1500, 0 },
+ { 1800, 1 },
+ { 1900, 2 },
+ { 2500, 3 },
+};
+
+int menelaus_set_vmem(unsigned int mV)
+{
+ int val;
+
+ if (mV == 0)
+ return menelaus_set_voltage(&vmem_vtg, 0, 0, 0);
+
+ val = menelaus_get_vtg_value(mV, vmem_values, ARRAY_SIZE(vmem_values));
+ if (val < 0)
+ return -EINVAL;
+ return menelaus_set_voltage(&vmem_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vmem);
+
+static const struct menelaus_vtg vio_vtg = {
+ .name = "VIO",
+ .vtg_reg = MENELAUS_LDO_CTRL1,
+ .vtg_shift = 2,
+ .vtg_bits = 2,
+ .mode_reg = MENELAUS_LDO_CTRL4,
+};
+
+static const struct menelaus_vtg_value vio_values[] = {
+ { 1500, 0 },
+ { 1800, 1 },
+ { 2500, 2 },
+ { 2800, 3 },
+};
+
+int menelaus_set_vio(unsigned int mV)
+{
+ int val;
+
+ if (mV == 0)
+ return menelaus_set_voltage(&vio_vtg, 0, 0, 0);
+
+ val = menelaus_get_vtg_value(mV, vio_values, ARRAY_SIZE(vio_values));
+ if (val < 0)
+ return -EINVAL;
+ return menelaus_set_voltage(&vio_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vio);
+
+static const struct menelaus_vtg_value vdcdc_values[] = {
+ { 1500, 0 },
+ { 1800, 1 },
+ { 2000, 2 },
+ { 2200, 3 },
+ { 2400, 4 },
+ { 2800, 5 },
+ { 3000, 6 },
+ { 3300, 7 },
+};
+
+static const struct menelaus_vtg vdcdc2_vtg = {
+ .name = "VDCDC2",
+ .vtg_reg = MENELAUS_DCDC_CTRL1,
+ .vtg_shift = 0,
+ .vtg_bits = 3,
+ .mode_reg = MENELAUS_DCDC_CTRL2,
+};
+
+static const struct menelaus_vtg vdcdc3_vtg = {
+ .name = "VDCDC3",
+ .vtg_reg = MENELAUS_DCDC_CTRL1,
+ .vtg_shift = 3,
+ .vtg_bits = 3,
+ .mode_reg = MENELAUS_DCDC_CTRL3,
+};
+
+int menelaus_set_vdcdc(int dcdc, unsigned int mV)
+{
+ const struct menelaus_vtg *vtg;
+ int val;
+
+ if (dcdc != 2 && dcdc != 3)
+ return -EINVAL;
+ if (dcdc == 2)
+ vtg = &vdcdc2_vtg;
+ else
+ vtg = &vdcdc3_vtg;
+
+ if (mV == 0)
+ return menelaus_set_voltage(vtg, 0, 0, 0);
+
+ val = menelaus_get_vtg_value(mV, vdcdc_values,
+ ARRAY_SIZE(vdcdc_values));
+ if (val < 0)
+ return -EINVAL;
+ return menelaus_set_voltage(vtg, mV, val, 0x03);
+}
+
+static const struct menelaus_vtg_value vmmc_values[] = {
+ { 1850, 0 },
+ { 2800, 1 },
+ { 3000, 2 },
+ { 3100, 3 },
+};
+
+static const struct menelaus_vtg vmmc_vtg = {
+ .name = "VMMC",
+ .vtg_reg = MENELAUS_LDO_CTRL1,
+ .vtg_shift = 6,
+ .vtg_bits = 2,
+ .mode_reg = MENELAUS_LDO_CTRL7,
+};
+
+int menelaus_set_vmmc(unsigned int mV)
+{
+ int val;
+
+ if (mV == 0)
+ return menelaus_set_voltage(&vmmc_vtg, 0, 0, 0);
+
+ val = menelaus_get_vtg_value(mV, vmmc_values, ARRAY_SIZE(vmmc_values));
+ if (val < 0)
+ return -EINVAL;
+ return menelaus_set_voltage(&vmmc_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vmmc);
+
+
+static const struct menelaus_vtg_value vaux_values[] = {
+ { 1500, 0 },
+ { 1800, 1 },
+ { 2500, 2 },
+ { 2800, 3 },
+};
+
+static const struct menelaus_vtg vaux_vtg = {
+ .name = "VAUX",
+ .vtg_reg = MENELAUS_LDO_CTRL1,
+ .vtg_shift = 4,
+ .vtg_bits = 2,
+ .mode_reg = MENELAUS_LDO_CTRL6,
+};
+
+int menelaus_set_vaux(unsigned int mV)
+{
+ int val;
+
+ if (mV == 0)
+ return menelaus_set_voltage(&vaux_vtg, 0, 0, 0);
+
+ val = menelaus_get_vtg_value(mV, vaux_values, ARRAY_SIZE(vaux_values));
+ if (val < 0)
+ return -EINVAL;
+ return menelaus_set_voltage(&vaux_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vaux);
+
+int menelaus_get_slot_pin_states(void)
+{
+ return menelaus_read_reg(MENELAUS_MCT_PIN_ST);
+}
+EXPORT_SYMBOL(menelaus_get_slot_pin_states);
+
+int menelaus_set_regulator_sleep(int enable, u32 val)
+{
+ int t, ret;
+ struct i2c_client *c = the_menelaus->client;
+
+ mutex_lock(&the_menelaus->lock);
+ ret = menelaus_write_reg(MENELAUS_SLEEP_CTRL2, val);
+ if (ret < 0)
+ goto out;
+
+ dev_dbg(&c->dev, "regulator sleep configuration: %02x\n", val);
+
+ ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
+ if (ret < 0)
+ goto out;
+ t = ((1 << 6) | 0x04);
+ if (enable)
+ ret |= t;
+ else
+ ret &= ~t;
+ ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
+out:
+ mutex_unlock(&the_menelaus->lock);
+ return ret;
+}
+
+/*-----------------------------------------------------------------------*/
+
+/* Handles Menelaus interrupts. Does not run in interrupt context */
+static void menelaus_work(struct work_struct *_menelaus)
+{
+ struct menelaus_chip *menelaus =
+ container_of(_menelaus, struct menelaus_chip, work);
+ void (*handler)(struct menelaus_chip *menelaus);
+
+ while (1) {
+ unsigned isr;
+
+ isr = (menelaus_read_reg(MENELAUS_INT_STATUS2)
+ & ~menelaus->mask2) << 8;
+ isr |= menelaus_read_reg(MENELAUS_INT_STATUS1)
+ & ~menelaus->mask1;
+ if (!isr)
+ break;
+
+ while (isr) {
+ int irq = fls(isr) - 1;
+ isr &= ~(1 << irq);
+
+ mutex_lock(&menelaus->lock);
+ menelaus_disable_irq(irq);
+ menelaus_ack_irq(irq);
+ handler = menelaus->handlers[irq];
+ if (handler)
+ handler(menelaus);
+ menelaus_enable_irq(irq);
+ mutex_unlock(&menelaus->lock);
+ }
+ }
+ enable_irq(menelaus->client->irq);
+}
+
+/*
+ * We cannot use I2C in interrupt context, so we just schedule work.
+ */
+static irqreturn_t menelaus_irq(int irq, void *_menelaus)
+{
+ struct menelaus_chip *menelaus = _menelaus;
+
+ disable_irq_nosync(irq);
+ (void)schedule_work(&menelaus->work);
+
+ return IRQ_HANDLED;
+}
+
+/*-----------------------------------------------------------------------*/
+
+/*
+ * The RTC needs to be set once, then it runs on backup battery power.
+ * It supports alarms, including system wake alarms (from some modes);
+ * and 1/second IRQs if requested.
+ */
+#ifdef CONFIG_RTC_DRV_TWL92330
+
+#define RTC_CTRL_RTC_EN (1 << 0)
+#define RTC_CTRL_AL_EN (1 << 1)
+#define RTC_CTRL_MODE12 (1 << 2)
+#define RTC_CTRL_EVERY_MASK (3 << 3)
+#define RTC_CTRL_EVERY_SEC (0 << 3)
+#define RTC_CTRL_EVERY_MIN (1 << 3)
+#define RTC_CTRL_EVERY_HR (2 << 3)
+#define RTC_CTRL_EVERY_DAY (3 << 3)
+
+#define RTC_UPDATE_EVERY 0x08
+
+#define RTC_HR_PM (1 << 7)
+
+static void menelaus_to_time(char *regs, struct rtc_time *t)
+{
+ t->tm_sec = bcd2bin(regs[0]);
+ t->tm_min = bcd2bin(regs[1]);
+ if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
+ t->tm_hour = bcd2bin(regs[2] & 0x1f) - 1;
+ if (regs[2] & RTC_HR_PM)
+ t->tm_hour += 12;
+ } else
+ t->tm_hour = bcd2bin(regs[2] & 0x3f);
+ t->tm_mday = bcd2bin(regs[3]);
+ t->tm_mon = bcd2bin(regs[4]) - 1;
+ t->tm_year = bcd2bin(regs[5]) + 100;
+}
+
+static int time_to_menelaus(struct rtc_time *t, int regnum)
+{
+ int hour, status;
+
+ status = menelaus_write_reg(regnum++, bin2bcd(t->tm_sec));
+ if (status < 0)
+ goto fail;
+
+ status = menelaus_write_reg(regnum++, bin2bcd(t->tm_min));
+ if (status < 0)
+ goto fail;
+
+ if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
+ hour = t->tm_hour + 1;
+ if (hour > 12)
+ hour = RTC_HR_PM | bin2bcd(hour - 12);
+ else
+ hour = bin2bcd(hour);
+ } else
+ hour = bin2bcd(t->tm_hour);
+ status = menelaus_write_reg(regnum++, hour);
+ if (status < 0)
+ goto fail;
+
+ status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mday));
+ if (status < 0)
+ goto fail;
+
+ status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mon + 1));
+ if (status < 0)
+ goto fail;
+
+ status = menelaus_write_reg(regnum++, bin2bcd(t->tm_year - 100));
+ if (status < 0)
+ goto fail;
+
+ return 0;
+fail:
+ dev_err(&the_menelaus->client->dev, "rtc write reg %02x, err %d\n",
+ --regnum, status);
+ return status;
+}
+
+static int menelaus_read_time(struct device *dev, struct rtc_time *t)
+{
+ struct i2c_msg msg[2];
+ char regs[7];
+ int status;
+
+ /* block read date and time registers */
+ regs[0] = MENELAUS_RTC_SEC;
+
+ msg[0].addr = MENELAUS_I2C_ADDRESS;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = regs;
+
+ msg[1].addr = MENELAUS_I2C_ADDRESS;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = sizeof(regs);
+ msg[1].buf = regs;
+
+ status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
+ if (status != 2) {
+ dev_err(dev, "%s error %d\n", "read", status);
+ return -EIO;
+ }
+
+ menelaus_to_time(regs, t);
+ t->tm_wday = bcd2bin(regs[6]);
+
+ return 0;
+}
+
+static int menelaus_set_time(struct device *dev, struct rtc_time *t)
+{
+ int status;
+
+ /* write date and time registers */
+ status = time_to_menelaus(t, MENELAUS_RTC_SEC);
+ if (status < 0)
+ return status;
+ status = menelaus_write_reg(MENELAUS_RTC_WKDAY, bin2bcd(t->tm_wday));
+ if (status < 0) {
+ dev_err(&the_menelaus->client->dev, "rtc write reg %02x "
+ "err %d\n", MENELAUS_RTC_WKDAY, status);
+ return status;
+ }
+
+ /* now commit the write */
+ status = menelaus_write_reg(MENELAUS_RTC_UPDATE, RTC_UPDATE_EVERY);
+ if (status < 0)
+ dev_err(&the_menelaus->client->dev, "rtc commit time, err %d\n",
+ status);
+
+ return 0;
+}
+
+static int menelaus_read_alarm(struct device *dev, struct rtc_wkalrm *w)
+{
+ struct i2c_msg msg[2];
+ char regs[6];
+ int status;
+
+ /* block read alarm registers */
+ regs[0] = MENELAUS_RTC_AL_SEC;
+
+ msg[0].addr = MENELAUS_I2C_ADDRESS;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = regs;
+
+ msg[1].addr = MENELAUS_I2C_ADDRESS;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = sizeof(regs);
+ msg[1].buf = regs;
+
+ status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
+ if (status != 2) {
+ dev_err(dev, "%s error %d\n", "alarm read", status);
+ return -EIO;
+ }
+
+ menelaus_to_time(regs, &w->time);
+
+ w->enabled = !!(the_menelaus->rtc_control & RTC_CTRL_AL_EN);
+
+ /* NOTE we *could* check if actually pending... */
+ w->pending = 0;
+
+ return 0;
+}
+
+static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w)
+{
+ int status;
+
+ if (the_menelaus->client->irq <= 0 && w->enabled)
+ return -ENODEV;
+
+ /* clear previous alarm enable */
+ if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) {
+ the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
+ status = menelaus_write_reg(MENELAUS_RTC_CTRL,
+ the_menelaus->rtc_control);
+ if (status < 0)
+ return status;
+ }
+
+ /* write alarm registers */
+ status = time_to_menelaus(&w->time, MENELAUS_RTC_AL_SEC);
+ if (status < 0)
+ return status;
+
+ /* enable alarm if requested */
+ if (w->enabled) {
+ the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
+ status = menelaus_write_reg(MENELAUS_RTC_CTRL,
+ the_menelaus->rtc_control);
+ }
+
+ return status;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+
+static void menelaus_rtc_update_work(struct menelaus_chip *m)
+{
+ /* report 1/sec update */
+ local_irq_disable();
+ rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF);
+ local_irq_enable();
+}
+
+static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg)
+{
+ int status;
+
+ if (the_menelaus->client->irq <= 0)
+ return -ENOIOCTLCMD;
+
+ switch (cmd) {
+ /* alarm IRQ */
+ case RTC_AIE_ON:
+ if (the_menelaus->rtc_control & RTC_CTRL_AL_EN)
+ return 0;
+ the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
+ break;
+ case RTC_AIE_OFF:
+ if (!(the_menelaus->rtc_control & RTC_CTRL_AL_EN))
+ return 0;
+ the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
+ break;
+ /* 1/second "update" IRQ */
+ case RTC_UIE_ON:
+ if (the_menelaus->uie)
+ return 0;
+ status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
+ status = menelaus_add_irq_work(MENELAUS_RTCTMR_IRQ,
+ menelaus_rtc_update_work);
+ if (status == 0)
+ the_menelaus->uie = 1;
+ return status;
+ case RTC_UIE_OFF:
+ if (!the_menelaus->uie)
+ return 0;
+ status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
+ if (status == 0)
+ the_menelaus->uie = 0;
+ return status;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
+}
+
+#else
+#define menelaus_ioctl NULL
+#endif
+
+/* REVISIT no compensation register support ... */
+
+static const struct rtc_class_ops menelaus_rtc_ops = {
+ .ioctl = menelaus_ioctl,
+ .read_time = menelaus_read_time,
+ .set_time = menelaus_set_time,
+ .read_alarm = menelaus_read_alarm,
+ .set_alarm = menelaus_set_alarm,
+};
+
+static void menelaus_rtc_alarm_work(struct menelaus_chip *m)
+{
+ /* report alarm */
+ local_irq_disable();
+ rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF);
+ local_irq_enable();
+
+ /* then disable it; alarms are oneshot */
+ the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
+ menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
+}
+
+static inline void menelaus_rtc_init(struct menelaus_chip *m)
+{
+ int alarm = (m->client->irq > 0);
+
+ /* assume 32KDETEN pin is pulled high */
+ if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) {
+ dev_dbg(&m->client->dev, "no 32k oscillator\n");
+ return;
+ }
+
+ /* support RTC alarm; it can issue wakeups */
+ if (alarm) {
+ if (menelaus_add_irq_work(MENELAUS_RTCALM_IRQ,
+ menelaus_rtc_alarm_work) < 0) {
+ dev_err(&m->client->dev, "can't handle RTC alarm\n");
+ return;
+ }
+ device_init_wakeup(&m->client->dev, 1);
+ }
+
+ /* be sure RTC is enabled; allow 1/sec irqs; leave 12hr mode alone */
+ m->rtc_control = menelaus_read_reg(MENELAUS_RTC_CTRL);
+ if (!(m->rtc_control & RTC_CTRL_RTC_EN)
+ || (m->rtc_control & RTC_CTRL_AL_EN)
+ || (m->rtc_control & RTC_CTRL_EVERY_MASK)) {
+ if (!(m->rtc_control & RTC_CTRL_RTC_EN)) {
+ dev_warn(&m->client->dev, "rtc clock needs setting\n");
+ m->rtc_control |= RTC_CTRL_RTC_EN;
+ }
+ m->rtc_control &= ~RTC_CTRL_EVERY_MASK;
+ m->rtc_control &= ~RTC_CTRL_AL_EN;
+ menelaus_write_reg(MENELAUS_RTC_CTRL, m->rtc_control);
+ }
+
+ m->rtc = rtc_device_register(DRIVER_NAME,
+ &m->client->dev,
+ &menelaus_rtc_ops, THIS_MODULE);
+ if (IS_ERR(m->rtc)) {
+ if (alarm) {
+ menelaus_remove_irq_work(MENELAUS_RTCALM_IRQ);
+ device_init_wakeup(&m->client->dev, 0);
+ }
+ dev_err(&m->client->dev, "can't register RTC: %d\n",
+ (int) PTR_ERR(m->rtc));
+ the_menelaus->rtc = NULL;
+ }
+}
+
+#else
+
+static inline void menelaus_rtc_init(struct menelaus_chip *m)
+{
+ /* nothing */
+}
+
+#endif
+
+/*-----------------------------------------------------------------------*/
+
+static struct i2c_driver menelaus_i2c_driver;
+
+static int menelaus_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct menelaus_chip *menelaus;
+ int rev = 0, val;
+ int err = 0;
+ struct menelaus_platform_data *menelaus_pdata =
+ client->dev.platform_data;
+
+ if (the_menelaus) {
+ dev_dbg(&client->dev, "only one %s for now\n",
+ DRIVER_NAME);
+ return -ENODEV;
+ }
+
+ menelaus = kzalloc(sizeof *menelaus, GFP_KERNEL);
+ if (!menelaus)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, menelaus);
+
+ the_menelaus = menelaus;
+ menelaus->client = client;
+
+ /* If a true probe check the device */
+ rev = menelaus_read_reg(MENELAUS_REV);
+ if (rev < 0) {
+ pr_err(DRIVER_NAME ": device not found");
+ err = -ENODEV;
+ goto fail1;
+ }
+
+ /* Ack and disable all Menelaus interrupts */
+ menelaus_write_reg(MENELAUS_INT_ACK1, 0xff);
+ menelaus_write_reg(MENELAUS_INT_ACK2, 0xff);
+ menelaus_write_reg(MENELAUS_INT_MASK1, 0xff);
+ menelaus_write_reg(MENELAUS_INT_MASK2, 0xff);
+ menelaus->mask1 = 0xff;
+ menelaus->mask2 = 0xff;
+
+ /* Set output buffer strengths */
+ menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73);
+
+ if (client->irq > 0) {
+ err = request_irq(client->irq, menelaus_irq, IRQF_DISABLED,
+ DRIVER_NAME, menelaus);
+ if (err) {
+ dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
+ client->irq, err);
+ goto fail1;
+ }
+ }
+
+ mutex_init(&menelaus->lock);
+ INIT_WORK(&menelaus->work, menelaus_work);
+
+ pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f);
+
+ val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
+ if (val < 0)
+ goto fail2;
+ if (val & (1 << 7))
+ menelaus->vcore_hw_mode = 1;
+ else
+ menelaus->vcore_hw_mode = 0;
+
+ if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) {
+ err = menelaus_pdata->late_init(&client->dev);
+ if (err < 0)
+ goto fail2;
+ }
+
+ menelaus_rtc_init(menelaus);
+
+ return 0;
+fail2:
+ free_irq(client->irq, menelaus);
+ flush_scheduled_work();
+fail1:
+ kfree(menelaus);
+ return err;
+}
+
+static int __exit menelaus_remove(struct i2c_client *client)
+{
+ struct menelaus_chip *menelaus = i2c_get_clientdata(client);
+
+ free_irq(client->irq, menelaus);
+ kfree(menelaus);
+ i2c_set_clientdata(client, NULL);
+ the_menelaus = NULL;
+ return 0;
+}
+
+static const struct i2c_device_id menelaus_id[] = {
+ { "menelaus", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, menelaus_id);
+
+static struct i2c_driver menelaus_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = menelaus_probe,
+ .remove = __exit_p(menelaus_remove),
+ .id_table = menelaus_id,
+};
+
+static int __init menelaus_init(void)
+{
+ int res;
+
+ res = i2c_add_driver(&menelaus_i2c_driver);
+ if (res < 0) {
+ pr_err(DRIVER_NAME ": driver registration failed\n");
+ return res;
+ }
+
+ return 0;
+}
+
+static void __exit menelaus_exit(void)
+{
+ i2c_del_driver(&menelaus_i2c_driver);
+
+ /* FIXME: Shutdown menelaus parts that can be shut down */
+}
+
+MODULE_AUTHOR("Texas Instruments, Inc. (and others)");
+MODULE_DESCRIPTION("I2C interface for Menelaus.");
+MODULE_LICENSE("GPL");
+
+module_init(menelaus_init);
+module_exit(menelaus_exit);
diff --git a/drivers/i2c/chips/pca9539.c b/drivers/i2c/chips/pca9539.c
new file mode 100644
index 0000000..270de4e
--- /dev/null
+++ b/drivers/i2c/chips/pca9539.c
@@ -0,0 +1,152 @@
+/*
+ pca9539.c - 16-bit I/O port with interrupt and reset
+
+ Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
+
+ 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; version 2 of the License.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon-sysfs.h>
+
+/* Addresses to scan: none, device is not autodetected */
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_1(pca9539);
+
+enum pca9539_cmd
+{
+ PCA9539_INPUT_0 = 0,
+ PCA9539_INPUT_1 = 1,
+ PCA9539_OUTPUT_0 = 2,
+ PCA9539_OUTPUT_1 = 3,
+ PCA9539_INVERT_0 = 4,
+ PCA9539_INVERT_1 = 5,
+ PCA9539_DIRECTION_0 = 6,
+ PCA9539_DIRECTION_1 = 7,
+};
+
+/* following are the sysfs callback functions */
+static ssize_t pca9539_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *psa = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ return sprintf(buf, "%d\n", i2c_smbus_read_byte_data(client,
+ psa->index));
+}
+
+static ssize_t pca9539_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *psa = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned long val = simple_strtoul(buf, NULL, 0);
+ if (val > 0xff)
+ return -EINVAL;
+ i2c_smbus_write_byte_data(client, psa->index, val);
+ return count;
+}
+
+/* Define the device attributes */
+
+#define PCA9539_ENTRY_RO(name, cmd_idx) \
+ static SENSOR_DEVICE_ATTR(name, S_IRUGO, pca9539_show, NULL, cmd_idx)
+
+#define PCA9539_ENTRY_RW(name, cmd_idx) \
+ static SENSOR_DEVICE_ATTR(name, S_IRUGO | S_IWUSR, pca9539_show, \
+ pca9539_store, cmd_idx)
+
+PCA9539_ENTRY_RO(input0, PCA9539_INPUT_0);
+PCA9539_ENTRY_RO(input1, PCA9539_INPUT_1);
+PCA9539_ENTRY_RW(output0, PCA9539_OUTPUT_0);
+PCA9539_ENTRY_RW(output1, PCA9539_OUTPUT_1);
+PCA9539_ENTRY_RW(invert0, PCA9539_INVERT_0);
+PCA9539_ENTRY_RW(invert1, PCA9539_INVERT_1);
+PCA9539_ENTRY_RW(direction0, PCA9539_DIRECTION_0);
+PCA9539_ENTRY_RW(direction1, PCA9539_DIRECTION_1);
+
+static struct attribute *pca9539_attributes[] = {
+ &sensor_dev_attr_input0.dev_attr.attr,
+ &sensor_dev_attr_input1.dev_attr.attr,
+ &sensor_dev_attr_output0.dev_attr.attr,
+ &sensor_dev_attr_output1.dev_attr.attr,
+ &sensor_dev_attr_invert0.dev_attr.attr,
+ &sensor_dev_attr_invert1.dev_attr.attr,
+ &sensor_dev_attr_direction0.dev_attr.attr,
+ &sensor_dev_attr_direction1.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group pca9539_defattr_group = {
+ .attrs = pca9539_attributes,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int pca9539_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ strlcpy(info->type, "pca9539", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int pca9539_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ /* Register sysfs hooks */
+ return sysfs_create_group(&client->dev.kobj,
+ &pca9539_defattr_group);
+}
+
+static int pca9539_remove(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &pca9539_defattr_group);
+ return 0;
+}
+
+static const struct i2c_device_id pca9539_id[] = {
+ { "pca9539", 0 },
+ { }
+};
+
+static struct i2c_driver pca9539_driver = {
+ .driver = {
+ .name = "pca9539",
+ },
+ .probe = pca9539_probe,
+ .remove = pca9539_remove,
+ .id_table = pca9539_id,
+
+ .detect = pca9539_detect,
+ .address_data = &addr_data,
+};
+
+static int __init pca9539_init(void)
+{
+ return i2c_add_driver(&pca9539_driver);
+}
+
+static void __exit pca9539_exit(void)
+{
+ i2c_del_driver(&pca9539_driver);
+}
+
+MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
+MODULE_DESCRIPTION("PCA9539 driver");
+MODULE_LICENSE("GPL");
+
+module_init(pca9539_init);
+module_exit(pca9539_exit);
+
diff --git a/drivers/i2c/chips/pcf8574.c b/drivers/i2c/chips/pcf8574.c
new file mode 100644
index 0000000..6ec3098
--- /dev/null
+++ b/drivers/i2c/chips/pcf8574.c
@@ -0,0 +1,215 @@
+/*
+ Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>,
+ Philip Edelbrock <phil@netroedge.com>,
+ Dan Eaton <dan.eaton@rocketlogix.com>
+ Ported to Linux 2.6 by Aurelien Jarno <aurel32@debian.org> with
+ the help of Jean Delvare <khali@linux-fr.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* A few notes about the PCF8574:
+
+* The PCF8574 is an 8-bit I/O expander for the I2C bus produced by
+ Philips Semiconductors. It is designed to provide a byte I2C
+ interface to up to 8 separate devices.
+
+* The PCF8574 appears as a very simple SMBus device which can be
+ read from or written to with SMBUS byte read/write accesses.
+
+ --Dan
+
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+/* Addresses to scan: none, device can't be detected */
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_2(pcf8574, pcf8574a);
+
+/* Each client has this additional data */
+struct pcf8574_data {
+ int write; /* Remember last written value */
+};
+
+static void pcf8574_init_client(struct i2c_client *client);
+
+/* following are the sysfs callback functions */
+static ssize_t show_read(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ return sprintf(buf, "%u\n", i2c_smbus_read_byte(client));
+}
+
+static DEVICE_ATTR(read, S_IRUGO, show_read, NULL);
+
+static ssize_t show_write(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pcf8574_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ if (data->write < 0)
+ return data->write;
+
+ return sprintf(buf, "%d\n", data->write);
+}
+
+static ssize_t set_write(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pcf8574_data *data = i2c_get_clientdata(client);
+ unsigned long val = simple_strtoul(buf, NULL, 10);
+
+ if (val > 0xff)
+ return -EINVAL;
+
+ data->write = val;
+ i2c_smbus_write_byte(client, data->write);
+ return count;
+}
+
+static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write);
+
+static struct attribute *pcf8574_attributes[] = {
+ &dev_attr_read.attr,
+ &dev_attr_write.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8574_attr_group = {
+ .attrs = pcf8574_attributes,
+};
+
+/*
+ * Real code
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int pcf8574_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ const char *client_name;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+ return -ENODEV;
+
+ /* Now, we would do the remaining detection. But the PCF8574 is plainly
+ impossible to detect! Stupid chip. */
+
+ /* Determine the chip type */
+ if (kind <= 0) {
+ if (client->addr >= 0x38 && client->addr <= 0x3f)
+ kind = pcf8574a;
+ else
+ kind = pcf8574;
+ }
+
+ if (kind == pcf8574a)
+ client_name = "pcf8574a";
+ else
+ client_name = "pcf8574";
+ strlcpy(info->type, client_name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int pcf8574_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf8574_data *data;
+ int err;
+
+ data = kzalloc(sizeof(struct pcf8574_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(client, data);
+
+ /* Initialize the PCF8574 chip */
+ pcf8574_init_client(client);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &pcf8574_attr_group);
+ if (err)
+ goto exit_free;
+ return 0;
+
+ exit_free:
+ kfree(data);
+ exit:
+ return err;
+}
+
+static int pcf8574_remove(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &pcf8574_attr_group);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+/* Called when we have found a new PCF8574. */
+static void pcf8574_init_client(struct i2c_client *client)
+{
+ struct pcf8574_data *data = i2c_get_clientdata(client);
+ data->write = -EAGAIN;
+}
+
+static const struct i2c_device_id pcf8574_id[] = {
+ { "pcf8574", 0 },
+ { "pcf8574a", 0 },
+ { }
+};
+
+static struct i2c_driver pcf8574_driver = {
+ .driver = {
+ .name = "pcf8574",
+ },
+ .probe = pcf8574_probe,
+ .remove = pcf8574_remove,
+ .id_table = pcf8574_id,
+
+ .detect = pcf8574_detect,
+ .address_data = &addr_data,
+};
+
+static int __init pcf8574_init(void)
+{
+ return i2c_add_driver(&pcf8574_driver);
+}
+
+static void __exit pcf8574_exit(void)
+{
+ i2c_del_driver(&pcf8574_driver);
+}
+
+
+MODULE_AUTHOR
+ ("Frodo Looijaard <frodol@dds.nl>, "
+ "Philip Edelbrock <phil@netroedge.com>, "
+ "Dan Eaton <dan.eaton@rocketlogix.com> "
+ "and Aurelien Jarno <aurelien@aurel32.net>");
+MODULE_DESCRIPTION("PCF8574 driver");
+MODULE_LICENSE("GPL");
+
+module_init(pcf8574_init);
+module_exit(pcf8574_exit);
diff --git a/drivers/i2c/chips/pcf8575.c b/drivers/i2c/chips/pcf8575.c
new file mode 100644
index 0000000..07fd7cb
--- /dev/null
+++ b/drivers/i2c/chips/pcf8575.c
@@ -0,0 +1,198 @@
+/*
+ pcf8575.c
+
+ About the PCF8575 chip: the PCF8575 is a 16-bit I/O expander for the I2C bus
+ produced by a.o. Philips Semiconductors.
+
+ Copyright (C) 2006 Michael Hennerich, Analog Devices Inc.
+ <hennerich@blackfin.uclinux.org>
+ Based on pcf8574.c.
+
+ Copyright (c) 2007 Bart Van Assche <bart.vanassche@gmail.com>.
+ Ported this driver from ucLinux to the mainstream Linux kernel.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h> /* kzalloc() */
+#include <linux/sysfs.h> /* sysfs_create_group() */
+
+/* Addresses to scan: none, device can't be detected */
+static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD;
+
+
+/* Each client has this additional data */
+struct pcf8575_data {
+ int write; /* last written value, or error code */
+};
+
+/* following are the sysfs callback functions */
+static ssize_t show_read(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u16 val;
+ u8 iopin_state[2];
+
+ i2c_master_recv(client, iopin_state, 2);
+
+ val = iopin_state[0];
+ val |= iopin_state[1] << 8;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static DEVICE_ATTR(read, S_IRUGO, show_read, NULL);
+
+static ssize_t show_write(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct pcf8575_data *data = dev_get_drvdata(dev);
+ if (data->write < 0)
+ return data->write;
+ return sprintf(buf, "%d\n", data->write);
+}
+
+static ssize_t set_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pcf8575_data *data = i2c_get_clientdata(client);
+ unsigned long val = simple_strtoul(buf, NULL, 10);
+ u8 iopin_state[2];
+
+ if (val > 0xffff)
+ return -EINVAL;
+
+ data->write = val;
+
+ iopin_state[0] = val & 0xFF;
+ iopin_state[1] = val >> 8;
+
+ i2c_master_send(client, iopin_state, 2);
+
+ return count;
+}
+
+static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write);
+
+static struct attribute *pcf8575_attributes[] = {
+ &dev_attr_read.attr,
+ &dev_attr_write.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8575_attr_group = {
+ .attrs = pcf8575_attributes,
+};
+
+/*
+ * Real code
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int pcf8575_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ /* This is the place to detect whether the chip at the specified
+ address really is a PCF8575 chip. However, there is no method known
+ to detect whether an I2C chip is a PCF8575 or any other I2C chip. */
+
+ strlcpy(info->type, "pcf8575", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int pcf8575_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf8575_data *data;
+ int err;
+
+ data = kzalloc(sizeof(struct pcf8575_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(client, data);
+ data->write = -EAGAIN;
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &pcf8575_attr_group);
+ if (err)
+ goto exit_free;
+
+ return 0;
+
+exit_free:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int pcf8575_remove(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &pcf8575_attr_group);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct i2c_device_id pcf8575_id[] = {
+ { "pcf8575", 0 },
+ { }
+};
+
+static struct i2c_driver pcf8575_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "pcf8575",
+ },
+ .probe = pcf8575_probe,
+ .remove = pcf8575_remove,
+ .id_table = pcf8575_id,
+
+ .detect = pcf8575_detect,
+ .address_data = &addr_data,
+};
+
+static int __init pcf8575_init(void)
+{
+ return i2c_add_driver(&pcf8575_driver);
+}
+
+static void __exit pcf8575_exit(void)
+{
+ i2c_del_driver(&pcf8575_driver);
+}
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>, "
+ "Bart Van Assche <bart.vanassche@gmail.com>");
+MODULE_DESCRIPTION("pcf8575 driver");
+MODULE_LICENSE("GPL");
+
+module_init(pcf8575_init);
+module_exit(pcf8575_exit);
diff --git a/drivers/i2c/chips/pcf8591.c b/drivers/i2c/chips/pcf8591.c
new file mode 100644
index 0000000..16ce3e1
--- /dev/null
+++ b/drivers/i2c/chips/pcf8591.c
@@ -0,0 +1,325 @@
+/*
+ Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net>
+ Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
+ the help of Jean Delvare <khali@linux-fr.org>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
+ 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_1(pcf8591);
+
+static int input_mode;
+module_param(input_mode, int, 0);
+MODULE_PARM_DESC(input_mode,
+ "Analog input mode:\n"
+ " 0 = four single ended inputs\n"
+ " 1 = three differential inputs\n"
+ " 2 = single ended and differential mixed\n"
+ " 3 = two differential inputs\n");
+
+/* The PCF8591 control byte
+ 7 6 5 4 3 2 1 0
+ | 0 |AOEF| AIP | 0 |AINC| AICH | */
+
+/* Analog Output Enable Flag (analog output active if 1) */
+#define PCF8591_CONTROL_AOEF 0x40
+
+/* Analog Input Programming
+ 0x00 = four single ended inputs
+ 0x10 = three differential inputs
+ 0x20 = single ended and differential mixed
+ 0x30 = two differential inputs */
+#define PCF8591_CONTROL_AIP_MASK 0x30
+
+/* Autoincrement Flag (switch on if 1) */
+#define PCF8591_CONTROL_AINC 0x04
+
+/* Channel selection
+ 0x00 = channel 0
+ 0x01 = channel 1
+ 0x02 = channel 2
+ 0x03 = channel 3 */
+#define PCF8591_CONTROL_AICH_MASK 0x03
+
+/* Initial values */
+#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF)
+#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */
+
+/* Conversions */
+#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg))
+
+struct pcf8591_data {
+ struct mutex update_lock;
+
+ u8 control;
+ u8 aout;
+};
+
+static void pcf8591_init_client(struct i2c_client *client);
+static int pcf8591_read_channel(struct device *dev, int channel);
+
+/* following are the sysfs callback functions */
+#define show_in_channel(channel) \
+static ssize_t show_in##channel##_input(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\
+} \
+static DEVICE_ATTR(in##channel##_input, S_IRUGO, \
+ show_in##channel##_input, NULL);
+
+show_in_channel(0);
+show_in_channel(1);
+show_in_channel(2);
+show_in_channel(3);
+
+static ssize_t show_out0_ouput(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
+ return sprintf(buf, "%d\n", data->aout * 10);
+}
+
+static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int value;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pcf8591_data *data = i2c_get_clientdata(client);
+ if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) {
+ data->aout = value;
+ i2c_smbus_write_byte_data(client, data->control, data->aout);
+ return count;
+ }
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO,
+ show_out0_ouput, set_out0_output);
+
+static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
+ return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF)));
+}
+
+static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pcf8591_data *data = i2c_get_clientdata(client);
+ unsigned long val = simple_strtoul(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock);
+ if (val)
+ data->control |= PCF8591_CONTROL_AOEF;
+ else
+ data->control &= ~PCF8591_CONTROL_AOEF;
+ i2c_smbus_write_byte(client, data->control);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO,
+ show_out0_enable, set_out0_enable);
+
+static struct attribute *pcf8591_attributes[] = {
+ &dev_attr_out0_enable.attr,
+ &dev_attr_out0_output.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8591_attr_group = {
+ .attrs = pcf8591_attributes,
+};
+
+static struct attribute *pcf8591_attributes_opt[] = {
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8591_attr_group_opt = {
+ .attrs = pcf8591_attributes_opt,
+};
+
+/*
+ * Real code
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int pcf8591_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE
+ | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -ENODEV;
+
+ /* Now, we would do the remaining detection. But the PCF8591 is plainly
+ impossible to detect! Stupid chip. */
+
+ strlcpy(info->type, "pcf8591", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int pcf8591_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf8591_data *data;
+ int err;
+
+ if (!(data = kzalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* Initialize the PCF8591 chip */
+ pcf8591_init_client(client);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group);
+ if (err)
+ goto exit_kfree;
+
+ /* Register input2 if not in "two differential inputs" mode */
+ if (input_mode != 3) {
+ if ((err = device_create_file(&client->dev,
+ &dev_attr_in2_input)))
+ goto exit_sysfs_remove;
+ }
+
+ /* Register input3 only in "four single ended inputs" mode */
+ if (input_mode == 0) {
+ if ((err = device_create_file(&client->dev,
+ &dev_attr_in3_input)))
+ goto exit_sysfs_remove;
+ }
+
+ return 0;
+
+exit_sysfs_remove:
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt);
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group);
+exit_kfree:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int pcf8591_remove(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt);
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+/* Called when we have found a new PCF8591. */
+static void pcf8591_init_client(struct i2c_client *client)
+{
+ struct pcf8591_data *data = i2c_get_clientdata(client);
+ data->control = PCF8591_INIT_CONTROL;
+ data->aout = PCF8591_INIT_AOUT;
+
+ i2c_smbus_write_byte_data(client, data->control, data->aout);
+
+ /* The first byte transmitted contains the conversion code of the
+ previous read cycle. FLUSH IT! */
+ i2c_smbus_read_byte(client);
+}
+
+static int pcf8591_read_channel(struct device *dev, int channel)
+{
+ u8 value;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pcf8591_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+
+ if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) {
+ data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK)
+ | channel;
+ i2c_smbus_write_byte(client, data->control);
+
+ /* The first byte transmitted contains the conversion code of
+ the previous read cycle. FLUSH IT! */
+ i2c_smbus_read_byte(client);
+ }
+ value = i2c_smbus_read_byte(client);
+
+ mutex_unlock(&data->update_lock);
+
+ if ((channel == 2 && input_mode == 2) ||
+ (channel != 3 && (input_mode == 1 || input_mode == 3)))
+ return (10 * REG_TO_SIGNED(value));
+ else
+ return (10 * value);
+}
+
+static const struct i2c_device_id pcf8591_id[] = {
+ { "pcf8591", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf8591_id);
+
+static struct i2c_driver pcf8591_driver = {
+ .driver = {
+ .name = "pcf8591",
+ },
+ .probe = pcf8591_probe,
+ .remove = pcf8591_remove,
+ .id_table = pcf8591_id,
+
+ .class = I2C_CLASS_HWMON, /* Nearest choice */
+ .detect = pcf8591_detect,
+ .address_data = &addr_data,
+};
+
+static int __init pcf8591_init(void)
+{
+ if (input_mode < 0 || input_mode > 3) {
+ printk(KERN_WARNING "pcf8591: invalid input_mode (%d)\n",
+ input_mode);
+ input_mode = 0;
+ }
+ return i2c_add_driver(&pcf8591_driver);
+}
+
+static void __exit pcf8591_exit(void)
+{
+ i2c_del_driver(&pcf8591_driver);
+}
+
+MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
+MODULE_DESCRIPTION("PCF8591 driver");
+MODULE_LICENSE("GPL");
+
+module_init(pcf8591_init);
+module_exit(pcf8591_exit);
diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c
new file mode 100644
index 0000000..acf8b9d
--- /dev/null
+++ b/drivers/i2c/chips/tps65010.c
@@ -0,0 +1,1072 @@
+/*
+ * tps65010 - driver for tps6501x power management chips
+ *
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004-2005 David Brownell
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <linux/i2c/tps65010.h>
+
+#include <asm/gpio.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_VERSION "2 May 2005"
+#define DRIVER_NAME (tps65010_driver.driver.name)
+
+MODULE_DESCRIPTION("TPS6501x Power Management Driver");
+MODULE_LICENSE("GPL");
+
+static struct i2c_driver tps65010_driver;
+
+/*-------------------------------------------------------------------------*/
+
+/* This driver handles a family of multipurpose chips, which incorporate
+ * voltage regulators, lithium ion/polymer battery charging, GPIOs, LEDs,
+ * and other features often needed in portable devices like cell phones
+ * or digital cameras.
+ *
+ * The tps65011 and tps65013 have different voltage settings compared
+ * to tps65010 and tps65012. The tps65013 has a NO_CHG status/irq.
+ * All except tps65010 have "wait" mode, possibly defaulted so that
+ * battery-insert != device-on.
+ *
+ * We could distinguish between some models by checking VDCDC1.UVLO or
+ * other registers, unless they've been changed already after powerup
+ * as part of board setup by a bootloader.
+ */
+enum tps_model {
+ TPS65010,
+ TPS65011,
+ TPS65012,
+ TPS65013,
+};
+
+struct tps65010 {
+ struct i2c_client *client;
+ struct mutex lock;
+ struct delayed_work work;
+ struct dentry *file;
+ unsigned charging:1;
+ unsigned por:1;
+ unsigned model:8;
+ u16 vbus;
+ unsigned long flags;
+#define FLAG_VBUS_CHANGED 0
+#define FLAG_IRQ_ENABLE 1
+
+ /* copies of last register state */
+ u8 chgstatus, regstatus, chgconf;
+ u8 nmask1, nmask2;
+
+ u8 outmask;
+ struct gpio_chip chip;
+ struct platform_device *leds;
+};
+
+#define POWER_POLL_DELAY msecs_to_jiffies(5000)
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(DEBUG) || defined(CONFIG_DEBUG_FS)
+
+static void dbg_chgstat(char *buf, size_t len, u8 chgstatus)
+{
+ snprintf(buf, len, "%02x%s%s%s%s%s%s%s%s\n",
+ chgstatus,
+ (chgstatus & TPS_CHG_USB) ? " USB" : "",
+ (chgstatus & TPS_CHG_AC) ? " AC" : "",
+ (chgstatus & TPS_CHG_THERM) ? " therm" : "",
+ (chgstatus & TPS_CHG_TERM) ? " done" :
+ ((chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
+ ? " (charging)" : ""),
+ (chgstatus & TPS_CHG_TAPER_TMO) ? " taper_tmo" : "",
+ (chgstatus & TPS_CHG_CHG_TMO) ? " charge_tmo" : "",
+ (chgstatus & TPS_CHG_PRECHG_TMO) ? " prechg_tmo" : "",
+ (chgstatus & TPS_CHG_TEMP_ERR) ? " temp_err" : "");
+}
+
+static void dbg_regstat(char *buf, size_t len, u8 regstatus)
+{
+ snprintf(buf, len, "%02x %s%s%s%s%s%s%s%s\n",
+ regstatus,
+ (regstatus & TPS_REG_ONOFF) ? "off" : "(on)",
+ (regstatus & TPS_REG_COVER) ? " uncover" : "",
+ (regstatus & TPS_REG_UVLO) ? " UVLO" : "",
+ (regstatus & TPS_REG_NO_CHG) ? " NO_CHG" : "",
+ (regstatus & TPS_REG_PG_LD02) ? " ld02_bad" : "",
+ (regstatus & TPS_REG_PG_LD01) ? " ld01_bad" : "",
+ (regstatus & TPS_REG_PG_MAIN) ? " main_bad" : "",
+ (regstatus & TPS_REG_PG_CORE) ? " core_bad" : "");
+}
+
+static void dbg_chgconf(int por, char *buf, size_t len, u8 chgconfig)
+{
+ const char *hibit;
+
+ if (por)
+ hibit = (chgconfig & TPS_CHARGE_POR)
+ ? "POR=69ms" : "POR=1sec";
+ else
+ hibit = (chgconfig & TPS65013_AUA) ? "AUA" : "";
+
+ snprintf(buf, len, "%02x %s%s%s AC=%d%% USB=%dmA %sCharge\n",
+ chgconfig, hibit,
+ (chgconfig & TPS_CHARGE_RESET) ? " reset" : "",
+ (chgconfig & TPS_CHARGE_FAST) ? " fast" : "",
+ ({int p; switch ((chgconfig >> 3) & 3) {
+ case 3: p = 100; break;
+ case 2: p = 75; break;
+ case 1: p = 50; break;
+ default: p = 25; break;
+ }; p; }),
+ (chgconfig & TPS_VBUS_CHARGING)
+ ? ((chgconfig & TPS_VBUS_500MA) ? 500 : 100)
+ : 0,
+ (chgconfig & TPS_CHARGE_ENABLE) ? "" : "No");
+}
+
+#endif
+
+#ifdef DEBUG
+
+static void show_chgstatus(const char *label, u8 chgstatus)
+{
+ char buf [100];
+
+ dbg_chgstat(buf, sizeof buf, chgstatus);
+ pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
+}
+
+static void show_regstatus(const char *label, u8 regstatus)
+{
+ char buf [100];
+
+ dbg_regstat(buf, sizeof buf, regstatus);
+ pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
+}
+
+static void show_chgconfig(int por, const char *label, u8 chgconfig)
+{
+ char buf [100];
+
+ dbg_chgconf(por, buf, sizeof buf, chgconfig);
+ pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
+}
+
+#else
+
+static inline void show_chgstatus(const char *label, u8 chgstatus) { }
+static inline void show_regstatus(const char *label, u8 chgstatus) { }
+static inline void show_chgconfig(int por, const char *label, u8 chgconfig) { }
+
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+
+static int dbg_show(struct seq_file *s, void *_)
+{
+ struct tps65010 *tps = s->private;
+ u8 value, v2;
+ unsigned i;
+ char buf[100];
+ const char *chip;
+
+ switch (tps->model) {
+ case TPS65010: chip = "tps65010"; break;
+ case TPS65011: chip = "tps65011"; break;
+ case TPS65012: chip = "tps65012"; break;
+ case TPS65013: chip = "tps65013"; break;
+ default: chip = NULL; break;
+ }
+ seq_printf(s, "driver %s\nversion %s\nchip %s\n\n",
+ DRIVER_NAME, DRIVER_VERSION, chip);
+
+ mutex_lock(&tps->lock);
+
+ /* FIXME how can we tell whether a battery is present?
+ * likely involves a charge gauging chip (like BQ26501).
+ */
+
+ seq_printf(s, "%scharging\n\n", tps->charging ? "" : "(not) ");
+
+
+ /* registers for monitoring battery charging and status; note
+ * that reading chgstat and regstat may ack IRQs...
+ */
+ value = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
+ dbg_chgconf(tps->por, buf, sizeof buf, value);
+ seq_printf(s, "chgconfig %s", buf);
+
+ value = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
+ dbg_chgstat(buf, sizeof buf, value);
+ seq_printf(s, "chgstat %s", buf);
+ value = i2c_smbus_read_byte_data(tps->client, TPS_MASK1);
+ dbg_chgstat(buf, sizeof buf, value);
+ seq_printf(s, "mask1 %s", buf);
+ /* ignore ackint1 */
+
+ value = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
+ dbg_regstat(buf, sizeof buf, value);
+ seq_printf(s, "regstat %s", buf);
+ value = i2c_smbus_read_byte_data(tps->client, TPS_MASK2);
+ dbg_regstat(buf, sizeof buf, value);
+ seq_printf(s, "mask2 %s\n", buf);
+ /* ignore ackint2 */
+
+ (void) schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
+
+
+ /* VMAIN voltage, enable lowpower, etc */
+ value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC1);
+ seq_printf(s, "vdcdc1 %02x\n", value);
+
+ /* VCORE voltage, vibrator on/off */
+ value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC2);
+ seq_printf(s, "vdcdc2 %02x\n", value);
+
+ /* both LD0s, and their lowpower behavior */
+ value = i2c_smbus_read_byte_data(tps->client, TPS_VREGS1);
+ seq_printf(s, "vregs1 %02x\n\n", value);
+
+
+ /* LEDs and GPIOs */
+ value = i2c_smbus_read_byte_data(tps->client, TPS_LED1_ON);
+ v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED1_PER);
+ seq_printf(s, "led1 %s, on=%02x, per=%02x, %d/%d msec\n",
+ (value & 0x80)
+ ? ((v2 & 0x80) ? "on" : "off")
+ : ((v2 & 0x80) ? "blink" : "(nPG)"),
+ value, v2,
+ (value & 0x7f) * 10, (v2 & 0x7f) * 100);
+
+ value = i2c_smbus_read_byte_data(tps->client, TPS_LED2_ON);
+ v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED2_PER);
+ seq_printf(s, "led2 %s, on=%02x, per=%02x, %d/%d msec\n",
+ (value & 0x80)
+ ? ((v2 & 0x80) ? "on" : "off")
+ : ((v2 & 0x80) ? "blink" : "off"),
+ value, v2,
+ (value & 0x7f) * 10, (v2 & 0x7f) * 100);
+
+ value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
+ v2 = i2c_smbus_read_byte_data(tps->client, TPS_MASK3);
+ seq_printf(s, "defgpio %02x mask3 %02x\n", value, v2);
+
+ for (i = 0; i < 4; i++) {
+ if (value & (1 << (4 + i)))
+ seq_printf(s, " gpio%d-out %s\n", i + 1,
+ (value & (1 << i)) ? "low" : "hi ");
+ else
+ seq_printf(s, " gpio%d-in %s %s %s\n", i + 1,
+ (value & (1 << i)) ? "hi " : "low",
+ (v2 & (1 << i)) ? "no-irq" : "irq",
+ (v2 & (1 << (4 + i))) ? "rising" : "falling");
+ }
+
+ mutex_unlock(&tps->lock);
+ return 0;
+}
+
+static int dbg_tps_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dbg_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+ .open = dbg_tps_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define DEBUG_FOPS &debug_fops
+
+#else
+#define DEBUG_FOPS NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* handle IRQS in a task context, so we can use I2C calls */
+static void tps65010_interrupt(struct tps65010 *tps)
+{
+ u8 tmp = 0, mask, poll;
+
+ /* IRQs won't trigger for certain events, but we can get
+ * others by polling (normally, with external power applied).
+ */
+ poll = 0;
+
+ /* regstatus irqs */
+ if (tps->nmask2) {
+ tmp = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
+ mask = tmp ^ tps->regstatus;
+ tps->regstatus = tmp;
+ mask &= tps->nmask2;
+ } else
+ mask = 0;
+ if (mask) {
+ tps->regstatus = tmp;
+ /* may need to shut something down ... */
+
+ /* "off" usually means deep sleep */
+ if (tmp & TPS_REG_ONOFF) {
+ pr_info("%s: power off button\n", DRIVER_NAME);
+#if 0
+ /* REVISIT: this might need its own workqueue
+ * plus tweaks including deadlock avoidance ...
+ * also needs to get error handling and probably
+ * an #ifdef CONFIG_HIBERNATION
+ */
+ hibernate();
+#endif
+ poll = 1;
+ }
+ }
+
+ /* chgstatus irqs */
+ if (tps->nmask1) {
+ tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
+ mask = tmp ^ tps->chgstatus;
+ tps->chgstatus = tmp;
+ mask &= tps->nmask1;
+ } else
+ mask = 0;
+ if (mask) {
+ unsigned charging = 0;
+
+ show_chgstatus("chg/irq", tmp);
+ if (tmp & (TPS_CHG_USB|TPS_CHG_AC))
+ show_chgconfig(tps->por, "conf", tps->chgconf);
+
+ /* Unless it was turned off or disabled, we charge any
+ * battery whenever there's power available for it
+ * and the charger hasn't been disabled.
+ */
+ if (!(tps->chgstatus & ~(TPS_CHG_USB|TPS_CHG_AC))
+ && (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
+ && (tps->chgconf & TPS_CHARGE_ENABLE)
+ ) {
+ if (tps->chgstatus & TPS_CHG_USB) {
+ /* VBUS options are readonly until reconnect */
+ if (mask & TPS_CHG_USB)
+ set_bit(FLAG_VBUS_CHANGED, &tps->flags);
+ charging = 1;
+ } else if (tps->chgstatus & TPS_CHG_AC)
+ charging = 1;
+ }
+ if (charging != tps->charging) {
+ tps->charging = charging;
+ pr_info("%s: battery %scharging\n",
+ DRIVER_NAME, charging ? "" :
+ ((tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
+ ? "NOT " : "dis"));
+ }
+ }
+
+ /* always poll to detect (a) power removal, without tps65013
+ * NO_CHG IRQ; or (b) restart of charging after stop.
+ */
+ if ((tps->model != TPS65013 || !tps->charging)
+ && (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC)))
+ poll = 1;
+ if (poll)
+ (void) schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
+
+ /* also potentially gpio-in rise or fall */
+}
+
+/* handle IRQs and polling using keventd for now */
+static void tps65010_work(struct work_struct *work)
+{
+ struct tps65010 *tps;
+
+ tps = container_of(work, struct tps65010, work.work);
+ mutex_lock(&tps->lock);
+
+ tps65010_interrupt(tps);
+
+ if (test_and_clear_bit(FLAG_VBUS_CHANGED, &tps->flags)) {
+ int status;
+ u8 chgconfig, tmp;
+
+ chgconfig = i2c_smbus_read_byte_data(tps->client,
+ TPS_CHGCONFIG);
+ chgconfig &= ~(TPS_VBUS_500MA | TPS_VBUS_CHARGING);
+ if (tps->vbus == 500)
+ chgconfig |= TPS_VBUS_500MA | TPS_VBUS_CHARGING;
+ else if (tps->vbus >= 100)
+ chgconfig |= TPS_VBUS_CHARGING;
+
+ status = i2c_smbus_write_byte_data(tps->client,
+ TPS_CHGCONFIG, chgconfig);
+
+ /* vbus update fails unless VBUS is connected! */
+ tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
+ tps->chgconf = tmp;
+ show_chgconfig(tps->por, "update vbus", tmp);
+ }
+
+ if (test_and_clear_bit(FLAG_IRQ_ENABLE, &tps->flags))
+ enable_irq(tps->client->irq);
+
+ mutex_unlock(&tps->lock);
+}
+
+static irqreturn_t tps65010_irq(int irq, void *_tps)
+{
+ struct tps65010 *tps = _tps;
+
+ disable_irq_nosync(irq);
+ set_bit(FLAG_IRQ_ENABLE, &tps->flags);
+ (void) schedule_work(&tps->work.work);
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* offsets 0..3 == GPIO1..GPIO4
+ * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes)
+ * offset 6 == vibrator motor driver
+ */
+static void
+tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ if (offset < 4)
+ tps65010_set_gpio_out_value(offset + 1, value);
+ else if (offset < 6)
+ tps65010_set_led(offset - 3, value ? ON : OFF);
+ else
+ tps65010_set_vib(value);
+}
+
+static int
+tps65010_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ /* GPIOs may be input-only */
+ if (offset < 4) {
+ struct tps65010 *tps;
+
+ tps = container_of(chip, struct tps65010, chip);
+ if (!(tps->outmask & (1 << offset)))
+ return -EINVAL;
+ tps65010_set_gpio_out_value(offset + 1, value);
+ } else if (offset < 6)
+ tps65010_set_led(offset - 3, value ? ON : OFF);
+ else
+ tps65010_set_vib(value);
+
+ return 0;
+}
+
+static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ int value;
+ struct tps65010 *tps;
+
+ tps = container_of(chip, struct tps65010, chip);
+
+ if (offset < 4) {
+ value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
+ if (value < 0)
+ return 0;
+ if (value & (1 << (offset + 4))) /* output */
+ return !(value & (1 << offset));
+ else /* input */
+ return (value & (1 << offset));
+ }
+
+ /* REVISIT we *could* report LED1/nPG and LED2 state ... */
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct tps65010 *the_tps;
+
+static int __exit tps65010_remove(struct i2c_client *client)
+{
+ struct tps65010 *tps = i2c_get_clientdata(client);
+ struct tps65010_board *board = client->dev.platform_data;
+
+ if (board && board->teardown) {
+ int status = board->teardown(client, board->context);
+ if (status < 0)
+ dev_dbg(&client->dev, "board %s %s err %d\n",
+ "teardown", client->name, status);
+ }
+ if (client->irq > 0)
+ free_irq(client->irq, tps);
+ cancel_delayed_work(&tps->work);
+ flush_scheduled_work();
+ debugfs_remove(tps->file);
+ kfree(tps);
+ i2c_set_clientdata(client, NULL);
+ the_tps = NULL;
+ return 0;
+}
+
+static int tps65010_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tps65010 *tps;
+ int status;
+ struct tps65010_board *board = client->dev.platform_data;
+
+ if (the_tps) {
+ dev_dbg(&client->dev, "only one tps6501x chip allowed\n");
+ return -ENODEV;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EINVAL;
+
+ tps = kzalloc(sizeof *tps, GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ mutex_init(&tps->lock);
+ INIT_DELAYED_WORK(&tps->work, tps65010_work);
+ tps->client = client;
+ tps->model = id->driver_data;
+
+ /* the IRQ is active low, but many gpio lines can't support that
+ * so this driver uses falling-edge triggers instead.
+ */
+ if (client->irq > 0) {
+ status = request_irq(client->irq, tps65010_irq,
+ IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
+ DRIVER_NAME, tps);
+ if (status < 0) {
+ dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
+ client->irq, status);
+ goto fail1;
+ }
+ /* annoying race here, ideally we'd have an option
+ * to claim the irq now and enable it later.
+ * FIXME genirq IRQF_NOAUTOEN now solves that ...
+ */
+ disable_irq(client->irq);
+ set_bit(FLAG_IRQ_ENABLE, &tps->flags);
+ } else
+ dev_warn(&client->dev, "IRQ not configured!\n");
+
+
+ switch (tps->model) {
+ case TPS65010:
+ case TPS65012:
+ tps->por = 1;
+ break;
+ /* else CHGCONFIG.POR is replaced by AUA, enabling a WAIT mode */
+ }
+ tps->chgconf = i2c_smbus_read_byte_data(client, TPS_CHGCONFIG);
+ show_chgconfig(tps->por, "conf/init", tps->chgconf);
+
+ show_chgstatus("chg/init",
+ i2c_smbus_read_byte_data(client, TPS_CHGSTATUS));
+ show_regstatus("reg/init",
+ i2c_smbus_read_byte_data(client, TPS_REGSTATUS));
+
+ pr_debug("%s: vdcdc1 0x%02x, vdcdc2 %02x, vregs1 %02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(client, TPS_VDCDC1),
+ i2c_smbus_read_byte_data(client, TPS_VDCDC2),
+ i2c_smbus_read_byte_data(client, TPS_VREGS1));
+ pr_debug("%s: defgpio 0x%02x, mask3 0x%02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(client, TPS_DEFGPIO),
+ i2c_smbus_read_byte_data(client, TPS_MASK3));
+
+ i2c_set_clientdata(client, tps);
+ the_tps = tps;
+
+#if defined(CONFIG_USB_GADGET) && !defined(CONFIG_USB_OTG)
+ /* USB hosts can't draw VBUS. OTG devices could, later
+ * when OTG infrastructure enables it. USB peripherals
+ * could be relying on VBUS while booting, though.
+ */
+ tps->vbus = 100;
+#endif
+
+ /* unmask the "interesting" irqs, then poll once to
+ * kickstart monitoring, initialize shadowed status
+ * registers, and maybe disable VBUS draw.
+ */
+ tps->nmask1 = ~0;
+ (void) i2c_smbus_write_byte_data(client, TPS_MASK1, ~tps->nmask1);
+
+ tps->nmask2 = TPS_REG_ONOFF;
+ if (tps->model == TPS65013)
+ tps->nmask2 |= TPS_REG_NO_CHG;
+ (void) i2c_smbus_write_byte_data(client, TPS_MASK2, ~tps->nmask2);
+
+ (void) i2c_smbus_write_byte_data(client, TPS_MASK3, 0x0f
+ | i2c_smbus_read_byte_data(client, TPS_MASK3));
+
+ tps65010_work(&tps->work.work);
+
+ tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
+ tps, DEBUG_FOPS);
+
+ /* optionally register GPIOs */
+ if (board && board->base > 0) {
+ tps->outmask = board->outmask;
+
+ tps->chip.label = client->name;
+ tps->chip.dev = &client->dev;
+ tps->chip.owner = THIS_MODULE;
+
+ tps->chip.set = tps65010_gpio_set;
+ tps->chip.direction_output = tps65010_output;
+
+ /* NOTE: only partial support for inputs; nyet IRQs */
+ tps->chip.get = tps65010_gpio_get;
+
+ tps->chip.base = board->base;
+ tps->chip.ngpio = 7;
+ tps->chip.can_sleep = 1;
+
+ status = gpiochip_add(&tps->chip);
+ if (status < 0)
+ dev_err(&client->dev, "can't add gpiochip, err %d\n",
+ status);
+ else if (board->setup) {
+ status = board->setup(client, board->context);
+ if (status < 0) {
+ dev_dbg(&client->dev,
+ "board %s %s err %d\n",
+ "setup", client->name, status);
+ status = 0;
+ }
+ }
+ }
+
+ return 0;
+fail1:
+ kfree(tps);
+ return status;
+}
+
+static const struct i2c_device_id tps65010_id[] = {
+ { "tps65010", TPS65010 },
+ { "tps65011", TPS65011 },
+ { "tps65012", TPS65012 },
+ { "tps65013", TPS65013 },
+ { "tps65014", TPS65011 }, /* tps65011 charging at 6.5V max */
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tps65010_id);
+
+static struct i2c_driver tps65010_driver = {
+ .driver = {
+ .name = "tps65010",
+ },
+ .probe = tps65010_probe,
+ .remove = __exit_p(tps65010_remove),
+ .id_table = tps65010_id,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Draw from VBUS:
+ * 0 mA -- DON'T DRAW (might supply power instead)
+ * 100 mA -- usb unit load (slowest charge rate)
+ * 500 mA -- usb high power (fast battery charge)
+ */
+int tps65010_set_vbus_draw(unsigned mA)
+{
+ unsigned long flags;
+
+ if (!the_tps)
+ return -ENODEV;
+
+ /* assumes non-SMP */
+ local_irq_save(flags);
+ if (mA >= 500)
+ mA = 500;
+ else if (mA >= 100)
+ mA = 100;
+ else
+ mA = 0;
+ the_tps->vbus = mA;
+ if ((the_tps->chgstatus & TPS_CHG_USB)
+ && test_and_set_bit(
+ FLAG_VBUS_CHANGED, &the_tps->flags)) {
+ /* gadget drivers call this in_irq() */
+ (void) schedule_work(&the_tps->work.work);
+ }
+ local_irq_restore(flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(tps65010_set_vbus_draw);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_gpio_out_value parameter:
+ * gpio: GPIO1, GPIO2, GPIO3 or GPIO4
+ * value: LOW or HIGH
+ */
+int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
+{
+ int status;
+ unsigned defgpio;
+
+ if (!the_tps)
+ return -ENODEV;
+ if ((gpio < GPIO1) || (gpio > GPIO4))
+ return -EINVAL;
+
+ mutex_lock(&the_tps->lock);
+
+ defgpio = i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO);
+
+ /* Configure GPIO for output */
+ defgpio |= 1 << (gpio + 3);
+
+ /* Writing 1 forces a logic 0 on that GPIO and vice versa */
+ switch (value) {
+ case LOW:
+ defgpio |= 1 << (gpio - 1); /* set GPIO low by writing 1 */
+ break;
+ /* case HIGH: */
+ default:
+ defgpio &= ~(1 << (gpio - 1)); /* set GPIO high by writing 0 */
+ break;
+ }
+
+ status = i2c_smbus_write_byte_data(the_tps->client,
+ TPS_DEFGPIO, defgpio);
+
+ pr_debug("%s: gpio%dout = %s, defgpio 0x%02x\n", DRIVER_NAME,
+ gpio, value ? "high" : "low",
+ i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO));
+
+ mutex_unlock(&the_tps->lock);
+ return status;
+}
+EXPORT_SYMBOL(tps65010_set_gpio_out_value);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_led parameter:
+ * led: LED1 or LED2
+ * mode: ON, OFF or BLINK
+ */
+int tps65010_set_led(unsigned led, unsigned mode)
+{
+ int status;
+ unsigned led_on, led_per, offs;
+
+ if (!the_tps)
+ return -ENODEV;
+
+ if (led == LED1)
+ offs = 0;
+ else {
+ offs = 2;
+ led = LED2;
+ }
+
+ mutex_lock(&the_tps->lock);
+
+ pr_debug("%s: led%i_on 0x%02x\n", DRIVER_NAME, led,
+ i2c_smbus_read_byte_data(the_tps->client,
+ TPS_LED1_ON + offs));
+
+ pr_debug("%s: led%i_per 0x%02x\n", DRIVER_NAME, led,
+ i2c_smbus_read_byte_data(the_tps->client,
+ TPS_LED1_PER + offs));
+
+ switch (mode) {
+ case OFF:
+ led_on = 1 << 7;
+ led_per = 0 << 7;
+ break;
+ case ON:
+ led_on = 1 << 7;
+ led_per = 1 << 7;
+ break;
+ case BLINK:
+ led_on = 0x30 | (0 << 7);
+ led_per = 0x08 | (1 << 7);
+ break;
+ default:
+ printk(KERN_ERR "%s: Wrong mode parameter for set_led()\n",
+ DRIVER_NAME);
+ mutex_unlock(&the_tps->lock);
+ return -EINVAL;
+ }
+
+ status = i2c_smbus_write_byte_data(the_tps->client,
+ TPS_LED1_ON + offs, led_on);
+
+ if (status != 0) {
+ printk(KERN_ERR "%s: Failed to write led%i_on register\n",
+ DRIVER_NAME, led);
+ mutex_unlock(&the_tps->lock);
+ return status;
+ }
+
+ pr_debug("%s: led%i_on 0x%02x\n", DRIVER_NAME, led,
+ i2c_smbus_read_byte_data(the_tps->client, TPS_LED1_ON + offs));
+
+ status = i2c_smbus_write_byte_data(the_tps->client,
+ TPS_LED1_PER + offs, led_per);
+
+ if (status != 0) {
+ printk(KERN_ERR "%s: Failed to write led%i_per register\n",
+ DRIVER_NAME, led);
+ mutex_unlock(&the_tps->lock);
+ return status;
+ }
+
+ pr_debug("%s: led%i_per 0x%02x\n", DRIVER_NAME, led,
+ i2c_smbus_read_byte_data(the_tps->client,
+ TPS_LED1_PER + offs));
+
+ mutex_unlock(&the_tps->lock);
+
+ return status;
+}
+EXPORT_SYMBOL(tps65010_set_led);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_vib parameter:
+ * value: ON or OFF
+ */
+int tps65010_set_vib(unsigned value)
+{
+ int status;
+ unsigned vdcdc2;
+
+ if (!the_tps)
+ return -ENODEV;
+
+ mutex_lock(&the_tps->lock);
+
+ vdcdc2 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC2);
+ vdcdc2 &= ~(1 << 1);
+ if (value)
+ vdcdc2 |= (1 << 1);
+ status = i2c_smbus_write_byte_data(the_tps->client,
+ TPS_VDCDC2, vdcdc2);
+
+ pr_debug("%s: vibrator %s\n", DRIVER_NAME, value ? "on" : "off");
+
+ mutex_unlock(&the_tps->lock);
+ return status;
+}
+EXPORT_SYMBOL(tps65010_set_vib);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_low_pwr parameter:
+ * mode: ON or OFF
+ */
+int tps65010_set_low_pwr(unsigned mode)
+{
+ int status;
+ unsigned vdcdc1;
+
+ if (!the_tps)
+ return -ENODEV;
+
+ mutex_lock(&the_tps->lock);
+
+ pr_debug("%s: %s low_pwr, vdcdc1 0x%02x\n", DRIVER_NAME,
+ mode ? "enable" : "disable",
+ i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+ vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
+
+ switch (mode) {
+ case OFF:
+ vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
+ break;
+ /* case ON: */
+ default:
+ vdcdc1 |= TPS_ENABLE_LP; /* enable ENABLE_LP bit */
+ break;
+ }
+
+ status = i2c_smbus_write_byte_data(the_tps->client,
+ TPS_VDCDC1, vdcdc1);
+
+ if (status != 0)
+ printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
+ DRIVER_NAME);
+ else
+ pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+ mutex_unlock(&the_tps->lock);
+
+ return status;
+}
+EXPORT_SYMBOL(tps65010_set_low_pwr);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_config_vregs1 parameter:
+ * value to be written to VREGS1 register
+ * Note: The complete register is written, set all bits you need
+ */
+int tps65010_config_vregs1(unsigned value)
+{
+ int status;
+
+ if (!the_tps)
+ return -ENODEV;
+
+ mutex_lock(&the_tps->lock);
+
+ pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
+
+ status = i2c_smbus_write_byte_data(the_tps->client,
+ TPS_VREGS1, value);
+
+ if (status != 0)
+ printk(KERN_ERR "%s: Failed to write vregs1 register\n",
+ DRIVER_NAME);
+ else
+ pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
+
+ mutex_unlock(&the_tps->lock);
+
+ return status;
+}
+EXPORT_SYMBOL(tps65010_config_vregs1);
+
+/*-------------------------------------------------------------------------*/
+/* tps65013_set_low_pwr parameter:
+ * mode: ON or OFF
+ */
+
+/* FIXME: Assumes AC or USB power is present. Setting AUA bit is not
+ required if power supply is through a battery */
+
+int tps65013_set_low_pwr(unsigned mode)
+{
+ int status;
+ unsigned vdcdc1, chgconfig;
+
+ if (!the_tps || the_tps->por)
+ return -ENODEV;
+
+ mutex_lock(&the_tps->lock);
+
+ pr_debug("%s: %s low_pwr, chgconfig 0x%02x vdcdc1 0x%02x\n",
+ DRIVER_NAME,
+ mode ? "enable" : "disable",
+ i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG),
+ i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+ chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
+ vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
+
+ switch (mode) {
+ case OFF:
+ chgconfig &= ~TPS65013_AUA; /* disable AUA bit */
+ vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
+ break;
+ /* case ON: */
+ default:
+ chgconfig |= TPS65013_AUA; /* enable AUA bit */
+ vdcdc1 |= TPS_ENABLE_LP; /* enable ENABLE_LP bit */
+ break;
+ }
+
+ status = i2c_smbus_write_byte_data(the_tps->client,
+ TPS_CHGCONFIG, chgconfig);
+ if (status != 0) {
+ printk(KERN_ERR "%s: Failed to write chconfig register\n",
+ DRIVER_NAME);
+ mutex_unlock(&the_tps->lock);
+ return status;
+ }
+
+ chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
+ the_tps->chgconf = chgconfig;
+ show_chgconfig(0, "chgconf", chgconfig);
+
+ status = i2c_smbus_write_byte_data(the_tps->client,
+ TPS_VDCDC1, vdcdc1);
+
+ if (status != 0)
+ printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
+ DRIVER_NAME);
+ else
+ pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
+ i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+ mutex_unlock(&the_tps->lock);
+
+ return status;
+}
+EXPORT_SYMBOL(tps65013_set_low_pwr);
+
+/*-------------------------------------------------------------------------*/
+
+static int __init tps_init(void)
+{
+ u32 tries = 3;
+ int status = -ENODEV;
+
+ printk(KERN_INFO "%s: version %s\n", DRIVER_NAME, DRIVER_VERSION);
+
+ /* some boards have startup glitches */
+ while (tries--) {
+ status = i2c_add_driver(&tps65010_driver);
+ if (the_tps)
+ break;
+ i2c_del_driver(&tps65010_driver);
+ if (!tries) {
+ printk(KERN_ERR "%s: no chip?\n", DRIVER_NAME);
+ return -ENODEV;
+ }
+ pr_debug("%s: re-probe ...\n", DRIVER_NAME);
+ msleep(10);
+ }
+
+ return status;
+}
+/* NOTE: this MUST be initialized before the other parts of the system
+ * that rely on it ... but after the i2c bus on which this relies.
+ * That is, much earlier than on PC-type systems, which don't often use
+ * I2C as a core system bus.
+ */
+subsys_initcall(tps_init);
+
+static void __exit tps_exit(void)
+{
+ i2c_del_driver(&tps65010_driver);
+}
+module_exit(tps_exit);
+
diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/i2c/chips/tsl2550.c
new file mode 100644
index 0000000..1a9cc13
--- /dev/null
+++ b/drivers/i2c/chips/tsl2550.c
@@ -0,0 +1,489 @@
+/*
+ * tsl2550.c - Linux kernel modules for ambient light sensor
+ *
+ * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#define TSL2550_DRV_NAME "tsl2550"
+#define DRIVER_VERSION "1.1.1"
+
+/*
+ * Defines
+ */
+
+#define TSL2550_POWER_DOWN 0x00
+#define TSL2550_POWER_UP 0x03
+#define TSL2550_STANDARD_RANGE 0x18
+#define TSL2550_EXTENDED_RANGE 0x1d
+#define TSL2550_READ_ADC0 0x43
+#define TSL2550_READ_ADC1 0x83
+
+/*
+ * Structs
+ */
+
+struct tsl2550_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+
+ unsigned int power_state : 1;
+ unsigned int operating_mode : 1;
+};
+
+/*
+ * Global data
+ */
+
+static const u8 TSL2550_MODE_RANGE[2] = {
+ TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE,
+};
+
+/*
+ * Management functions
+ */
+
+static int tsl2550_set_operating_mode(struct i2c_client *client, int mode)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+
+ int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]);
+
+ data->operating_mode = mode;
+
+ return ret;
+}
+
+static int tsl2550_set_power_state(struct i2c_client *client, int state)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ int ret;
+
+ if (state == 0)
+ ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN);
+ else {
+ ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
+
+ /* On power up we should reset operating mode also... */
+ tsl2550_set_operating_mode(client, data->operating_mode);
+ }
+
+ data->power_state = state;
+
+ return ret;
+}
+
+static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
+{
+ unsigned long end;
+ int loop = 0, ret = 0;
+
+ /*
+ * Read ADC channel waiting at most 400ms (see data sheet for further
+ * info).
+ * To avoid long busy wait we spin for few milliseconds then
+ * start sleeping.
+ */
+ end = jiffies + msecs_to_jiffies(400);
+ while (time_before(jiffies, end)) {
+ i2c_smbus_write_byte(client, cmd);
+
+ if (loop++ < 5)
+ mdelay(1);
+ else
+ msleep(1);
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0)
+ return ret;
+ else if (ret & 0x0080)
+ break;
+ }
+ if (!(ret & 0x80))
+ return -EIO;
+ return ret & 0x7f; /* remove the "valid" bit */
+}
+
+/*
+ * LUX calculation
+ */
+
+#define TSL2550_MAX_LUX 1846
+
+static const u8 ratio_lut[] = {
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 98, 98, 98, 98, 98,
+ 98, 98, 97, 97, 97, 97, 97, 96,
+ 96, 96, 96, 95, 95, 95, 94, 94,
+ 93, 93, 93, 92, 92, 91, 91, 90,
+ 89, 89, 88, 87, 87, 86, 85, 84,
+ 83, 82, 81, 80, 79, 78, 77, 75,
+ 74, 73, 71, 69, 68, 66, 64, 62,
+ 60, 58, 56, 54, 52, 49, 47, 44,
+ 42, 41, 40, 40, 39, 39, 38, 38,
+ 37, 37, 37, 36, 36, 36, 35, 35,
+ 35, 35, 34, 34, 34, 34, 33, 33,
+ 33, 33, 32, 32, 32, 32, 32, 31,
+ 31, 31, 31, 31, 30, 30, 30, 30,
+ 30,
+};
+
+static const u16 count_lut[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 49, 53, 57, 61, 65, 69, 73, 77,
+ 81, 85, 89, 93, 97, 101, 105, 109,
+ 115, 123, 131, 139, 147, 155, 163, 171,
+ 179, 187, 195, 203, 211, 219, 227, 235,
+ 247, 263, 279, 295, 311, 327, 343, 359,
+ 375, 391, 407, 423, 439, 455, 471, 487,
+ 511, 543, 575, 607, 639, 671, 703, 735,
+ 767, 799, 831, 863, 895, 927, 959, 991,
+ 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
+ 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
+ 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
+ 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015,
+};
+
+/*
+ * This function is described into Taos TSL2550 Designer's Notebook
+ * pages 2, 3.
+ */
+static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
+{
+ unsigned int lux;
+
+ /* Look up count from channel values */
+ u16 c0 = count_lut[ch0];
+ u16 c1 = count_lut[ch1];
+
+ /*
+ * Calculate ratio.
+ * Note: the "128" is a scaling factor
+ */
+ u8 r = 128;
+
+ /* Avoid division by 0 and count 1 cannot be greater than count 0 */
+ if (c0 && (c1 <= c0))
+ r = c1 * 128 / c0;
+ else
+ return -1;
+
+ /* Calculate LUX */
+ lux = ((c0 - c1) * ratio_lut[r]) / 256;
+
+ /* LUX range check */
+ return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
+}
+
+/*
+ * SysFS support
+ */
+
+static ssize_t tsl2550_show_power_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ return sprintf(buf, "%u\n", data->power_state);
+}
+
+static ssize_t tsl2550_store_power_state(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ unsigned long val = simple_strtoul(buf, NULL, 10);
+ int ret;
+
+ if (val < 0 || val > 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ ret = tsl2550_set_power_state(client, val);
+ mutex_unlock(&data->update_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
+ tsl2550_show_power_state, tsl2550_store_power_state);
+
+static ssize_t tsl2550_show_operating_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ return sprintf(buf, "%u\n", data->operating_mode);
+}
+
+static ssize_t tsl2550_store_operating_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ unsigned long val = simple_strtoul(buf, NULL, 10);
+ int ret;
+
+ if (val < 0 || val > 1)
+ return -EINVAL;
+
+ if (data->power_state == 0)
+ return -EBUSY;
+
+ mutex_lock(&data->update_lock);
+ ret = tsl2550_set_operating_mode(client, val);
+ mutex_unlock(&data->update_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
+ tsl2550_show_operating_mode, tsl2550_store_operating_mode);
+
+static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
+{
+ u8 ch0, ch1;
+ int ret;
+
+ ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0);
+ if (ret < 0)
+ return ret;
+ ch0 = ret;
+
+ mdelay(1);
+
+ ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
+ if (ret < 0)
+ return ret;
+ ch1 = ret;
+
+ /* Do the job */
+ ret = tsl2550_calculate_lux(ch0, ch1);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t tsl2550_show_lux1_input(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ int ret;
+
+ /* No LUX data if not operational */
+ if (!data->power_state)
+ return -EBUSY;
+
+ mutex_lock(&data->update_lock);
+ ret = __tsl2550_show_lux(client, buf);
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static DEVICE_ATTR(lux1_input, S_IRUGO,
+ tsl2550_show_lux1_input, NULL);
+
+static struct attribute *tsl2550_attributes[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_operating_mode.attr,
+ &dev_attr_lux1_input.attr,
+ NULL
+};
+
+static const struct attribute_group tsl2550_attr_group = {
+ .attrs = tsl2550_attributes,
+};
+
+/*
+ * Initialization function
+ */
+
+static int tsl2550_init_client(struct i2c_client *client)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ int err;
+
+ /*
+ * Probe the chip. To do so we try to power up the device and then to
+ * read back the 0x03 code
+ */
+ err = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
+ if (err < 0)
+ return err;
+ mdelay(1);
+ if (i2c_smbus_read_byte(client) != TSL2550_POWER_UP)
+ return -ENODEV;
+ data->power_state = 1;
+
+ /* Set the default operating mode */
+ err = i2c_smbus_write_byte(client,
+ TSL2550_MODE_RANGE[data->operating_mode]);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+
+static struct i2c_driver tsl2550_driver;
+static int __devinit tsl2550_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct tsl2550_data *data;
+ int *opmode, err = 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
+ err = -EIO;
+ goto exit;
+ }
+
+ data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ data->client = client;
+ i2c_set_clientdata(client, data);
+
+ /* Check platform data */
+ opmode = client->dev.platform_data;
+ if (opmode) {
+ if (*opmode < 0 || *opmode > 1) {
+ dev_err(&client->dev, "invalid operating_mode (%d)\n",
+ *opmode);
+ err = -EINVAL;
+ goto exit_kfree;
+ }
+ data->operating_mode = *opmode;
+ } else
+ data->operating_mode = 0; /* default mode is standard */
+ dev_info(&client->dev, "%s operating mode\n",
+ data->operating_mode ? "extended" : "standard");
+
+ mutex_init(&data->update_lock);
+
+ /* Initialize the TSL2550 chip */
+ err = tsl2550_init_client(client);
+ if (err)
+ goto exit_kfree;
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
+ if (err)
+ goto exit_kfree;
+
+ dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
+
+ return 0;
+
+exit_kfree:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int __devexit tsl2550_remove(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
+
+ /* Power down the device */
+ tsl2550_set_power_state(client, 0);
+
+ kfree(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ return tsl2550_set_power_state(client, 0);
+}
+
+static int tsl2550_resume(struct i2c_client *client)
+{
+ return tsl2550_set_power_state(client, 1);
+}
+
+#else
+
+#define tsl2550_suspend NULL
+#define tsl2550_resume NULL
+
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id tsl2550_id[] = {
+ { "tsl2550", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tsl2550_id);
+
+static struct i2c_driver tsl2550_driver = {
+ .driver = {
+ .name = TSL2550_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .suspend = tsl2550_suspend,
+ .resume = tsl2550_resume,
+ .probe = tsl2550_probe,
+ .remove = __devexit_p(tsl2550_remove),
+ .id_table = tsl2550_id,
+};
+
+static int __init tsl2550_init(void)
+{
+ return i2c_add_driver(&tsl2550_driver);
+}
+
+static void __exit tsl2550_exit(void)
+{
+ i2c_del_driver(&tsl2550_driver);
+}
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("TSL2550 ambient light sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(tsl2550_init);
+module_exit(tsl2550_exit);
OpenPOWER on IntegriCloud