diff options
Diffstat (limited to 'drivers/i2c')
94 files changed, 43891 insertions, 0 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig new file mode 100644 index 00000000..30f06e95 --- /dev/null +++ b/drivers/i2c/Kconfig @@ -0,0 +1,113 @@ +# +# I2C subsystem configuration +# + +menuconfig I2C + tristate "I2C support" + depends on HAS_IOMEM + select RT_MUTEXES + ---help--- + I2C (pronounce: I-square-C) is a slow serial bus protocol used in + many micro controller applications and developed by Philips. SMBus, + or System Management Bus is a subset of the I2C protocol. More + information is contained in the directory <file:Documentation/i2c/>, + especially in the file called "summary" there. + + Both I2C and SMBus are supported here. You will need this for + hardware sensors support, and also for Video For Linux support. + + If you want I2C support, you should say Y here and also to the + specific driver for your bus adapter(s) below. + + This I2C support can also be built as a module. If so, the module + will be called i2c-core. + +if I2C + +config I2C_BOARDINFO + boolean + default y + +config I2C_COMPAT + boolean "Enable compatibility bits for old user-space" + default y + help + Say Y here if you intend to run lm-sensors 3.1.1 or older, or any + other user-space package which expects i2c adapters to be class + devices. If you don't know, say Y. + +config I2C_CHARDEV + tristate "I2C device interface" + help + Say Y here to use i2c-* device files, usually found in the /dev + directory on your system. They make it possible to have user-space + programs use the I2C bus. Information on how to do this is + contained in the file <file:Documentation/i2c/dev-interface>. + + This support is also available as a module. If so, the module + will be called i2c-dev. + +config I2C_MUX + tristate "I2C bus multiplexing support" + depends on EXPERIMENTAL + help + Say Y here if you want the I2C core to support the ability to + handle multiplexed I2C bus topologies, by presenting each + multiplexed segment as a I2C adapter. + + This support is also available as a module. If so, the module + will be called i2c-mux. + +source drivers/i2c/muxes/Kconfig + +config I2C_HELPER_AUTO + bool "Autoselect pertinent helper modules" + default y + help + Some I2C bus drivers require so-called "I2C algorithm" modules + to work. These are basically software-only abstractions of generic + I2C interfaces. This option will autoselect them so that you don't + have to care. + + Unselect this only if you need to enable additional helper + modules, for example for use with external I2C bus drivers. + + In doubt, say Y. + +config I2C_SMBUS + tristate "SMBus-specific protocols" if !I2C_HELPER_AUTO + help + Say Y here if you want support for SMBus extensions to the I2C + specification. At the moment, the only supported extension is + the SMBus alert protocol. + + This support is also available as a module. If so, the module + will be called i2c-smbus. + +source drivers/i2c/algos/Kconfig +source drivers/i2c/busses/Kconfig + +config I2C_DEBUG_CORE + bool "I2C Core debugging messages" + help + Say Y here if you want the I2C core to produce a bunch of debug + messages to the system log. Select this if you are having a + problem with I2C support and want to see more of what is going on. + +config I2C_DEBUG_ALGO + bool "I2C Algorithm debugging messages" + help + Say Y here if you want the I2C algorithm drivers to produce a bunch + of debug messages to the system log. Select this if you are having + a problem with I2C support and want to see more of what is going + on. + +config I2C_DEBUG_BUS + bool "I2C Bus debugging messages" + help + Say Y here if you want the I2C bus drivers to produce a bunch of + debug messages to the system log. Select this if you are having + a problem with I2C support and want to see more of what is going + on. + +endif # I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile new file mode 100644 index 00000000..beee6b2d --- /dev/null +++ b/drivers/i2c/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the i2c core. +# + +obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o +obj-$(CONFIG_I2C) += i2c-core.o +obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o +obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o +obj-$(CONFIG_I2C_MUX) += i2c-mux.o +obj-y += algos/ busses/ muxes/ + +ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG +CFLAGS_i2c-core.o := -Wno-deprecated-declarations diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig new file mode 100644 index 00000000..f1cfe7e5 --- /dev/null +++ b/drivers/i2c/algos/Kconfig @@ -0,0 +1,17 @@ +# +# I2C algorithm drivers configuration +# + +menu "I2C Algorithms" + visible if !I2C_HELPER_AUTO + +config I2C_ALGOBIT + tristate "I2C bit-banging interfaces" + +config I2C_ALGOPCF + tristate "I2C PCF 8584 interfaces" + +config I2C_ALGOPCA + tristate "I2C PCA 9564 interfaces" + +endmenu diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile new file mode 100644 index 00000000..215303f6 --- /dev/null +++ b/drivers/i2c/algos/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the i2c algorithms +# + +obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o +obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o +obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o + +ccflags-$(CONFIG_I2C_DEBUG_ALGO) := -DDEBUG diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c new file mode 100644 index 00000000..f2ce8c36 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -0,0 +1,667 @@ +/* ------------------------------------------------------------------------- + * i2c-algo-bit.c i2c driver algorithms for bit-shift adapters + * ------------------------------------------------------------------------- + * Copyright (C) 1995-2000 Simon G. Vogl + + 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. + * ------------------------------------------------------------------------- */ + +/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki + <kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org> */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + + +/* ----- global defines ----------------------------------------------- */ + +#ifdef DEBUG +#define bit_dbg(level, dev, format, args...) \ + do { \ + if (i2c_debug >= level) \ + dev_dbg(dev, format, ##args); \ + } while (0) +#else +#define bit_dbg(level, dev, format, args...) \ + do {} while (0) +#endif /* DEBUG */ + +/* ----- global variables --------------------------------------------- */ + +static int bit_test; /* see if the line-setting functions work */ +module_param(bit_test, bool, 0); +MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck"); + +#ifdef DEBUG +static int i2c_debug = 1; +module_param(i2c_debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2 verbose; 3 very verbose"); +#endif + +/* --- setting states on the bus with the right timing: --------------- */ + +#define setsda(adap, val) adap->setsda(adap->data, val) +#define setscl(adap, val) adap->setscl(adap->data, val) +#define getsda(adap) adap->getsda(adap->data) +#define getscl(adap) adap->getscl(adap->data) + +static inline void sdalo(struct i2c_algo_bit_data *adap) +{ + setsda(adap, 0); + udelay((adap->udelay + 1) / 2); +} + +static inline void sdahi(struct i2c_algo_bit_data *adap) +{ + setsda(adap, 1); + udelay((adap->udelay + 1) / 2); +} + +static inline void scllo(struct i2c_algo_bit_data *adap) +{ + setscl(adap, 0); + udelay(adap->udelay / 2); +} + +/* + * Raise scl line, and do checking for delays. This is necessary for slower + * devices. + */ +static int sclhi(struct i2c_algo_bit_data *adap) +{ + unsigned long start; + + setscl(adap, 1); + + /* Not all adapters have scl sense line... */ + if (!adap->getscl) + goto done; + + start = jiffies; + while (!getscl(adap)) { + /* This hw knows how to read the clock line, so we wait + * until it actually gets high. This is safer as some + * chips may hold it low ("clock stretching") while they + * are processing data internally. + */ + if (time_after(jiffies, start + adap->timeout)) { + /* Test one last time, as we may have been preempted + * between last check and timeout test. + */ + if (getscl(adap)) + break; + return -ETIMEDOUT; + } + cond_resched(); + } +#ifdef DEBUG + if (jiffies != start && i2c_debug >= 3) + pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go " + "high\n", jiffies - start); +#endif + +done: + udelay(adap->udelay); + return 0; +} + + +/* --- other auxiliary functions -------------------------------------- */ +static void i2c_start(struct i2c_algo_bit_data *adap) +{ + /* assert: scl, sda are high */ + setsda(adap, 0); + udelay(adap->udelay); + scllo(adap); +} + +static void i2c_repstart(struct i2c_algo_bit_data *adap) +{ + /* assert: scl is low */ + sdahi(adap); + sclhi(adap); + setsda(adap, 0); + udelay(adap->udelay); + scllo(adap); +} + + +static void i2c_stop(struct i2c_algo_bit_data *adap) +{ + /* assert: scl is low */ + sdalo(adap); + sclhi(adap); + setsda(adap, 1); + udelay(adap->udelay); +} + + + +/* send a byte without start cond., look for arbitration, + check ackn. from slave */ +/* returns: + * 1 if the device acknowledged + * 0 if the device did not ack + * -ETIMEDOUT if an error occurred (while raising the scl line) + */ +static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c) +{ + int i; + int sb; + int ack; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + for (i = 7; i >= 0; i--) { + sb = (c >> i) & 1; + setsda(adap, sb); + udelay((adap->udelay + 1) / 2); + if (sclhi(adap) < 0) { /* timed out */ + bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " + "timeout at bit #%d\n", (int)c, i); + return -ETIMEDOUT; + } + /* FIXME do arbitration here: + * if (sb && !getsda(adap)) -> ouch! Get out of here. + * + * Report a unique code, so higher level code can retry + * the whole (combined) message and *NOT* issue STOP. + */ + scllo(adap); + } + sdahi(adap); + if (sclhi(adap) < 0) { /* timeout */ + bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " + "timeout at ack\n", (int)c); + return -ETIMEDOUT; + } + + /* read ack: SDA should be pulled down by slave, or it may + * NAK (usually to report problems with the data we wrote). + */ + ack = !getsda(adap); /* ack: sda is pulled low -> success */ + bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c, + ack ? "A" : "NA"); + + scllo(adap); + return ack; + /* assert: scl is low (sda undef) */ +} + + +static int i2c_inb(struct i2c_adapter *i2c_adap) +{ + /* read byte via i2c port, without start/stop sequence */ + /* acknowledge is sent in i2c_read. */ + int i; + unsigned char indata = 0; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + sdahi(adap); + for (i = 0; i < 8; i++) { + if (sclhi(adap) < 0) { /* timeout */ + bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit " + "#%d\n", 7 - i); + return -ETIMEDOUT; + } + indata *= 2; + if (getsda(adap)) + indata |= 0x01; + setscl(adap, 0); + udelay(i == 7 ? adap->udelay / 2 : adap->udelay); + } + /* assert: scl is low */ + return indata; +} + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_adapter *i2c_adap) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + const char *name = i2c_adap->name; + int scl, sda, ret; + + if (adap->pre_xfer) { + ret = adap->pre_xfer(i2c_adap); + if (ret < 0) + return -ENODEV; + } + + if (adap->getscl == NULL) + pr_info("%s: Testing SDA only, SCL is not readable\n", name); + + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (!scl || !sda) { + printk(KERN_WARNING "%s: bus seems to be busy\n", name); + goto bailout; + } + + sdalo(adap); + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (sda) { + printk(KERN_WARNING "%s: SDA stuck high!\n", name); + goto bailout; + } + if (!scl) { + printk(KERN_WARNING "%s: SCL unexpected low " + "while pulling SDA low!\n", name); + goto bailout; + } + + sdahi(adap); + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (!sda) { + printk(KERN_WARNING "%s: SDA stuck low!\n", name); + goto bailout; + } + if (!scl) { + printk(KERN_WARNING "%s: SCL unexpected low " + "while pulling SDA high!\n", name); + goto bailout; + } + + scllo(adap); + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 0 : getscl(adap); + if (scl) { + printk(KERN_WARNING "%s: SCL stuck high!\n", name); + goto bailout; + } + if (!sda) { + printk(KERN_WARNING "%s: SDA unexpected low " + "while pulling SCL low!\n", name); + goto bailout; + } + + sclhi(adap); + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (!scl) { + printk(KERN_WARNING "%s: SCL stuck low!\n", name); + goto bailout; + } + if (!sda) { + printk(KERN_WARNING "%s: SDA unexpected low " + "while pulling SCL high!\n", name); + goto bailout; + } + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + + pr_info("%s: Test OK\n", name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + + return -ENODEV; +} + +/* ----- Utility functions + */ + +/* try_address tries to contact a chip for a number of + * times before it gives up. + * return values: + * 1 chip answered + * 0 chip did not answer + * -x transmission error + */ +static int try_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i, ret = 0; + + for (i = 0; i <= retries; i++) { + ret = i2c_outb(i2c_adap, addr); + if (ret == 1 || i == retries) + break; + bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); + i2c_stop(adap); + udelay(adap->udelay); + yield(); + bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); + i2c_start(adap); + } + if (i && ret) + bit_dbg(1, &i2c_adap->dev, "Used %d tries to %s client at " + "0x%02x: %s\n", i + 1, + addr & 1 ? "read from" : "write to", addr >> 1, + ret == 1 ? "success" : "failed, timeout?"); + return ret; +} + +static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + const unsigned char *temp = msg->buf; + int count = msg->len; + unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + int retval; + int wrcount = 0; + + while (count > 0) { + retval = i2c_outb(i2c_adap, *temp); + + /* OK/ACK; or ignored NAK */ + if ((retval > 0) || (nak_ok && (retval == 0))) { + count--; + temp++; + wrcount++; + + /* A slave NAKing the master means the slave didn't like + * something about the data it saw. For example, maybe + * the SMBus PEC was wrong. + */ + } else if (retval == 0) { + dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.\n"); + return -EIO; + + /* Timeout; or (someday) lost arbitration + * + * FIXME Lost ARB implies retrying the transaction from + * the first message, after the "winning" master issues + * its STOP. As a rule, upper layer code has no reason + * to know or care about this ... it is *NOT* an error. + */ + } else { + dev_err(&i2c_adap->dev, "sendbytes: error %d\n", + retval); + return retval; + } + } + return wrcount; +} + +static int acknak(struct i2c_adapter *i2c_adap, int is_ack) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: sda is high */ + if (is_ack) /* send ack */ + setsda(adap, 0); + udelay((adap->udelay + 1) / 2); + if (sclhi(adap) < 0) { /* timeout */ + dev_err(&i2c_adap->dev, "readbytes: ack/nak timeout\n"); + return -ETIMEDOUT; + } + scllo(adap); + return 0; +} + +static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + int inval; + int rdcount = 0; /* counts bytes read */ + unsigned char *temp = msg->buf; + int count = msg->len; + const unsigned flags = msg->flags; + + while (count > 0) { + inval = i2c_inb(i2c_adap); + if (inval >= 0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + break; + } + + temp++; + count--; + + /* Some SMBus transactions require that we receive the + transaction length as the first read byte. */ + if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) { + if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) { + if (!(flags & I2C_M_NO_RD_ACK)) + acknak(i2c_adap, 0); + dev_err(&i2c_adap->dev, "readbytes: invalid " + "block length (%d)\n", inval); + return -EREMOTEIO; + } + /* The original count value accounts for the extra + bytes, that is, either 1 for a regular transaction, + or 2 for a PEC transaction. */ + count += inval; + msg->len += inval; + } + + bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n", + inval, + (flags & I2C_M_NO_RD_ACK) + ? "(no ack/nak)" + : (count ? "A" : "NA")); + + if (!(flags & I2C_M_NO_RD_ACK)) { + inval = acknak(i2c_adap, count); + if (inval < 0) + return inval; + } + } + return rdcount; +} + +/* doAddress initiates the transfer by generating the start condition (in + * try_address) and transmits the address in the necessary format to handle + * reads, writes as well as 10bit-addresses. + * returns: + * 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set + * -x an error occurred (like: -EREMOTEIO if the device did not answer, or + * -ETIMEDOUT, for example if the lines are stuck...) + */ +static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + unsigned short flags = msg->flags; + unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + unsigned char addr; + int ret, retries; + + retries = nak_ok ? 0 : i2c_adap->retries; + + if (flags & I2C_M_TEN) { + /* a ten bit address */ + addr = 0xf0 | ((msg->addr >> 7) & 0x06); + bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr); + /* try extended address code...*/ + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) { + dev_err(&i2c_adap->dev, + "died at extended address code\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(i2c_adap, msg->addr & 0xff); + if ((ret != 1) && !nak_ok) { + /* the chip did not ack / xmission error occurred */ + dev_err(&i2c_adap->dev, "died at 2nd address code\n"); + return -EREMOTEIO; + } + if (flags & I2C_M_RD) { + bit_dbg(3, &i2c_adap->dev, "emitting repeated " + "start condition\n"); + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) { + dev_err(&i2c_adap->dev, + "died at repeated address code\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = msg->addr << 1; + if (flags & I2C_M_RD) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) + return -ENXIO; + } + + return 0; +} + +static int bit_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i, ret; + unsigned short nak_ok; + + if (adap->pre_xfer) { + ret = adap->pre_xfer(i2c_adap); + if (ret < 0) + return ret; + } + + bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); + i2c_start(adap); + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; + if (!(pmsg->flags & I2C_M_NOSTART)) { + if (i) { + bit_dbg(3, &i2c_adap->dev, "emitting " + "repeated start condition\n"); + i2c_repstart(adap); + } + ret = bit_doAddress(i2c_adap, pmsg); + if ((ret != 0) && !nak_ok) { + bit_dbg(1, &i2c_adap->dev, "NAK from " + "device addr 0x%02x msg #%d\n", + msgs[i].addr, i); + goto bailout; + } + } + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer*/ + ret = readbytes(i2c_adap, pmsg); + if (ret >= 1) + bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n", + ret, ret == 1 ? "" : "s"); + if (ret < pmsg->len) { + if (ret >= 0) + ret = -EREMOTEIO; + goto bailout; + } + } else { + /* write bytes from buffer */ + ret = sendbytes(i2c_adap, pmsg); + if (ret >= 1) + bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n", + ret, ret == 1 ? "" : "s"); + if (ret < pmsg->len) { + if (ret >= 0) + ret = -EREMOTEIO; + goto bailout; + } + } + } + ret = i; + +bailout: + bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); + i2c_stop(adap); + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + return ret; +} + +static u32 bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static const struct i2c_algorithm i2c_bit_algo = { + .master_xfer = bit_xfer, + .functionality = bit_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +static int __i2c_bit_add_bus(struct i2c_adapter *adap, + int (*add_adapter)(struct i2c_adapter *)) +{ + struct i2c_algo_bit_data *bit_adap = adap->algo_data; + int ret; + + if (bit_test) { + ret = test_bus(adap); + if (ret < 0) + return -ENODEV; + } + + /* register new adapter to i2c module... */ + adap->algo = &i2c_bit_algo; + adap->retries = 3; + + ret = add_adapter(adap); + if (ret < 0) + return ret; + + /* Complain if SCL can't be read */ + if (bit_adap->getscl == NULL) { + dev_warn(&adap->dev, "Not I2C compliant: can't read SCL\n"); + dev_warn(&adap->dev, "Bus may be unreliable\n"); + } + return 0; +} + +int i2c_bit_add_bus(struct i2c_adapter *adap) +{ + return __i2c_bit_add_bus(adap, i2c_add_adapter); +} +EXPORT_SYMBOL(i2c_bit_add_bus); + +int i2c_bit_add_numbered_bus(struct i2c_adapter *adap) +{ + return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter); +} +EXPORT_SYMBOL(i2c_bit_add_numbered_bus); + +MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c new file mode 100644 index 00000000..4ca9cf9c --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -0,0 +1,563 @@ +/* + * i2c-algo-pca.c i2c driver algorithms for PCA9564 adapters + * Copyright (C) 2004 Arcom Control Systems + * Copyright (C) 2008 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. + * + * 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/moduleparam.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-pca.h> + +#define DEB1(fmt, args...) do { if (i2c_debug >= 1) \ + printk(KERN_DEBUG fmt, ## args); } while (0) +#define DEB2(fmt, args...) do { if (i2c_debug >= 2) \ + printk(KERN_DEBUG fmt, ## args); } while (0) +#define DEB3(fmt, args...) do { if (i2c_debug >= 3) \ + printk(KERN_DEBUG fmt, ## args); } while (0) + +static int i2c_debug; + +#define pca_outw(adap, reg, val) adap->write_byte(adap->data, reg, val) +#define pca_inw(adap, reg) adap->read_byte(adap->data, reg) + +#define pca_status(adap) pca_inw(adap, I2C_PCA_STA) +#define pca_clock(adap) adap->i2c_clock +#define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val) +#define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON) +#define pca_wait(adap) adap->wait_for_completion(adap->data) +#define pca_reset(adap) adap->reset_chip(adap->data) + +static void pca9665_reset(void *pd) +{ + struct i2c_algo_pca_data *adap = pd; + pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET); + pca_outw(adap, I2C_PCA_IND, 0xA5); + pca_outw(adap, I2C_PCA_IND, 0x5A); +} + +/* + * Generate a start condition on the i2c bus. + * + * returns after the start condition has occurred + */ +static int pca_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + return pca_wait(adap); +} + +/* + * Generate a repeated start condition on the i2c bus + * + * return after the repeated start condition has occurred + */ +static int pca_repeated_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== REPEATED START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + return pca_wait(adap); +} + +/* + * Generate a stop condition on the i2c bus + * + * returns after the stop condition has been generated + * + * STOPs do not generate an interrupt or set the SI flag, since the + * part returns the idle state (0xf8). Hence we don't need to + * pca_wait here. + */ +static void pca_stop(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== STOP\n"); + sta |= I2C_PCA_CON_STO; + sta &= ~(I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); +} + +/* + * Send the slave address and R/W bit + * + * returns after the address has been sent + */ +static int pca_address(struct i2c_algo_pca_data *adap, + struct i2c_msg *msg) +{ + int sta = pca_get_con(adap); + int addr; + + addr = ((0x7f & msg->addr) << 1); + if (msg->flags & I2C_M_RD) + addr |= 1; + DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n", + msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr); + + pca_outw(adap, I2C_PCA_DAT, addr); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + return pca_wait(adap); +} + +/* + * Transmit a byte. + * + * Returns after the byte has been transmitted + */ +static int pca_tx_byte(struct i2c_algo_pca_data *adap, + __u8 b) +{ + int sta = pca_get_con(adap); + DEB2("=== WRITE %#04x\n", b); + pca_outw(adap, I2C_PCA_DAT, b); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + return pca_wait(adap); +} + +/* + * Receive a byte + * + * returns immediately. + */ +static void pca_rx_byte(struct i2c_algo_pca_data *adap, + __u8 *b, int ack) +{ + *b = pca_inw(adap, I2C_PCA_DAT); + DEB2("=== READ %#04x %s\n", *b, ack ? "ACK" : "NACK"); +} + +/* + * Setup ACK or NACK for next received byte and wait for it to arrive. + * + * Returns after next byte has arrived. + */ +static int pca_rx_ack(struct i2c_algo_pca_data *adap, + int ack) +{ + int sta = pca_get_con(adap); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI|I2C_PCA_CON_AA); + + if (ack) + sta |= I2C_PCA_CON_AA; + + pca_set_con(adap, sta); + return pca_wait(adap); +} + +static int pca_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_pca_data *adap = i2c_adap->algo_data; + struct i2c_msg *msg = NULL; + int curmsg; + int numbytes = 0; + int state; + int ret; + int completed = 1; + unsigned long timeout = jiffies + i2c_adap->timeout; + + while ((state = pca_status(adap)) != 0xf8) { + if (time_before(jiffies, timeout)) { + msleep(10); + } else { + dev_dbg(&i2c_adap->dev, "bus is not idle. status is " + "%#04x\n", state); + return -EAGAIN; + } + } + + DEB1("{{{ XFER %d messages\n", num); + + if (i2c_debug >= 2) { + for (curmsg = 0; curmsg < num; curmsg++) { + int addr, i; + msg = &msgs[curmsg]; + + addr = (0x7f & msg->addr) ; + + if (msg->flags & I2C_M_RD) + printk(KERN_INFO " [%02d] RD %d bytes from %#02x [%#02x, ...]\n", + curmsg, msg->len, addr, (addr << 1) | 1); + else { + printk(KERN_INFO " [%02d] WR %d bytes to %#02x [%#02x%s", + curmsg, msg->len, addr, addr << 1, + msg->len == 0 ? "" : ", "); + for (i = 0; i < msg->len; i++) + printk("%#04x%s", msg->buf[i], i == msg->len - 1 ? "" : ", "); + printk("]\n"); + } + } + } + + curmsg = 0; + ret = -EREMOTEIO; + while (curmsg < num) { + state = pca_status(adap); + + DEB3("STATE is 0x%02x\n", state); + msg = &msgs[curmsg]; + + switch (state) { + case 0xf8: /* On reset or stop the bus is idle */ + completed = pca_start(adap); + break; + + case 0x08: /* A START condition has been transmitted */ + case 0x10: /* A repeated start condition has been transmitted */ + completed = pca_address(adap, msg); + break; + + case 0x18: /* SLA+W has been transmitted; ACK has been received */ + case 0x28: /* Data byte in I2CDAT has been transmitted; ACK has been received */ + if (numbytes < msg->len) { + completed = pca_tx_byte(adap, + msg->buf[numbytes]); + numbytes++; + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + completed = pca_repeated_start(adap); + break; + + case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after SLA+W\n"); + pca_stop(adap); + goto out; + + case 0x40: /* SLA+R has been transmitted; ACK has been received */ + completed = pca_rx_ack(adap, msg->len > 1); + break; + + case 0x50: /* Data bytes has been received; ACK has been returned */ + if (numbytes < msg->len) { + pca_rx_byte(adap, &msg->buf[numbytes], 1); + numbytes++; + completed = pca_rx_ack(adap, + numbytes < msg->len - 1); + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + completed = pca_repeated_start(adap); + break; + + case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after SLA+R\n"); + pca_stop(adap); + goto out; + + case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after data byte\n"); + pca_stop(adap); + goto out; + + case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */ + DEB2("Arbitration lost\n"); + /* + * The PCA9564 data sheet (2006-09-01) says "A + * START condition will be transmitted when the + * bus becomes free (STOP or SCL and SDA high)" + * when the STA bit is set (p. 11). + * + * In case this won't work, try pca_reset() + * instead. + */ + pca_start(adap); + goto out; + + case 0x58: /* Data byte has been received; NOT ACK has been returned */ + if (numbytes == msg->len - 1) { + pca_rx_byte(adap, &msg->buf[numbytes], 0); + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + completed = pca_repeated_start(adap); + } else { + DEB2("NOT ACK sent after data byte received. " + "Not final byte. numbytes %d. len %d\n", + numbytes, msg->len); + pca_stop(adap); + goto out; + } + break; + case 0x70: /* Bus error - SDA stuck low */ + DEB2("BUS ERROR - SDA Stuck low\n"); + pca_reset(adap); + goto out; + case 0x90: /* Bus error - SCL stuck low */ + DEB2("BUS ERROR - SCL Stuck low\n"); + pca_reset(adap); + goto out; + case 0x00: /* Bus error during master or slave mode due to illegal START or STOP condition */ + DEB2("BUS ERROR - Illegal START or STOP\n"); + pca_reset(adap); + goto out; + default: + dev_err(&i2c_adap->dev, "unhandled SIO state 0x%02x\n", state); + break; + } + + if (!completed) + goto out; + } + + ret = curmsg; + out: + DEB1("}}} transferred %d/%d messages. " + "status is %#04x. control is %#04x\n", + curmsg, num, pca_status(adap), + pca_get_con(adap)); + return ret; +} + +static u32 pca_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm pca_algo = { + .master_xfer = pca_xfer, + .functionality = pca_func, +}; + +static unsigned int pca_probe_chip(struct i2c_adapter *adap) +{ + struct i2c_algo_pca_data *pca_data = adap->algo_data; + /* The trick here is to check if there is an indirect register + * available. If there is one, we will read the value we first + * wrote on I2C_PCA_IADR. Otherwise, we will read the last value + * we wrote on I2C_PCA_ADR + */ + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IADR); + pca_outw(pca_data, I2C_PCA_IND, 0xAA); + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ITO); + pca_outw(pca_data, I2C_PCA_IND, 0x00); + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IADR); + if (pca_inw(pca_data, I2C_PCA_IND) == 0xAA) { + printk(KERN_INFO "%s: PCA9665 detected.\n", adap->name); + return I2C_PCA_CHIP_9665; + } else { + printk(KERN_INFO "%s: PCA9564 detected.\n", adap->name); + return I2C_PCA_CHIP_9564; + } +} + +static int pca_init(struct i2c_adapter *adap) +{ + struct i2c_algo_pca_data *pca_data = adap->algo_data; + + adap->algo = &pca_algo; + + if (pca_probe_chip(adap) == I2C_PCA_CHIP_9564) { + static int freqs[] = {330, 288, 217, 146, 88, 59, 44, 36}; + int clock; + + if (pca_data->i2c_clock > 7) { + switch (pca_data->i2c_clock) { + case 330000: + pca_data->i2c_clock = I2C_PCA_CON_330kHz; + break; + case 288000: + pca_data->i2c_clock = I2C_PCA_CON_288kHz; + break; + case 217000: + pca_data->i2c_clock = I2C_PCA_CON_217kHz; + break; + case 146000: + pca_data->i2c_clock = I2C_PCA_CON_146kHz; + break; + case 88000: + pca_data->i2c_clock = I2C_PCA_CON_88kHz; + break; + case 59000: + pca_data->i2c_clock = I2C_PCA_CON_59kHz; + break; + case 44000: + pca_data->i2c_clock = I2C_PCA_CON_44kHz; + break; + case 36000: + pca_data->i2c_clock = I2C_PCA_CON_36kHz; + break; + default: + printk(KERN_WARNING + "%s: Invalid I2C clock speed selected." + " Using default 59kHz.\n", adap->name); + pca_data->i2c_clock = I2C_PCA_CON_59kHz; + } + } else { + printk(KERN_WARNING "%s: " + "Choosing the clock frequency based on " + "index is deprecated." + " Use the nominal frequency.\n", adap->name); + } + + pca_reset(pca_data); + + clock = pca_clock(pca_data); + printk(KERN_INFO "%s: Clock frequency is %dkHz\n", + adap->name, freqs[clock]); + + pca_set_con(pca_data, I2C_PCA_CON_ENSIO | clock); + } else { + int clock; + int mode; + int tlow, thi; + /* Values can be found on PCA9665 datasheet section 7.3.2.6 */ + int min_tlow, min_thi; + /* These values are the maximum raise and fall values allowed + * by the I2C operation mode (Standard, Fast or Fast+) + * They are used (added) below to calculate the clock dividers + * of PCA9665. Note that they are slightly different of the + * real maximum, to allow the change on mode exactly on the + * maximum clock rate for each mode + */ + int raise_fall_time; + + /* Ignore the reset function from the module, + * we can use the parallel bus reset + */ + pca_data->reset_chip = pca9665_reset; + + if (pca_data->i2c_clock > 1265800) { + printk(KERN_WARNING "%s: I2C clock speed too high." + " Using 1265.8kHz.\n", adap->name); + pca_data->i2c_clock = 1265800; + } + + if (pca_data->i2c_clock < 60300) { + printk(KERN_WARNING "%s: I2C clock speed too low." + " Using 60.3kHz.\n", adap->name); + pca_data->i2c_clock = 60300; + } + + /* To avoid integer overflow, use clock/100 for calculations */ + clock = pca_clock(pca_data) / 100; + + if (pca_data->i2c_clock > 10000) { + mode = I2C_PCA_MODE_TURBO; + min_tlow = 14; + min_thi = 5; + raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */ + } else if (pca_data->i2c_clock > 4000) { + mode = I2C_PCA_MODE_FASTP; + min_tlow = 17; + min_thi = 9; + raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */ + } else if (pca_data->i2c_clock > 1000) { + mode = I2C_PCA_MODE_FAST; + min_tlow = 44; + min_thi = 20; + raise_fall_time = 58; /* Raise 29e-8s, Fall 29e-8s */ + } else { + mode = I2C_PCA_MODE_STD; + min_tlow = 157; + min_thi = 134; + raise_fall_time = 127; /* Raise 29e-8s, Fall 98e-8s */ + } + + /* The minimum clock that respects the thi/tlow = 134/157 is + * 64800 Hz. Below that, we have to fix the tlow to 255 and + * calculate the thi factor. + */ + if (clock < 648) { + tlow = 255; + thi = 1000000 - clock * raise_fall_time; + thi /= (I2C_PCA_OSC_PER * clock) - tlow; + } else { + tlow = (1000000 - clock * raise_fall_time) * min_tlow; + tlow /= I2C_PCA_OSC_PER * clock * (min_thi + min_tlow); + thi = tlow * min_thi / min_tlow; + } + + pca_reset(pca_data); + + printk(KERN_INFO + "%s: Clock frequency is %dHz\n", adap->name, clock * 100); + + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IMODE); + pca_outw(pca_data, I2C_PCA_IND, mode); + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLL); + pca_outw(pca_data, I2C_PCA_IND, tlow); + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLH); + pca_outw(pca_data, I2C_PCA_IND, thi); + + pca_set_con(pca_data, I2C_PCA_CON_ENSIO); + } + udelay(500); /* 500 us for oscilator to stabilise */ + + return 0; +} + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pca_add_bus(struct i2c_adapter *adap) +{ + int rval; + + rval = pca_init(adap); + if (rval) + return rval; + + return i2c_add_adapter(adap); +} +EXPORT_SYMBOL(i2c_pca_add_bus); + +int i2c_pca_add_numbered_bus(struct i2c_adapter *adap) +{ + int rval; + + rval = pca_init(adap); + if (rval) + return rval; + + return i2c_add_numbered_adapter(adap); +} +EXPORT_SYMBOL(i2c_pca_add_numbered_bus); + +MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>, " + "Wolfram Sang <w.sang@pengutronix.de>"); +MODULE_DESCRIPTION("I2C-Bus PCA9564/PCA9665 algorithm"); +MODULE_LICENSE("GPL"); + +module_param(i2c_debug, int, 0); diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c new file mode 100644 index 00000000..5eebf562 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pcf.c @@ -0,0 +1,441 @@ +/* + * i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters + * + * Copyright (C) 1995-1997 Simon G. Vogl + * 1998-2000 Hans Berglund + * + * 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. + * + * With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and + * Frodo Looijaard <frodol@dds.nl>, and also from Martin Bailey + * <mbailey@littlefeet-inc.com> + * + * Partially rewriten by Oleg I. Vdovikin <vdovikin@jscc.ru> to handle multiple + * messages, proper stop/repstart signaling during receive, added detect code + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-pcf.h> +#include "i2c-algo-pcf.h" + + +#define DEB2(x) if (i2c_debug >= 2) x +#define DEB3(x) if (i2c_debug >= 3) x /* print several statistical values */ +#define DEBPROTO(x) if (i2c_debug >= 9) x; + /* debug the protocol by showing transferred bits */ +#define DEF_TIMEOUT 16 + +/* + * module parameters: + */ +static int i2c_debug; + +/* setting states on the bus with the right timing: */ + +#define set_pcf(adap, ctl, val) adap->setpcf(adap->data, ctl, val) +#define get_pcf(adap, ctl) adap->getpcf(adap->data, ctl) +#define get_own(adap) adap->getown(adap->data) +#define get_clock(adap) adap->getclock(adap->data) +#define i2c_outb(adap, val) adap->setpcf(adap->data, 0, val) +#define i2c_inb(adap) adap->getpcf(adap->data, 0) + +/* other auxiliary functions */ + +static void i2c_start(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk(KERN_DEBUG "S ")); + set_pcf(adap, 1, I2C_PCF_START); +} + +static void i2c_repstart(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk(" Sr ")); + set_pcf(adap, 1, I2C_PCF_REPSTART); +} + +static void i2c_stop(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk("P\n")); + set_pcf(adap, 1, I2C_PCF_STOP); +} + +static void handle_lab(struct i2c_algo_pcf_data *adap, const int *status) +{ + DEB2(printk(KERN_INFO + "i2c-algo-pcf.o: lost arbitration (CSR 0x%02x)\n", + *status)); + /* + * Cleanup from LAB -- reset and enable ESO. + * This resets the PCF8584; since we've lost the bus, no + * further attempts should be made by callers to clean up + * (no i2c_stop() etc.) + */ + set_pcf(adap, 1, I2C_PCF_PIN); + set_pcf(adap, 1, I2C_PCF_ESO); + /* + * We pause for a time period sufficient for any running + * I2C transaction to complete -- the arbitration logic won't + * work properly until the next START is seen. + * It is assumed the bus driver or client has set a proper value. + * + * REVISIT: should probably use msleep instead of mdelay if we + * know we can sleep. + */ + if (adap->lab_mdelay) + mdelay(adap->lab_mdelay); + + DEB2(printk(KERN_INFO + "i2c-algo-pcf.o: reset LAB condition (CSR 0x%02x)\n", + get_pcf(adap, 1))); +} + +static int wait_for_bb(struct i2c_algo_pcf_data *adap) +{ + + int timeout = DEF_TIMEOUT; + int status; + + status = get_pcf(adap, 1); + + while (!(status & I2C_PCF_BB) && --timeout) { + udelay(100); /* wait for 100 us */ + status = get_pcf(adap, 1); + } + + if (timeout == 0) { + printk(KERN_ERR "Timeout waiting for Bus Busy\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) +{ + + int timeout = DEF_TIMEOUT; + + *status = get_pcf(adap, 1); + + while ((*status & I2C_PCF_PIN) && --timeout) { + adap->waitforpin(adap->data); + *status = get_pcf(adap, 1); + } + if (*status & I2C_PCF_LAB) { + handle_lab(adap, status); + return -EINTR; + } + + if (timeout == 0) + return -ETIMEDOUT; + + return 0; +} + +/* + * This should perform the 'PCF8584 initialization sequence' as described + * in the Philips IC12 data book (1995, Aug 29). + * There should be a 30 clock cycle wait after reset, I assume this + * has been fulfilled. + * There should be a delay at the end equal to the longest I2C message + * to synchronize the BB-bit (in multimaster systems). How long is + * this? I assume 1 second is always long enough. + * + * vdovikin: added detect code for PCF8584 + */ +static int pcf_init_8584 (struct i2c_algo_pcf_data *adap) +{ + unsigned char temp; + + DEB3(printk(KERN_DEBUG "i2c-algo-pcf.o: PCF state 0x%02x\n", + get_pcf(adap, 1))); + + /* S1=0x80: S0 selected, serial interface off */ + set_pcf(adap, 1, I2C_PCF_PIN); + /* + * check to see S1 now used as R/W ctrl - + * PCF8584 does that when ESO is zero + */ + if (((temp = get_pcf(adap, 1)) & 0x7f) != (0)) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S0 (0x%02x).\n", temp)); + return -ENXIO; /* definitely not PCF8584 */ + } + + /* load own address in S0, effective address is (own << 1) */ + i2c_outb(adap, get_own(adap)); + /* check it's really written */ + if ((temp = i2c_inb(adap)) != get_own(adap)) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't set S0 (0x%02x).\n", temp)); + return -ENXIO; + } + + /* S1=0xA0, next byte in S2 */ + set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1); + /* check to see S2 now selected */ + if (((temp = get_pcf(adap, 1)) & 0x7f) != I2C_PCF_ES1) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S2 (0x%02x).\n", temp)); + return -ENXIO; + } + + /* load clock register S2 */ + i2c_outb(adap, get_clock(adap)); + /* check it's really written, the only 5 lowest bits does matter */ + if (((temp = i2c_inb(adap)) & 0x1f) != get_clock(adap)) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't set S2 (0x%02x).\n", temp)); + return -ENXIO; + } + + /* Enable serial interface, idle, S0 selected */ + set_pcf(adap, 1, I2C_PCF_IDLE); + + /* check to see PCF is really idled and we can access status register */ + if ((temp = get_pcf(adap, 1)) != (I2C_PCF_PIN | I2C_PCF_BB)) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: PCF detection failed -- can't select S1` (0x%02x).\n", temp)); + return -ENXIO; + } + + printk(KERN_DEBUG "i2c-algo-pcf.o: detected and initialized PCF8584.\n"); + + return 0; +} + +static int pcf_sendbytes(struct i2c_adapter *i2c_adap, const char *buf, + int count, int last) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wrcount, status, timeout; + + for (wrcount=0; wrcount<count; ++wrcount) { + DEB2(dev_dbg(&i2c_adap->dev, "i2c_write: writing %2.2X\n", + buf[wrcount] & 0xff)); + i2c_outb(adap, buf[wrcount]); + timeout = wait_for_pin(adap, &status); + if (timeout) { + if (timeout == -EINTR) + return -EINTR; /* arbitration lost */ + + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_write: error - timeout.\n"); + return -EREMOTEIO; /* got a better one ?? */ + } + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_write: error - no ack.\n"); + return -EREMOTEIO; /* got a better one ?? */ + } + } + if (last) + i2c_stop(adap); + else + i2c_repstart(adap); + + return wrcount; +} + +static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, + int count, int last) +{ + int i, status; + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wfp; + + /* increment number of bytes to read by one -- read dummy byte */ + for (i = 0; i <= count; i++) { + + if ((wfp = wait_for_pin(adap, &status))) { + if (wfp == -EINTR) + return -EINTR; /* arbitration lost */ + + i2c_stop(adap); + dev_err(&i2c_adap->dev, "pcf_readbytes timed out.\n"); + return -1; + } + + if ((status & I2C_PCF_LRB) && (i != count)) { + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_read: i2c_inb, No ack.\n"); + return -1; + } + + if (i == count - 1) { + set_pcf(adap, 1, I2C_PCF_ESO); + } else if (i == count) { + if (last) + i2c_stop(adap); + else + i2c_repstart(adap); + } + + if (i) + buf[i - 1] = i2c_inb(adap); + else + i2c_inb(adap); /* dummy read */ + } + + return i - 1; +} + + +static int pcf_doAddress(struct i2c_algo_pcf_data *adap, + struct i2c_msg *msg) +{ + unsigned short flags = msg->flags; + unsigned char addr; + + addr = msg->addr << 1; + if (flags & I2C_M_RD) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + i2c_outb(adap, addr); + + return 0; +} + +static int pcf_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i; + int ret=0, timeout, status; + + if (adap->xfer_begin) + adap->xfer_begin(adap->data); + + /* Check for bus busy */ + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: " + "Timeout waiting for BB in pcf_xfer\n");) + i = -EIO; + goto out; + } + + for (i = 0;ret >= 0 && i < num; i++) { + pmsg = &msgs[i]; + + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: Doing %s %d bytes to 0x%02x - %d of %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->addr, i + 1, num);) + + ret = pcf_doAddress(adap, pmsg); + + /* Send START */ + if (i == 0) + i2c_start(adap); + + /* Wait for PIN (pending interrupt NOT) */ + timeout = wait_for_pin(adap, &status); + if (timeout) { + if (timeout == -EINTR) { + /* arbitration lost */ + i = -EINTR; + goto out; + } + i2c_stop(adap); + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: Timeout waiting " + "for PIN(1) in pcf_xfer\n");) + i = -EREMOTEIO; + goto out; + } + + /* Check LRB (last rcvd bit - slave ack) */ + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + DEB2(printk(KERN_ERR "i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");) + i = -EREMOTEIO; + goto out; + } + + DEB3(printk(KERN_DEBUG "i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n", + i, msgs[i].addr, msgs[i].flags, msgs[i].len);) + + if (pmsg->flags & I2C_M_RD) { + ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); + + if (ret != pmsg->len) { + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: " + "only read %d bytes.\n",ret)); + } else { + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: read %d bytes.\n",ret)); + } + } else { + ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); + + if (ret != pmsg->len) { + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: " + "only wrote %d bytes.\n",ret)); + } else { + DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: wrote %d bytes.\n",ret)); + } + } + } + +out: + if (adap->xfer_end) + adap->xfer_end(adap->data); + return i; +} + +static u32 pcf_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_PROTOCOL_MANGLING; +} + +/* exported algorithm data: */ +static const struct i2c_algorithm pcf_algo = { + .master_xfer = pcf_xfer, + .functionality = pcf_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct i2c_adapter *adap) +{ + struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; + int rval; + + DEB2(dev_dbg(&adap->dev, "hw routines registered.\n")); + + /* register new adapter to i2c module... */ + adap->algo = &pcf_algo; + + if ((rval = pcf_init_8584(pcf_adap))) + return rval; + + rval = i2c_add_adapter(adap); + + return rval; +} +EXPORT_SYMBOL(i2c_pcf_add_bus); + +MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); +MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); +MODULE_LICENSE("GPL"); + +module_param(i2c_debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); diff --git a/drivers/i2c/algos/i2c-algo-pcf.h b/drivers/i2c/algos/i2c-algo-pcf.h new file mode 100644 index 00000000..5263a9ee --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pcf.h @@ -0,0 +1,76 @@ +/* -------------------------------------------------------------------- */ +/* i2c-pcf8584.h: PCF 8584 global defines */ +/* -------------------------------------------------------------------- */ +/* Copyright (C) 1996 Simon G. Vogl + 1999 Hans Berglund + + 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. */ +/* -------------------------------------------------------------------- */ + +/* With some changes from Frodo Looijaard <frodol@dds.nl> */ + +#ifndef I2C_PCF8584_H +#define I2C_PCF8584_H 1 + +/* ----- Control register bits ---------------------------------------- */ +#define I2C_PCF_PIN 0x80 +#define I2C_PCF_ESO 0x40 +#define I2C_PCF_ES1 0x20 +#define I2C_PCF_ES2 0x10 +#define I2C_PCF_ENI 0x08 +#define I2C_PCF_STA 0x04 +#define I2C_PCF_STO 0x02 +#define I2C_PCF_ACK 0x01 + +#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) +#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) + +/* ----- Status register bits ----------------------------------------- */ +/*#define I2C_PCF_PIN 0x80 as above*/ + +#define I2C_PCF_INI 0x40 /* 1 if not initialized */ +#define I2C_PCF_STS 0x20 +#define I2C_PCF_BER 0x10 +#define I2C_PCF_AD0 0x08 +#define I2C_PCF_LRB 0x08 +#define I2C_PCF_AAS 0x04 +#define I2C_PCF_LAB 0x02 +#define I2C_PCF_BB 0x01 + +/* ----- Chip clock frequencies --------------------------------------- */ +#define I2C_PCF_CLK3 0x00 +#define I2C_PCF_CLK443 0x10 +#define I2C_PCF_CLK6 0x14 +#define I2C_PCF_CLK 0x18 +#define I2C_PCF_CLK12 0x1c + +/* ----- transmission frequencies ------------------------------------- */ +#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ +#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ +#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ +#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ + + +/* ----- Access to internal registers according to ES1,ES2 ------------ */ +/* they are mapped to the data port ( a0 = 0 ) */ +/* available when ESO == 0 : */ + +#define I2C_PCF_OWNADR 0 +#define I2C_PCF_INTREG I2C_PCF_ES2 +#define I2C_PCF_CLKREG I2C_PCF_ES1 + +#endif /* I2C_PCF8584_H */ diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig new file mode 100644 index 00000000..646068e5 --- /dev/null +++ b/drivers/i2c/busses/Kconfig @@ -0,0 +1,880 @@ +# +# Sensor device configuration +# + +menu "I2C Hardware Bus support" + +comment "PC SMBus host controller drivers" + depends on PCI + +config I2C_ALI1535 + tristate "ALI 1535" + depends on PCI + help + If you say yes to this option, support will be included for the SMB + Host controller on Acer Labs Inc. (ALI) M1535 South Bridges. The SMB + controller is part of the 7101 device, which is an ACPI-compliant + Power Management Unit (PMU). + + This driver can also be built as a module. If so, the module + will be called i2c-ali1535. + +config I2C_ALI1563 + tristate "ALI 1563" + depends on PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the SMB + Host controller on Acer Labs Inc. (ALI) M1563 South Bridges. The SMB + controller is part of the 7101 device, which is an ACPI-compliant + Power Management Unit (PMU). + + This driver can also be built as a module. If so, the module + will be called i2c-ali1563. + +config I2C_ALI15X3 + tristate "ALI 15x3" + depends on PCI + help + If you say yes to this option, support will be included for the + Acer Labs Inc. (ALI) M1514 and M1543 motherboard I2C interfaces. + + This driver can also be built as a module. If so, the module + will be called i2c-ali15x3. + +config I2C_AMD756 + tristate "AMD 756/766/768/8111 and nVidia nForce" + depends on PCI + help + If you say yes to this option, support will be included for the AMD + 756/766/768 mainboard I2C interfaces. The driver also includes + support for the first (SMBus 1.0) I2C interface of the AMD 8111 and + the nVidia nForce I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-amd756. + +config I2C_AMD756_S4882 + tristate "SMBus multiplexing on the Tyan S4882" + depends on I2C_AMD756 && X86 && EXPERIMENTAL + help + Enabling this option will add specific SMBus support for the Tyan + S4882 motherboard. On this 4-CPU board, the SMBus is multiplexed + over 8 different channels, where the various memory module EEPROMs + and temperature sensors live. Saying yes here will give you access + to these in addition to the trunk. + + This driver can also be built as a module. If so, the module + will be called i2c-amd756-s4882. + +config I2C_AMD8111 + tristate "AMD 8111" + depends on PCI + help + If you say yes to this option, support will be included for the + second (SMBus 2.0) AMD 8111 mainboard I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-amd8111. + +config I2C_I801 + tristate "Intel 82801 (ICH/PCH)" + depends on PCI + select CHECK_SIGNATURE if X86 && DMI + help + If you say yes to this option, support will be included for the Intel + 801 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset are supported: + 82801AA + 82801AB + 82801BA + 82801CA/CAM + 82801DB + 82801EB/ER (ICH5/ICH5R) + 6300ESB + ICH6 + ICH7 + ESB2 + ICH8 + ICH9 + EP80579 (Tolapai) + ICH10 + 5/3400 Series (PCH) + 6 Series (PCH) + Patsburg (PCH) + DH89xxCC (PCH) + Panther Point (PCH) + + This driver can also be built as a module. If so, the module + will be called i2c-i801. + +config I2C_ISCH + tristate "Intel SCH SMBus 1.0" + depends on PCI + select MFD_CORE + select LPC_SCH + help + Say Y here if you want to use SMBus controller on the Intel SCH + based systems. + + This driver can also be built as a module. If so, the module + will be called i2c-isch. + +config I2C_PIIX4 + tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)" + depends on PCI + help + If you say yes to this option, support will be included for the Intel + PIIX4 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset are supported (note that Serverworks is part + of Broadcom): + Intel PIIX4 + Intel 440MX + ATI IXP200 + ATI IXP300 + ATI IXP400 + ATI SB600 + ATI SB700 + ATI SB800 + AMD Hudson-2 + Serverworks OSB4 + Serverworks CSB5 + Serverworks CSB6 + Serverworks HT-1000 + Serverworks HT-1100 + SMSC Victory66 + + This driver can also be built as a module. If so, the module + will be called i2c-piix4. + +config I2C_NFORCE2 + tristate "Nvidia nForce2, nForce3 and nForce4" + depends on PCI + help + If you say yes to this option, support will be included for the Nvidia + nForce2, nForce3 and nForce4 families of mainboard I2C interfaces. + + This driver can also be built as a module. If so, the module + will be called i2c-nforce2. + +config I2C_NFORCE2_S4985 + tristate "SMBus multiplexing on the Tyan S4985" + depends on I2C_NFORCE2 && X86 && EXPERIMENTAL + help + Enabling this option will add specific SMBus support for the Tyan + S4985 motherboard. On this 4-CPU board, the SMBus is multiplexed + over 4 different channels, where the various memory module EEPROMs + live. Saying yes here will give you access to these in addition + to the trunk. + + This driver can also be built as a module. If so, the module + will be called i2c-nforce2-s4985. + +config I2C_SIS5595 + tristate "SiS 5595" + depends on PCI + help + If you say yes to this option, support will be included for the + SiS5595 SMBus (a subset of I2C) interface. + + This driver can also be built as a module. If so, the module + will be called i2c-sis5595. + +config I2C_SIS630 + tristate "SiS 630/730" + depends on PCI + help + If you say yes to this option, support will be included for the + SiS630 and SiS730 SMBus (a subset of I2C) interface. + + This driver can also be built as a module. If so, the module + will be called i2c-sis630. + +config I2C_SIS96X + tristate "SiS 96x" + depends on PCI + help + If you say yes to this option, support will be included for the SiS + 96x SMBus (a subset of I2C) interfaces. Specifically, the following + chipsets are supported: + 645/961 + 645DX/961 + 645DX/962 + 648/961 + 650/961 + 735 + 745 + + This driver can also be built as a module. If so, the module + will be called i2c-sis96x. + +config I2C_VIA + tristate "VIA VT82C586B" + depends on PCI && EXPERIMENTAL + select I2C_ALGOBIT + help + If you say yes to this option, support will be included for the VIA + 82C586B I2C interface + + This driver can also be built as a module. If so, the module + will be called i2c-via. + +config I2C_VIAPRO + tristate "VIA VT82C596/82C686/82xx and CX700/VX8xx" + depends on PCI + help + If you say yes to this option, support will be included for the VIA + VT82C596 and later SMBus interface. Specifically, the following + chipsets are supported: + VT82C596A/B + VT82C686A/B + VT8231 + VT8233/A + VT8235 + VT8237R/A/S + VT8251 + CX700 + VX800/VX820 + VX855/VX875 + + This driver can also be built as a module. If so, the module + will be called i2c-viapro. + +if ACPI + +comment "ACPI drivers" + +config I2C_SCMI + tristate "SMBus Control Method Interface" + help + This driver supports the SMBus Control Method Interface. It needs the + BIOS to declare ACPI control methods as described in the SMBus Control + Method Interface specification. + + To compile this driver as a module, choose M here: + the module will be called i2c-scmi. + +endif # ACPI + +comment "Mac SMBus host controller drivers" + depends on PPC_CHRP || PPC_PMAC + +config I2C_HYDRA + tristate "CHRP Apple Hydra Mac I/O I2C interface" + depends on PCI && PPC_CHRP && EXPERIMENTAL + select I2C_ALGOBIT + help + This supports the use of the I2C interface in the Apple Hydra Mac + I/O chip on some CHRP machines (e.g. the LongTrail). Say Y if you + have such a machine. + + This support is also available as a module. If so, the module + will be called i2c-hydra. + +config I2C_POWERMAC + tristate "Powermac I2C interface" + depends on PPC_PMAC + default y + help + This exposes the various PowerMac i2c interfaces to the linux i2c + layer and to userland. It is used by various drivers on the PowerMac + platform, and should generally be enabled. + + This support is also available as a module. If so, the module + will be called i2c-powermac. + +comment "I2C system bus drivers (mostly embedded / system-on-chip)" + +config I2C_AT91 + tristate "Atmel AT91 I2C Two-Wire interface (TWI)" + depends on ARCH_AT91 && EXPERIMENTAL && BROKEN + help + This supports the use of the I2C interface on Atmel AT91 + processors. + + This driver is BROKEN because the controller which it uses + will easily trigger RX overrun and TX underrun errors. Using + low I2C clock rates may partially work around those issues + on some systems. Another serious problem is that there is no + documented way to issue repeated START conditions, as needed + to support combined I2C messages. Use the i2c-gpio driver + unless your system can cope with those limitations. + +config I2C_AU1550 + tristate "Au1550/Au1200 SMBus interface" + depends on SOC_AU1550 || SOC_AU1200 + help + If you say yes to this option, support will be included for the + Au1550 and Au1200 SMBus interface. + + This driver can also be built as a module. If so, the module + will be called i2c-au1550. + +config I2C_BLACKFIN_TWI + tristate "Blackfin TWI I2C support" + depends on BLACKFIN + depends on !BF561 && !BF531 && !BF532 && !BF533 + help + This is the I2C bus driver for Blackfin on-chip TWI interface. + + This driver can also be built as a module. If so, the module + will be called i2c-bfin-twi. + +config I2C_BLACKFIN_TWI_CLK_KHZ + int "Blackfin TWI I2C clock (kHz)" + depends on I2C_BLACKFIN_TWI + range 21 400 + default 50 + help + The unit of the TWI clock is kHz. + +config I2C_CPM + tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)" + depends on (CPM1 || CPM2) && OF_I2C + help + This supports the use of the I2C interface on Freescale + processors with CPM1 or CPM2. + + This driver can also be built as a module. If so, the module + will be called i2c-cpm. + +config I2C_DAVINCI + tristate "DaVinci I2C driver" + depends on ARCH_DAVINCI + help + Support for TI DaVinci I2C controller driver. + + This driver can also be built as a module. If so, the module + will be called i2c-davinci. + + Please note that this driver might be needed to bring up other + devices such as DaVinci NIC. + For details please see http://www.ti.com/davinci + +config I2C_DESIGNWARE + tristate "Synopsys DesignWare" + depends on HAVE_CLK + help + If you say yes to this option, support will be included for the + Synopsys DesignWare I2C adapter. Only master mode is supported. + + This driver can also be built as a module. If so, the module + will be called i2c-designware. + +config I2C_GPIO + tristate "GPIO-based bitbanging I2C" + depends on GENERIC_GPIO + select I2C_ALGOBIT + help + This is a very simple bitbanging I2C driver utilizing the + arch-neutral GPIO API to control the SCL and SDA lines. + +config I2C_HIGHLANDER + tristate "Highlander FPGA SMBus interface" + depends on SH_HIGHLANDER + help + If you say yes to this option, support will be included for + the SMBus interface located in the FPGA on various Highlander + boards, particularly the R0P7780LC0011RL and R0P7785LC0011RL + FPGAs. This is wholly unrelated to the SoC I2C. + + This driver can also be built as a module. If so, the module + will be called i2c-highlander. + +config I2C_IBM_IIC + tristate "IBM PPC 4xx on-chip I2C interface" + depends on 4xx + help + Say Y here if you want to use IIC peripheral found on + embedded IBM PPC 4xx based systems. + + This driver can also be built as a module. If so, the module + will be called i2c-ibm_iic. + +config I2C_IMX + tristate "IMX I2C interface" + depends on ARCH_MXC + help + Say Y here if you want to use the IIC bus controller on + the Freescale i.MX/MXC processors. + + This driver can also be built as a module. If so, the module + will be called i2c-imx. + +config I2C_INTEL_MID + tristate "Intel Moorestown/Medfield Platform I2C controller" + depends on PCI + help + Say Y here if you have an Intel Moorestown/Medfield platform I2C + controller. + + This support is also available as a module. If so, the module + will be called i2c-intel-mid. + +config I2C_IOP3XX + tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface" + depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX + help + Say Y here if you want to use the IIC bus controller on + the Intel IOPx3xx I/O Processors or IXP4xx Network Processors. + + This driver can also be built as a module. If so, the module + will be called i2c-iop3xx. + +config I2C_IXP2000 + tristate "IXP2000 GPIO-Based I2C Interface (DEPRECATED)" + depends on ARCH_IXP2000 + select I2C_ALGOBIT + help + Say Y here if you have an Intel IXP2000 (2400, 2800, 2850) based + system and are using GPIO lines for an I2C bus. + + This support is also available as a module. If so, the module + will be called i2c-ixp2000. + + This driver is deprecated and will be dropped soon. Use i2c-gpio + instead. + +config I2C_MPC + tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx" + depends on PPC + help + If you say yes to this option, support will be included for the + built-in I2C interface on the MPC107, Tsi107, MPC512x, MPC52xx, + MPC8240, MPC8245, MPC83xx, MPC85xx and MPC8641 family processors. + + This driver can also be built as a module. If so, the module + will be called i2c-mpc. + +config I2C_MV64XXX + tristate "Marvell mv64xxx I2C Controller" + depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL + help + If you say yes to this option, support will be included for the + built-in I2C interface on the Marvell 64xxx line of host bridges. + + This driver can also be built as a module. If so, the module + will be called i2c-mv64xxx. + +config I2C_MXS + tristate "Freescale i.MX28 I2C interface" + depends on SOC_IMX28 + help + Say Y here if you want to use the I2C bus controller on + the Freescale i.MX28 processors. + + This driver can also be built as a module. If so, the module + will be called i2c-mxs. + +config I2C_NOMADIK + tristate "ST-Ericsson Nomadik/Ux500 I2C Controller" + depends on PLAT_NOMADIK + help + If you say yes to this option, support will be included for the + I2C interface from ST-Ericsson's Nomadik and Ux500 architectures. + +config I2C_NUC900 + tristate "NUC900 I2C Driver" + depends on ARCH_W90X900 + help + Say Y here to include support for I2C controller in the + Winbond/Nuvoton NUC900 based System-on-Chip devices. + +config I2C_OCORES + tristate "OpenCores I2C Controller" + depends on EXPERIMENTAL + help + If you say yes to this option, support will be included for the + OpenCores I2C controller. For details see + http://www.opencores.org/projects.cgi/web/i2c/overview + + This driver can also be built as a module. If so, the module + will be called i2c-ocores. + +config I2C_OMAP + tristate "OMAP I2C adapter" + depends on ARCH_OMAP + default y if MACH_OMAP_H3 || MACH_OMAP_OSK + help + If you say yes to this option, support will be included for the + I2C interface on the Texas Instruments OMAP1/2 family of processors. + Like OMAP1510/1610/1710/5912 and OMAP242x. + For details see http://www.ti.com/omap. + +config I2C_PASEMI + tristate "PA Semi SMBus interface" + depends on PPC_PASEMI && PCI + help + Supports the PA Semi PWRficient on-chip SMBus interfaces. + +config I2C_PCA_PLATFORM + tristate "PCA9564/PCA9665 as platform device" + select I2C_ALGOPCA + default n + help + This driver supports a memory mapped Philips PCA9564/PCA9665 + parallel bus to I2C bus controller. + + This driver can also be built as a module. If so, the module + will be called i2c-pca-platform. + +config I2C_PMCMSP + tristate "PMC MSP I2C TWI Controller" + depends on PMC_MSP + help + This driver supports the PMC TWI controller on MSP devices. + + This driver can also be built as module. If so, the module + will be called i2c-pmcmsp. + +config I2C_PNX + tristate "I2C bus support for Philips PNX and NXP LPC targets" + depends on ARCH_PNX4008 || ARCH_LPC32XX + help + This driver supports the Philips IP3204 I2C IP block master and/or + slave controller + + This driver can also be built as a module. If so, the module + will be called i2c-pnx. + +config I2C_PUV3 + tristate "PKUnity v3 I2C bus support" + depends on UNICORE32 && ARCH_PUV3 + select I2C_ALGOBIT + help + This driver supports the I2C IP inside the PKUnity-v3 SoC. + This I2C bus controller is under AMBA/AXI bus. + + This driver can also be built as a module. If so, the module + will be called i2c-puv3. + +config I2C_PXA + tristate "Intel PXA2XX I2C adapter" + depends on ARCH_PXA || ARCH_MMP || (X86_32 && PCI && OF) + help + If you have devices in the PXA I2C bus, say yes to this option. + This driver can also be built as a module. If so, the module + will be called i2c-pxa. + +config I2C_PXA_PCI + def_bool I2C_PXA && X86_32 && PCI && OF + +config I2C_PXA_SLAVE + bool "Intel PXA2XX I2C Slave comms support" + depends on I2C_PXA && !X86_32 + help + Support I2C slave mode communications on the PXA I2C bus. This + is necessary for systems where the PXA may be a target on the + I2C bus. + +config HAVE_S3C2410_I2C + bool + help + This will include I2C support for Samsung SoCs. If you want to + include I2C support for any machine, kindly select this in the + respective Kconfig file. + +config I2C_S3C2410 + tristate "S3C2410 I2C Driver" + depends on HAVE_S3C2410_I2C + help + Say Y here to include support for I2C controller in the + Samsung SoCs. + +config I2C_S6000 + tristate "S6000 I2C support" + depends on XTENSA_VARIANT_S6000 + help + This driver supports the on chip I2C device on the + S6000 xtensa processor family. + + To compile this driver as a module, choose M here. The module + will be called i2c-s6000. + +config I2C_SH7760 + tristate "Renesas SH7760 I2C Controller" + depends on CPU_SUBTYPE_SH7760 + help + This driver supports the 2 I2C interfaces on the Renesas SH7760. + + This driver can also be built as a module. If so, the module + will be called i2c-sh7760. + +config I2C_SH_MOBILE + tristate "SuperH Mobile I2C Controller" + depends on SUPERH || ARCH_SHMOBILE + help + If you say yes to this option, support will be included for the + built-in I2C interface on the Renesas SH-Mobile processor. + + This driver can also be built as a module. If so, the module + will be called i2c-sh_mobile. + +config I2C_SIMTEC + tristate "Simtec Generic I2C interface" + select I2C_ALGOBIT + help + If you say yes to this option, support will be included for + the Simtec Generic I2C interface. This driver is for the + simple I2C bus used on newer Simtec products for general + I2C, such as DDC on the Simtec BBD2016A. + + This driver can also be built as a module. If so, the module + will be called i2c-simtec. + +config I2C_STU300 + tristate "ST Microelectronics DDC I2C interface" + depends on MACH_U300 + default y if MACH_U300 + help + If you say yes to this option, support will be included for the + I2C interface from ST Microelectronics simply called "DDC I2C" + supporting both I2C and DDC, used in e.g. the U300 series + mobile platforms. + + This driver can also be built as a module. If so, the module + will be called i2c-stu300. + +config I2C_TEGRA + tristate "NVIDIA Tegra internal I2C controller" + depends on ARCH_TEGRA + help + If you say yes to this option, support will be included for the + I2C controller embedded in NVIDIA Tegra SOCs + +config I2C_VERSATILE + tristate "ARM Versatile/Realview I2C bus support" + depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS + select I2C_ALGOBIT + help + Say yes if you want to support the I2C serial bus on ARMs Versatile + range of platforms. + + This driver can also be built as a module. If so, the module + will be called i2c-versatile. + +config I2C_OCTEON + tristate "Cavium OCTEON I2C bus support" + depends on CPU_CAVIUM_OCTEON + help + Say yes if you want to support the I2C serial bus on Cavium + OCTEON SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-octeon. + +config I2C_XILINX + tristate "Xilinx I2C Controller" + depends on EXPERIMENTAL && HAS_IOMEM + help + If you say yes to this option, support will be included for the + Xilinx I2C controller. + + This driver can also be built as a module. If so, the module + will be called xilinx_i2c. + +config I2C_EG20T + tristate "Intel EG20T PCH / OKI SEMICONDUCTOR IOH(ML7213/ML7223)" + depends on PCI + help + This driver is for PCH(Platform controller Hub) I2C of EG20T which + is an IOH(Input/Output Hub) for x86 embedded processor. + This driver can access PCH I2C bus device. + + This driver also can be used for OKI SEMICONDUCTOR IOH(Input/ + Output Hub), ML7213 and ML7223. + ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is + for MP(Media Phone) use. + ML7213/ML7223 is companion chip for Intel Atom E6xx series. + ML7213/ML7223 is completely compatible for Intel EG20T PCH. + +comment "External I2C/SMBus adapter drivers" + +config I2C_DIOLAN_U2C + tristate "Diolan U2C-12 USB adapter" + depends on USB + help + If you say yes to this option, support will be included for Diolan + U2C-12, a USB to I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-diolan-u2c. + +config I2C_PARPORT + tristate "Parallel port adapter" + depends on PARPORT + select I2C_ALGOBIT + select I2C_SMBUS + help + This supports parallel port I2C adapters such as the ones made by + Philips or Velleman, Analog Devices evaluation boards, and more. + Basically any adapter using the parallel port as an I2C bus with + no extra chipset is supported by this driver, or could be. + + This driver is a replacement for (and was inspired by) an older + driver named i2c-philips-par. The new driver supports more devices, + and makes it easier to add support for new devices. + + An adapter type parameter is now mandatory. Please read the file + Documentation/i2c/busses/i2c-parport for details. + + Another driver exists, named i2c-parport-light, which doesn't depend + on the parport driver. This is meant for embedded systems. Don't say + Y here if you intend to say Y or M there. + + This support is also available as a module. If so, the module + will be called i2c-parport. + +config I2C_PARPORT_LIGHT + tristate "Parallel port adapter (light)" + select I2C_ALGOBIT + select I2C_SMBUS + help + This supports parallel port I2C adapters such as the ones made by + Philips or Velleman, Analog Devices evaluation boards, and more. + Basically any adapter using the parallel port as an I2C bus with + no extra chipset is supported by this driver, or could be. + + This driver is a light version of i2c-parport. It doesn't depend + on the parport driver, and uses direct I/O access instead. This + might be preferred on embedded systems where wasting memory for + the clean but heavy parport handling is not an option. The + drawback is a reduced portability and the impossibility to + daisy-chain other parallel port devices. + + Don't say Y here if you said Y or M to i2c-parport. Saying M to + both is possible but both modules should not be loaded at the same + time. + + This support is also available as a module. If so, the module + will be called i2c-parport-light. + +config I2C_TAOS_EVM + tristate "TAOS evaluation module" + depends on EXPERIMENTAL + select SERIO + select SERIO_SERPORT + default n + help + This supports TAOS evaluation modules on serial port. In order to + use this driver, you will need the inputattach tool, which is part + of the input-utils package. + + If unsure, say N. + + This support is also available as a module. If so, the module + will be called i2c-taos-evm. + +config I2C_TINY_USB + tristate "Tiny-USB adapter" + depends on USB + help + If you say yes to this option, support will be included for the + i2c-tiny-usb, a simple do-it-yourself USB to I2C interface. See + http://www.harbaum.org/till/i2c_tiny_usb for hardware details. + + This driver can also be built as a module. If so, the module + will be called i2c-tiny-usb. + +comment "Other I2C/SMBus bus drivers" + +config I2C_ACORN + tristate "Acorn IOC/IOMD I2C bus support" + depends on ARCH_ACORN + default y + select I2C_ALGOBIT + help + Say yes if you want to support the I2C bus on Acorn platforms. + + If you don't know, say Y. + +config I2C_ELEKTOR + tristate "Elektor ISA card" + depends on ISA && BROKEN_ON_SMP + select I2C_ALGOPCF + help + This supports the PCF8584 ISA bus I2C adapter. Say Y if you own + such an adapter. + + This support is also available as a module. If so, the module + will be called i2c-elektor. + +config I2C_PCA_ISA + tristate "PCA9564/PCA9665 on an ISA bus" + depends on ISA + select I2C_ALGOPCA + default n + help + This driver supports ISA boards using the Philips PCA9564/PCA9665 + parallel bus to I2C bus controller. + + This driver can also be built as a module. If so, the module + will be called i2c-pca-isa. + + This device is almost undetectable and using this driver on a + system which doesn't have this device will result in long + delays when I2C/SMBus chip drivers are loaded (e.g. at boot + time). If unsure, say N. + +config I2C_SIBYTE + tristate "SiByte SMBus interface" + depends on SIBYTE_SB1xxx_SOC + help + Supports the SiByte SOC on-chip I2C interfaces (2 channels). + +config I2C_STUB + tristate "I2C/SMBus Test Stub" + depends on EXPERIMENTAL && m + default 'n' + help + This module may be useful to developers of SMBus client drivers, + especially for certain kinds of sensor chips. + + If you do build this module, be sure to read the notes and warnings + in <file:Documentation/i2c/i2c-stub>. + + If you don't know what to do here, definitely say N. + +config SCx200_I2C + tristate "NatSemi SCx200 I2C using GPIO pins (DEPRECATED)" + depends on SCx200_GPIO + select I2C_ALGOBIT + help + Enable the use of two GPIO pins of a SCx200 processor as an I2C bus. + + If you don't know what to do here, say N. + + This support is also available as a module. If so, the module + will be called scx200_i2c. + + This driver is deprecated and will be dropped soon. Use i2c-gpio + (or scx200_acb) instead. + +config SCx200_I2C_SCL + int "GPIO pin used for SCL" + depends on SCx200_I2C + default "12" + help + Enter the GPIO pin number used for the SCL signal. This value can + also be specified with a module parameter. + +config SCx200_I2C_SDA + int "GPIO pin used for SDA" + depends on SCx200_I2C + default "13" + help + Enter the GPIO pin number used for the SSA signal. This value can + also be specified with a module parameter. + +config SCx200_ACB + tristate "Geode ACCESS.bus support" + depends on X86_32 && PCI + help + Enable the use of the ACCESS.bus controllers on the Geode SCx200 and + SC1100 processors and the CS5535 and CS5536 Geode companion devices. + + If you don't know what to do here, say N. + + This support is also available as a module. If so, the module + will be called scx200_acb. + +endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile new file mode 100644 index 00000000..e6cf294d --- /dev/null +++ b/drivers/i2c/busses/Makefile @@ -0,0 +1,86 @@ +# +# Makefile for the i2c bus drivers. +# + +# ACPI drivers +obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o + +# PC SMBus host controller drivers +obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o +obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o +obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o +obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o +obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o +obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o +obj-$(CONFIG_I2C_I801) += i2c-i801.o +obj-$(CONFIG_I2C_ISCH) += i2c-isch.o +obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o +obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o +obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o +obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o +obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o +obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o +obj-$(CONFIG_I2C_VIA) += i2c-via.o +obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o + +# Mac SMBus host controller drivers +obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o +obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o + +# Embedded system I2C/SMBus host controller drivers +obj-$(CONFIG_I2C_AT91) += i2c-at91.o +obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o +obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o +obj-$(CONFIG_I2C_CPM) += i2c-cpm.o +obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o +obj-$(CONFIG_I2C_DESIGNWARE) += i2c-designware.o +obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o +obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o +obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o +obj-$(CONFIG_I2C_IMX) += i2c-imx.o +obj-$(CONFIG_I2C_INTEL_MID) += i2c-intel-mid.o +obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o +obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o +obj-$(CONFIG_I2C_MPC) += i2c-mpc.o +obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o +obj-$(CONFIG_I2C_MXS) += i2c-mxs.o +obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o +obj-$(CONFIG_I2C_NUC900) += i2c-nuc900.o +obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o +obj-$(CONFIG_I2C_OMAP) += i2c-omap.o +obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o +obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o +obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o +obj-$(CONFIG_I2C_PNX) += i2c-pnx.o +obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o +obj-$(CONFIG_I2C_PXA) += i2c-pxa.o +obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o +obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o +obj-$(CONFIG_I2C_S6000) += i2c-s6000.o +obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o +obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o +obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o +obj-$(CONFIG_I2C_STU300) += i2c-stu300.o +obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o +obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o +obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o + +# External I2C/SMBus adapter drivers +obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o +obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o +obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o +obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o +obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o + +# Other I2C/SMBus bus drivers +obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o +obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o +obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o +obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o +obj-$(CONFIG_I2C_STUB) += i2c-stub.o +obj-$(CONFIG_SCx200_ACB) += scx200_acb.o +obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o + +ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c new file mode 100644 index 00000000..86796488 --- /dev/null +++ b/drivers/i2c/busses/i2c-acorn.c @@ -0,0 +1,97 @@ +/* + * linux/drivers/acorn/char/i2c.c + * + * Copyright (C) 2000 Russell King + * + * 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. + * + * ARM IOC/IOMD i2c driver. + * + * On Acorn machines, the following i2c devices are on the bus: + * - PCF8583 real time clock & static RAM + */ +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/io.h> + +#include <mach/hardware.h> +#include <asm/hardware/ioc.h> +#include <asm/system.h> + +#define FORCE_ONES 0xdc +#define SCL 0x02 +#define SDA 0x01 + +/* + * We must preserve all non-i2c output bits in IOC_CONTROL. + * Note also that we need to preserve the value of SCL and + * SDA outputs as well (which may be different from the + * values read back from IOC_CONTROL). + */ +static u_int force_ones; + +static void ioc_setscl(void *data, int state) +{ + u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); + u_int ones = force_ones; + + if (state) + ones |= SCL; + else + ones &= ~SCL; + + force_ones = ones; + + ioc_writeb(ioc_control | ones, IOC_CONTROL); +} + +static void ioc_setsda(void *data, int state) +{ + u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); + u_int ones = force_ones; + + if (state) + ones |= SDA; + else + ones &= ~SDA; + + force_ones = ones; + + ioc_writeb(ioc_control | ones, IOC_CONTROL); +} + +static int ioc_getscl(void *data) +{ + return (ioc_readb(IOC_CONTROL) & SCL) != 0; +} + +static int ioc_getsda(void *data) +{ + return (ioc_readb(IOC_CONTROL) & SDA) != 0; +} + +static struct i2c_algo_bit_data ioc_data = { + .setsda = ioc_setsda, + .setscl = ioc_setscl, + .getsda = ioc_getsda, + .getscl = ioc_getscl, + .udelay = 80, + .timeout = HZ, +}; + +static struct i2c_adapter ioc_ops = { + .nr = 0, + .algo_data = &ioc_data, +}; + +static int __init i2c_ioc_init(void) +{ + force_ones = FORCE_ONES | SCL | SDA; + + return i2c_bit_add_numbered_bus(&ioc_ops); +} + +module_init(i2c_ioc_init); diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c new file mode 100644 index 00000000..cd7ac5c6 --- /dev/null +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -0,0 +1,540 @@ +/* + Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>, + Philip Edelbrock <phil@netroedge.com>, + Mark D. Studebaker <mdsxyz123@yahoo.com>, + Dan Eaton <dan.eaton@rocketlogix.com> and + Stephen Rousset<stephen.rousset@rocketlogix.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. + + 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. +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1535 South Bridge. + + The M1535 is a South bridge for portable systems. + It is very similar to the M15x3 South bridges also produced + by Acer Labs Inc. Some of the registers within the part + have moved and some have been redefined slightly. Additionally, + the sequencing of the SMBus transactions has been modified + to be more consistent with the sequencing recommended by + the manufacturer and observed through testing. These + changes are reflected in this driver and can be identified + by comparing this driver to the i2c-ali15x3 driver. + For an overview of these chips see http://www.acerlabs.com + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + + This driver does not use interrupts. +*/ + + +/* Note: we assume there can only be one ALI1535, with one SMBus interface */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/io.h> + + +/* ALI1535 SMBus address offsets */ +#define SMBHSTSTS (0 + ali1535_smba) +#define SMBHSTTYP (1 + ali1535_smba) +#define SMBHSTPORT (2 + ali1535_smba) +#define SMBHSTCMD (7 + ali1535_smba) +#define SMBHSTADD (3 + ali1535_smba) +#define SMBHSTDAT0 (4 + ali1535_smba) +#define SMBHSTDAT1 (5 + ali1535_smba) +#define SMBBLKDAT (6 + ali1535_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBREV 0x008 +#define SMBCFG 0x0D1 +#define SMBBA 0x0E2 +#define SMBHSTCFG 0x0F0 +#define SMBCLK 0x0F2 + +/* Other settings */ +#define MAX_TIMEOUT 500 /* times 1/100 sec */ +#define ALI1535_SMB_IOSIZE 32 + +#define ALI1535_SMB_DEFAULTBASE 0x8040 + +/* ALI1535 address lock bits */ +#define ALI1535_LOCK 0x06 /* dwe */ + +/* ALI1535 command constants */ +#define ALI1535_QUICK 0x00 +#define ALI1535_BYTE 0x10 +#define ALI1535_BYTE_DATA 0x20 +#define ALI1535_WORD_DATA 0x30 +#define ALI1535_BLOCK_DATA 0x40 +#define ALI1535_I2C_READ 0x60 + +#define ALI1535_DEV10B_EN 0x80 /* Enable 10-bit addressing in */ + /* I2C read */ +#define ALI1535_T_OUT 0x08 /* Time-out Command (write) */ +#define ALI1535_A_HIGH_BIT9 0x08 /* Bit 9 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ +#define ALI1535_KILL 0x04 /* Kill Command (write) */ +#define ALI1535_A_HIGH_BIT8 0x04 /* Bit 8 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ + +#define ALI1535_D_HI_MASK 0x03 /* Mask for isolating bits 9-8 */ + /* of 10-bit address in I2C */ + /* Read Command */ + +/* ALI1535 status register bits */ +#define ALI1535_STS_IDLE 0x04 +#define ALI1535_STS_BUSY 0x08 /* host busy */ +#define ALI1535_STS_DONE 0x10 /* transaction complete */ +#define ALI1535_STS_DEV 0x20 /* device error */ +#define ALI1535_STS_BUSERR 0x40 /* bus error */ +#define ALI1535_STS_FAIL 0x80 /* failed bus transaction */ +#define ALI1535_STS_ERR 0xE0 /* all the bad error bits */ + +#define ALI1535_BLOCK_CLR 0x04 /* reset block data index */ + +/* ALI1535 device address register bits */ +#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */ + /* Address field */ + /* -> Write = 0 */ + /* -> Read = 1 */ +#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */ + +static struct pci_driver ali1535_driver; +static unsigned short ali1535_smba; + +/* Detect whether a ALI1535 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +static int __devinit ali1535_setup(struct pci_dev *dev) +{ + int retval; + unsigned char temp; + + /* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses + */ + + /* Determine the address of the SMBus area */ + pci_read_config_word(dev, SMBBA, &ali1535_smba); + ali1535_smba &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1)); + if (ali1535_smba == 0) { + dev_warn(&dev->dev, + "ALI1535_smb region uninitialized - upgrade BIOS?\n"); + retval = -ENODEV; + goto exit; + } + + retval = acpi_check_region(ali1535_smba, ALI1535_SMB_IOSIZE, + ali1535_driver.name); + if (retval) + goto exit; + + if (!request_region(ali1535_smba, ALI1535_SMB_IOSIZE, + ali1535_driver.name)) { + dev_err(&dev->dev, "ALI1535_smb region 0x%x already in use!\n", + ali1535_smba); + retval = -EBUSY; + goto exit; + } + + /* check if whole device is enabled */ + pci_read_config_byte(dev, SMBCFG, &temp); + if ((temp & ALI1535_SMBIO_EN) == 0) { + dev_err(&dev->dev, "SMB device not enabled - upgrade BIOS?\n"); + retval = -ENODEV; + goto exit_free; + } + + /* Is SMB Host controller enabled? */ + pci_read_config_byte(dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + dev_err(&dev->dev, "SMBus controller not enabled - upgrade BIOS?\n"); + retval = -ENODEV; + goto exit_free; + } + + /* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(dev, SMBCLK, 0x20); + + /* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + dev_dbg(&dev->dev, "ALI1535 using Interrupt 9 for SMBus.\n"); + */ + pci_read_config_byte(dev, SMBREV, &temp); + dev_dbg(&dev->dev, "SMBREV = 0x%X\n", temp); + dev_dbg(&dev->dev, "ALI1535_smba = 0x%X\n", ali1535_smba); + + return 0; + +exit_free: + release_region(ali1535_smba, ALI1535_SMB_IOSIZE); +exit: + return retval; +} + +static int ali1535_transaction(struct i2c_adapter *adap) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&adap->dev, "Transaction (pre): STS=%02x, TYP=%02x, " + "CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI1535_STS_BUSY) { + /* If the host controller is still busy, it may have timed out + * in the previous transaction, resulting in a "SMBus Timeout" + * printk. I've tried the following to reset a stuck busy bit. + * 1. Reset the controller with an KILL command. (this + * doesn't seem to clear the controller if an external + * device is hung) + * 2. Reset the controller and the other SMBus devices with a + * T_OUT command. (this clears the host busy bit if an + * external device is hung, but it comes back upon a new + * access to a device) + * 3. Disable and reenable the controller in SMBHSTCFG. Worst + * case, nothing seems to work except power reset. + */ + + /* Try resetting entire SMB bus, including other devices - This + * may not work either - it clears the BUSY bit but then the + * BUSY bit may come back on when you try and use the chip + * again. If that's the case you are stuck. + */ + dev_info(&adap->dev, + "Resetting entire SMB Bus to clear busy condition (%02x)\n", + temp); + outb_p(ALI1535_T_OUT, SMBHSTTYP); + temp = inb_p(SMBHSTSTS); + } + + /* now check the error bits and the busy bit */ + if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { + /* This is probably going to be correctable only by a + * power reset as one of the bits now appears to be + * stuck */ + /* This may be a bus or device with electrical problems. */ + dev_err(&adap->dev, + "SMBus reset failed! (0x%02x) - controller or " + "device on bus is probably hung\n", temp); + return -EBUSY; + } + } else { + /* check and clear done bit */ + if (temp & ALI1535_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTPORT); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + msleep(1); + temp = inb_p(SMBHSTSTS); + } while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE)) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + result = -ETIMEDOUT; + dev_err(&adap->dev, "SMBus Timeout!\n"); + } + + if (temp & ALI1535_STS_FAIL) { + result = -EIO; + dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); + } + + /* Unfortunately the ALI SMB controller maps "no response" and "bus + * collision" into a single bit. No response is the usual case so don't + * do a printk. This means that bus collisions go unreported. + */ + if (temp & ALI1535_STS_BUSERR) { + result = -ENXIO; + dev_dbg(&adap->dev, + "Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); + } + + /* haven't ever seen this */ + if (temp & ALI1535_STS_DEV) { + result = -EIO; + dev_err(&adap->dev, "Error: device error\n"); + } + + /* check to see if the "command complete" indication is set */ + if (!(temp & ALI1535_STS_DONE)) { + result = -ETIMEDOUT; + dev_err(&adap->dev, "Error: command never completed\n"); + } + + dev_dbg(&adap->dev, "Transaction (post): STS=%02x, TYP=%02x, " + "CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); + + /* take consequent actions for error conditions */ + if (!(temp & ALI1535_STS_DONE)) { + /* issue "kill" to reset host controller */ + outb_p(ALI1535_KILL,SMBHSTTYP); + outb_p(0xFF,SMBHSTSTS); + } else if (temp & ALI1535_STS_ERR) { + /* issue "timeout" to reset all devices on bus */ + outb_p(ALI1535_T_OUT,SMBHSTTYP); + outb_p(0xFF,SMBHSTSTS); + } + + return result; +} + +/* Return negative errno on error. */ +static s32 ali1535_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int i, len; + int temp; + int timeout; + s32 result = 0; + + /* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE); + timeout++) { + msleep(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) + dev_warn(&adap->dev, "Idle wait Timeout! STS=0x%02x\n", temp); + + /* clear status register (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_QUICK; + outb_p(size, SMBHSTTYP); /* output command */ + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BYTE; + outb_p(size, SMBHSTTYP); /* output command */ + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BYTE_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_WORD_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BLOCK_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + /* Reset SMBBLKDAT */ + outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + result = -EOPNOTSUPP; + goto EXIT; + } + + result = ali1535_transaction(adap); + if (result) + goto EXIT; + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) { + result = 0; + goto EXIT; + } + + switch (size) { + case ALI1535_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI1535_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI1535_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI1535_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + /* Reset SMBBLKDAT */ + outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); + dev_dbg(&adap->dev, "Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); + } + break; + } +EXIT: + return result; +} + + +static u32 ali1535_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = ali1535_access, + .functionality = ali1535_func, +}; + +static struct i2c_adapter ali1535_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, +}; + +static const struct pci_device_id ali1535_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, + { }, +}; + +MODULE_DEVICE_TABLE (pci, ali1535_ids); + +static int __devinit ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + if (ali1535_setup(dev)) { + dev_warn(&dev->dev, + "ALI1535 not detected, module not inserted.\n"); + return -ENODEV; + } + + /* set up the sysfs linkage to our parent device */ + ali1535_adapter.dev.parent = &dev->dev; + + snprintf(ali1535_adapter.name, sizeof(ali1535_adapter.name), + "SMBus ALI1535 adapter at %04x", ali1535_smba); + return i2c_add_adapter(&ali1535_adapter); +} + +static void __devexit ali1535_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&ali1535_adapter); + release_region(ali1535_smba, ALI1535_SMB_IOSIZE); +} + +static struct pci_driver ali1535_driver = { + .name = "ali1535_smbus", + .id_table = ali1535_ids, + .probe = ali1535_probe, + .remove = __devexit_p(ali1535_remove), +}; + +static int __init i2c_ali1535_init(void) +{ + return pci_register_driver(&ali1535_driver); +} + +static void __exit i2c_ali1535_exit(void) +{ + pci_unregister_driver(&ali1535_driver); +} + +MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " + "Philip Edelbrock <phil@netroedge.com>, " + "Mark D. Studebaker <mdsxyz123@yahoo.com> " + "and Dan Eaton <dan.eaton@rocketlogix.com>"); +MODULE_DESCRIPTION("ALI1535 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_ali1535_init); +module_exit(i2c_ali1535_exit); diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c new file mode 100644 index 00000000..a409cfcf --- /dev/null +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -0,0 +1,448 @@ +/** + * i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge + * + * Copyright (C) 2004 Patrick Mochel + * 2005 Rudolf Marek <r.marek@assembler.cz> + * + * The 1563 southbridge is deceptively similar to the 1533, with a + * few notable exceptions. One of those happens to be the fact they + * upgraded the i2c core to be 2.0 compliant, and happens to be almost + * identical to the i2c controller found in the Intel 801 south + * bridges. + * + * This driver is based on a mix of the 15x3, 1535, and i801 drivers, + * with a little help from the ALi 1563 spec. + * + * This file is released under the GPLv2 + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/acpi.h> + +#define ALI1563_MAX_TIMEOUT 500 +#define ALI1563_SMBBA 0x80 +#define ALI1563_SMB_IOEN 1 +#define ALI1563_SMB_HOSTEN 2 +#define ALI1563_SMB_IOSIZE 16 + +#define SMB_HST_STS (ali1563_smba + 0) +#define SMB_HST_CNTL1 (ali1563_smba + 1) +#define SMB_HST_CNTL2 (ali1563_smba + 2) +#define SMB_HST_CMD (ali1563_smba + 3) +#define SMB_HST_ADD (ali1563_smba + 4) +#define SMB_HST_DAT0 (ali1563_smba + 5) +#define SMB_HST_DAT1 (ali1563_smba + 6) +#define SMB_BLK_DAT (ali1563_smba + 7) + +#define HST_STS_BUSY 0x01 +#define HST_STS_INTR 0x02 +#define HST_STS_DEVERR 0x04 +#define HST_STS_BUSERR 0x08 +#define HST_STS_FAIL 0x10 +#define HST_STS_DONE 0x80 +#define HST_STS_BAD 0x1c + + +#define HST_CNTL1_TIMEOUT 0x80 +#define HST_CNTL1_LAST 0x40 + +#define HST_CNTL2_KILL 0x04 +#define HST_CNTL2_START 0x40 +#define HST_CNTL2_QUICK 0x00 +#define HST_CNTL2_BYTE 0x01 +#define HST_CNTL2_BYTE_DATA 0x02 +#define HST_CNTL2_WORD_DATA 0x03 +#define HST_CNTL2_BLOCK 0x05 + + +#define HST_CNTL2_SIZEMASK 0x38 + +static struct pci_driver ali1563_pci_driver; +static unsigned short ali1563_smba; + +static int ali1563_transaction(struct i2c_adapter * a, int size) +{ + u32 data; + int timeout; + int status = -EIO; + + dev_dbg(&a->dev, "Transaction (pre): STS=%02x, CNTL1=%02x, " + "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2), + inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0), + inb_p(SMB_HST_DAT1)); + + data = inb_p(SMB_HST_STS); + if (data & HST_STS_BAD) { + dev_err(&a->dev, "ali1563: Trying to reset busy device\n"); + outb_p(data | HST_STS_BAD,SMB_HST_STS); + data = inb_p(SMB_HST_STS); + if (data & HST_STS_BAD) + return -EBUSY; + } + outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2); + + timeout = ALI1563_MAX_TIMEOUT; + do { + msleep(1); + } while (((data = inb_p(SMB_HST_STS)) & HST_STS_BUSY) && --timeout); + + dev_dbg(&a->dev, "Transaction (post): STS=%02x, CNTL1=%02x, " + "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2), + inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0), + inb_p(SMB_HST_DAT1)); + + if (timeout && !(data & HST_STS_BAD)) + return 0; + + if (!timeout) { + dev_err(&a->dev, "Timeout - Trying to KILL transaction!\n"); + /* Issue 'kill' to host controller */ + outb_p(HST_CNTL2_KILL,SMB_HST_CNTL2); + data = inb_p(SMB_HST_STS); + status = -ETIMEDOUT; + } + + /* device error - no response, ignore the autodetection case */ + if (data & HST_STS_DEVERR) { + if (size != HST_CNTL2_QUICK) + dev_err(&a->dev, "Device error!\n"); + status = -ENXIO; + } + /* bus collision */ + if (data & HST_STS_BUSERR) { + dev_err(&a->dev, "Bus collision!\n"); + /* Issue timeout, hoping it helps */ + outb_p(HST_CNTL1_TIMEOUT,SMB_HST_CNTL1); + } + + if (data & HST_STS_FAIL) { + dev_err(&a->dev, "Cleaning fail after KILL!\n"); + outb_p(0x0,SMB_HST_CNTL2); + } + + return status; +} + +static int ali1563_block_start(struct i2c_adapter * a) +{ + u32 data; + int timeout; + int status = -EIO; + + dev_dbg(&a->dev, "Block (pre): STS=%02x, CNTL1=%02x, " + "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2), + inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0), + inb_p(SMB_HST_DAT1)); + + data = inb_p(SMB_HST_STS); + if (data & HST_STS_BAD) { + dev_warn(&a->dev,"ali1563: Trying to reset busy device\n"); + outb_p(data | HST_STS_BAD,SMB_HST_STS); + data = inb_p(SMB_HST_STS); + if (data & HST_STS_BAD) + return -EBUSY; + } + + /* Clear byte-ready bit */ + outb_p(data | HST_STS_DONE, SMB_HST_STS); + + /* Start transaction and wait for byte-ready bit to be set */ + outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2); + + timeout = ALI1563_MAX_TIMEOUT; + do { + msleep(1); + } while (!((data = inb_p(SMB_HST_STS)) & HST_STS_DONE) && --timeout); + + dev_dbg(&a->dev, "Block (post): STS=%02x, CNTL1=%02x, " + "CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", + inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2), + inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0), + inb_p(SMB_HST_DAT1)); + + if (timeout && !(data & HST_STS_BAD)) + return 0; + + if (timeout == 0) + status = -ETIMEDOUT; + + if (data & HST_STS_DEVERR) + status = -ENXIO; + + dev_err(&a->dev, "SMBus Error: %s%s%s%s%s\n", + timeout ? "" : "Timeout ", + data & HST_STS_FAIL ? "Transaction Failed " : "", + data & HST_STS_BUSERR ? "No response or Bus Collision " : "", + data & HST_STS_DEVERR ? "Device Error " : "", + !(data & HST_STS_DONE) ? "Transaction Never Finished " : ""); + return status; +} + +static int ali1563_block(struct i2c_adapter * a, union i2c_smbus_data * data, u8 rw) +{ + int i, len; + int error = 0; + + /* Do we need this? */ + outb_p(HST_CNTL1_LAST,SMB_HST_CNTL1); + + if (rw == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 1) + len = 1; + else if (len > 32) + len = 32; + outb_p(len,SMB_HST_DAT0); + outb_p(data->block[1],SMB_BLK_DAT); + } else + len = 32; + + outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_BLOCK, SMB_HST_CNTL2); + + for (i = 0; i < len; i++) { + if (rw == I2C_SMBUS_WRITE) { + outb_p(data->block[i + 1], SMB_BLK_DAT); + if ((error = ali1563_block_start(a))) + break; + } else { + if ((error = ali1563_block_start(a))) + break; + if (i == 0) { + len = inb_p(SMB_HST_DAT0); + if (len < 1) + len = 1; + else if (len > 32) + len = 32; + } + data->block[i+1] = inb_p(SMB_BLK_DAT); + } + } + /* Do we need this? */ + outb_p(HST_CNTL1_LAST,SMB_HST_CNTL1); + return error; +} + +static s32 ali1563_access(struct i2c_adapter * a, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data * data) +{ + int error = 0; + int timeout; + u32 reg; + + for (timeout = ALI1563_MAX_TIMEOUT; timeout; timeout--) { + if (!(reg = inb_p(SMB_HST_STS) & HST_STS_BUSY)) + break; + } + if (!timeout) + dev_warn(&a->dev,"SMBus not idle. HST_STS = %02x\n",reg); + outb_p(0xff,SMB_HST_STS); + + /* Map the size to what the chip understands */ + switch (size) { + case I2C_SMBUS_QUICK: + size = HST_CNTL2_QUICK; + break; + case I2C_SMBUS_BYTE: + size = HST_CNTL2_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + size = HST_CNTL2_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + size = HST_CNTL2_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + size = HST_CNTL2_BLOCK; + break; + default: + dev_warn(&a->dev, "Unsupported transaction %d\n", size); + error = -EOPNOTSUPP; + goto Done; + } + + outb_p(((addr & 0x7f) << 1) | (rw & 0x01), SMB_HST_ADD); + outb_p((inb_p(SMB_HST_CNTL2) & ~HST_CNTL2_SIZEMASK) | (size << 3), SMB_HST_CNTL2); + + /* Write the command register */ + + switch(size) { + case HST_CNTL2_BYTE: + if (rw== I2C_SMBUS_WRITE) + /* Beware it uses DAT0 register and not CMD! */ + outb_p(cmd, SMB_HST_DAT0); + break; + case HST_CNTL2_BYTE_DATA: + outb_p(cmd, SMB_HST_CMD); + if (rw == I2C_SMBUS_WRITE) + outb_p(data->byte, SMB_HST_DAT0); + break; + case HST_CNTL2_WORD_DATA: + outb_p(cmd, SMB_HST_CMD); + if (rw == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMB_HST_DAT0); + outb_p((data->word & 0xff00) >> 8, SMB_HST_DAT1); + } + break; + case HST_CNTL2_BLOCK: + outb_p(cmd, SMB_HST_CMD); + error = ali1563_block(a,data,rw); + goto Done; + } + + if ((error = ali1563_transaction(a, size))) + goto Done; + + if ((rw == I2C_SMBUS_WRITE) || (size == HST_CNTL2_QUICK)) + goto Done; + + switch (size) { + case HST_CNTL2_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMB_HST_DAT0); + break; + case HST_CNTL2_BYTE_DATA: + data->byte = inb_p(SMB_HST_DAT0); + break; + case HST_CNTL2_WORD_DATA: + data->word = inb_p(SMB_HST_DAT0) + (inb_p(SMB_HST_DAT1) << 8); + break; + } +Done: + return error; +} + +static u32 ali1563_func(struct i2c_adapter * a) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + + +static int __devinit ali1563_setup(struct pci_dev * dev) +{ + u16 ctrl; + + pci_read_config_word(dev,ALI1563_SMBBA,&ctrl); + + /* SMB I/O Base in high 12 bits and must be aligned with the + * size of the I/O space. */ + ali1563_smba = ctrl & ~(ALI1563_SMB_IOSIZE - 1); + if (!ali1563_smba) { + dev_warn(&dev->dev,"ali1563_smba Uninitialized\n"); + goto Err; + } + + /* Check if device is enabled */ + if (!(ctrl & ALI1563_SMB_HOSTEN)) { + dev_warn(&dev->dev, "Host Controller not enabled\n"); + goto Err; + } + if (!(ctrl & ALI1563_SMB_IOEN)) { + dev_warn(&dev->dev, "I/O space not enabled, trying manually\n"); + pci_write_config_word(dev, ALI1563_SMBBA, + ctrl | ALI1563_SMB_IOEN); + pci_read_config_word(dev, ALI1563_SMBBA, &ctrl); + if (!(ctrl & ALI1563_SMB_IOEN)) { + dev_err(&dev->dev, "I/O space still not enabled, " + "giving up\n"); + goto Err; + } + } + + if (acpi_check_region(ali1563_smba, ALI1563_SMB_IOSIZE, + ali1563_pci_driver.name)) + goto Err; + + if (!request_region(ali1563_smba, ALI1563_SMB_IOSIZE, + ali1563_pci_driver.name)) { + dev_err(&dev->dev, "Could not allocate I/O space at 0x%04x\n", + ali1563_smba); + goto Err; + } + dev_info(&dev->dev, "Found ALi1563 SMBus at 0x%04x\n", ali1563_smba); + + return 0; +Err: + return -ENODEV; +} + +static void ali1563_shutdown(struct pci_dev *dev) +{ + release_region(ali1563_smba,ALI1563_SMB_IOSIZE); +} + +static const struct i2c_algorithm ali1563_algorithm = { + .smbus_xfer = ali1563_access, + .functionality = ali1563_func, +}; + +static struct i2c_adapter ali1563_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &ali1563_algorithm, +}; + +static int __devinit ali1563_probe(struct pci_dev * dev, + const struct pci_device_id * id_table) +{ + int error; + + if ((error = ali1563_setup(dev))) + goto exit; + ali1563_adapter.dev.parent = &dev->dev; + snprintf(ali1563_adapter.name, sizeof(ali1563_adapter.name), + "SMBus ALi 1563 Adapter @ %04x", ali1563_smba); + if ((error = i2c_add_adapter(&ali1563_adapter))) + goto exit_shutdown; + return 0; + +exit_shutdown: + ali1563_shutdown(dev); +exit: + dev_warn(&dev->dev, "ALi1563 SMBus probe failed (%d)\n", error); + return error; +} + +static void __devexit ali1563_remove(struct pci_dev * dev) +{ + i2c_del_adapter(&ali1563_adapter); + ali1563_shutdown(dev); +} + +static const struct pci_device_id ali1563_id_table[] __devinitconst = { + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1563) }, + {}, +}; + +MODULE_DEVICE_TABLE (pci, ali1563_id_table); + +static struct pci_driver ali1563_pci_driver = { + .name = "ali1563_smbus", + .id_table = ali1563_id_table, + .probe = ali1563_probe, + .remove = __devexit_p(ali1563_remove), +}; + +static int __init ali1563_init(void) +{ + return pci_register_driver(&ali1563_pci_driver); +} + +module_init(ali1563_init); + +static void __exit ali1563_exit(void) +{ + pci_unregister_driver(&ali1563_pci_driver); +} + +module_exit(ali1563_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c new file mode 100644 index 00000000..83e8a60c --- /dev/null +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -0,0 +1,533 @@ +/* + Copyright (c) 1999 Frodo Looijaard <frodol@dds.nl> and + Philip Edelbrock <phil@netroedge.com> and + Mark D. Studebaker <mdsxyz123@yahoo.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. + + 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. +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1541 and M1543C South Bridges. + + The M1543C is a South bridge for desktop systems. + The M1533 is a South bridge for portable systems. + They are part of the following ALI chipsets: + "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin V": Includes the M1541 Socket 7 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin IV": Includes the M1541 Socket 7 North bridge + with host bus up to 83.3 MHz. + For an overview of these chips see http://www.acerlabs.com + + The M1533/M1543C devices appear as FOUR separate devices + on the PCI bus. An output of lspci will show something similar + to the following: + + 00:02.0 USB Controller: Acer Laboratories Inc. M5237 + 00:03.0 Bridge: Acer Laboratories Inc. M7101 + 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 + 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + The SMB Slave controller on the M15X3 is not enabled. + + This driver does not use interrupts. +*/ + +/* Note: we assume there can only be one ALI15X3, with one SMBus interface */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/io.h> + +/* ALI15X3 SMBus address offsets */ +#define SMBHSTSTS (0 + ali15x3_smba) +#define SMBHSTCNT (1 + ali15x3_smba) +#define SMBHSTSTART (2 + ali15x3_smba) +#define SMBHSTCMD (7 + ali15x3_smba) +#define SMBHSTADD (3 + ali15x3_smba) +#define SMBHSTDAT0 (4 + ali15x3_smba) +#define SMBHSTDAT1 (5 + ali15x3_smba) +#define SMBBLKDAT (6 + ali15x3_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBBA 0x014 +#define SMBATPC 0x05B /* used to unlock xxxBA registers */ +#define SMBHSTCFG 0x0E0 +#define SMBSLVC 0x0E1 +#define SMBCLK 0x0E2 +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 200 /* times 1/100 sec */ +#define ALI15X3_SMB_IOSIZE 32 + +/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB. + We don't use these here. If the bases aren't set to some value we + tell user to upgrade BIOS and we fail. +*/ +#define ALI15X3_SMB_DEFAULTBASE 0xE800 + +/* ALI15X3 address lock bits */ +#define ALI15X3_LOCK 0x06 + +/* ALI15X3 command constants */ +#define ALI15X3_ABORT 0x02 +#define ALI15X3_T_OUT 0x04 +#define ALI15X3_QUICK 0x00 +#define ALI15X3_BYTE 0x10 +#define ALI15X3_BYTE_DATA 0x20 +#define ALI15X3_WORD_DATA 0x30 +#define ALI15X3_BLOCK_DATA 0x40 +#define ALI15X3_BLOCK_CLR 0x80 + +/* ALI15X3 status register bits */ +#define ALI15X3_STS_IDLE 0x04 +#define ALI15X3_STS_BUSY 0x08 +#define ALI15X3_STS_DONE 0x10 +#define ALI15X3_STS_DEV 0x20 /* device error */ +#define ALI15X3_STS_COLL 0x40 /* collision or no response */ +#define ALI15X3_STS_TERM 0x80 /* terminated by abort */ +#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */ + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static u16 force_addr; +module_param(force_addr, ushort, 0); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the i2c controller"); + +static struct pci_driver ali15x3_driver; +static unsigned short ali15x3_smba; + +static int __devinit ali15x3_setup(struct pci_dev *ALI15X3_dev) +{ + u16 a; + unsigned char temp; + + /* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses + */ + + /* Unlock the register. + The data sheet says that the address registers are read-only + if the lock bits are 1, but in fact the address registers + are zero unless you clear the lock bits. + */ + pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp); + if (temp & ALI15X3_LOCK) { + temp &= ~ALI15X3_LOCK; + pci_write_config_byte(ALI15X3_dev, SMBATPC, temp); + } + + /* Determine the address of the SMBus area */ + pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba); + ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1)); + if (ali15x3_smba == 0 && force_addr == 0) { + dev_err(&ALI15X3_dev->dev, "ALI15X3_smb region uninitialized " + "- upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if(force_addr) + ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1); + + if (acpi_check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, + ali15x3_driver.name)) + return -EBUSY; + + if (!request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, + ali15x3_driver.name)) { + dev_err(&ALI15X3_dev->dev, + "ALI15X3_smb region 0x%x already in use!\n", + ali15x3_smba); + return -ENODEV; + } + + if(force_addr) { + dev_info(&ALI15X3_dev->dev, "forcing ISA address 0x%04X\n", + ali15x3_smba); + if (PCIBIOS_SUCCESSFUL != pci_write_config_word(ALI15X3_dev, + SMBBA, + ali15x3_smba)) + goto error; + if (PCIBIOS_SUCCESSFUL != pci_read_config_word(ALI15X3_dev, + SMBBA, &a)) + goto error; + if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) { + /* make sure it works */ + dev_err(&ALI15X3_dev->dev, + "force address failed - not supported?\n"); + goto error; + } + } + /* check if whole device is enabled */ + pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp); + if ((temp & 1) == 0) { + dev_info(&ALI15X3_dev->dev, "enabling SMBus device\n"); + pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01); + } + + /* Is SMB Host controller enabled? */ + pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + dev_info(&ALI15X3_dev->dev, "enabling SMBus controller\n"); + pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01); + } + + /* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20); + + /* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + dev_dbg(&ALI15X3_dev->dev, "ALI15X3 using Interrupt 9 for SMBus.\n"); + */ + pci_read_config_byte(ALI15X3_dev, SMBREV, &temp); + dev_dbg(&ALI15X3_dev->dev, "SMBREV = 0x%X\n", temp); + dev_dbg(&ALI15X3_dev->dev, "iALI15X3_smba = 0x%X\n", ali15x3_smba); + + return 0; +error: + release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); + return -ENODEV; +} + +/* Another internally used function */ +static int ali15x3_transaction(struct i2c_adapter *adap) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&adap->dev, "Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), + inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), + inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI15X3_STS_BUSY) { + /* + If the host controller is still busy, it may have timed out in the + previous transaction, resulting in a "SMBus Timeout" Dev. + I've tried the following to reset a stuck busy bit. + 1. Reset the controller with an ABORT command. + (this doesn't seem to clear the controller if an external + device is hung) + 2. Reset the controller and the other SMBus devices with a + T_OUT command. (this clears the host busy bit if an + external device is hung, but it comes back upon a new access + to a device) + 3. Disable and reenable the controller in SMBHSTCFG + Worst case, nothing seems to work except power reset. + */ + /* Abort - reset the host controller */ + /* + Try resetting entire SMB bus, including other devices - + This may not work either - it clears the BUSY bit but + then the BUSY bit may come back on when you try and use the chip again. + If that's the case you are stuck. + */ + dev_info(&adap->dev, "Resetting entire SMB Bus to " + "clear busy condition (%02x)\n", temp); + outb_p(ALI15X3_T_OUT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + } + + /* now check the error bits and the busy bit */ + if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* this is probably going to be correctable only by a power reset + as one of the bits now appears to be stuck */ + /* This may be a bus or device with electrical problems. */ + dev_err(&adap->dev, "SMBus reset failed! (0x%02x) - " + "controller or device on bus is probably hung\n", + temp); + return -EBUSY; + } + } else { + /* check and clear done bit */ + if (temp & ALI15X3_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTSTART); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + msleep(1); + temp = inb_p(SMBHSTSTS); + } while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE))) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + result = -ETIMEDOUT; + dev_err(&adap->dev, "SMBus Timeout!\n"); + } + + if (temp & ALI15X3_STS_TERM) { + result = -EIO; + dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); + } + + /* + Unfortunately the ALI SMB controller maps "no response" and "bus + collision" into a single bit. No response is the usual case so don't + do a printk. + This means that bus collisions go unreported. + */ + if (temp & ALI15X3_STS_COLL) { + result = -ENXIO; + dev_dbg(&adap->dev, + "Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); + } + + /* haven't ever seen this */ + if (temp & ALI15X3_STS_DEV) { + result = -EIO; + dev_err(&adap->dev, "Error: device error\n"); + } + dev_dbg(&adap->dev, "Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), + inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), + inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); + return result; +} + +/* Return negative errno on error. */ +static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data * data) +{ + int i, len; + int temp; + int timeout; + + /* clear all the bits (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); + /* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE); + timeout++) { + msleep(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) { + dev_err(&adap->dev, "Idle wait Timeout! STS=0x%02x\n", temp); + } + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI15X3_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = ALI15X3_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = ALI15X3_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = ALI15X3_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + /* Reset SMBBLKDAT */ + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = ALI15X3_BLOCK_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + outb_p(size, SMBHSTCNT); /* output command */ + + temp = ali15x3_transaction(adap); + if (temp) + return temp; + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK)) + return 0; + + + switch (size) { + case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI15X3_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + /* Reset SMBBLKDAT */ + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); + dev_dbg(&adap->dev, "Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); + } + break; + } + return 0; +} + +static u32 ali15x3_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = ali15x3_access, + .functionality = ali15x3_func, +}; + +static struct i2c_adapter ali15x3_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, +}; + +static const struct pci_device_id ali15x3_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, ali15x3_ids); + +static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + if (ali15x3_setup(dev)) { + dev_err(&dev->dev, + "ALI15X3 not detected, module not inserted.\n"); + return -ENODEV; + } + + /* set up the sysfs linkage to our parent device */ + ali15x3_adapter.dev.parent = &dev->dev; + + snprintf(ali15x3_adapter.name, sizeof(ali15x3_adapter.name), + "SMBus ALI15X3 adapter at %04x", ali15x3_smba); + return i2c_add_adapter(&ali15x3_adapter); +} + +static void __devexit ali15x3_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&ali15x3_adapter); + release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); +} + +static struct pci_driver ali15x3_driver = { + .name = "ali15x3_smbus", + .id_table = ali15x3_ids, + .probe = ali15x3_probe, + .remove = __devexit_p(ali15x3_remove), +}; + +static int __init i2c_ali15x3_init(void) +{ + return pci_register_driver(&ali15x3_driver); +} + +static void __exit i2c_ali15x3_exit(void) +{ + pci_unregister_driver(&ali15x3_driver); +} + +MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, " + "Philip Edelbrock <phil@netroedge.com>, " + "and Mark D. Studebaker <mdsxyz123@yahoo.com>"); +MODULE_DESCRIPTION("ALI15X3 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_ali15x3_init); +module_exit(i2c_ali15x3_exit); diff --git a/drivers/i2c/busses/i2c-amd756-s4882.c b/drivers/i2c/busses/i2c-amd756-s4882.c new file mode 100644 index 00000000..378fcb5d --- /dev/null +++ b/drivers/i2c/busses/i2c-amd756-s4882.c @@ -0,0 +1,262 @@ +/* + * i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard + * + * Copyright (C) 2004, 2008 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. + */ + +/* + * We select the channels by sending commands to the Philips + * PCA9556 chip at I2C address 0x18. The main adapter is used for + * the non-multiplexed part of the bus, and 4 virtual adapters + * are defined for the multiplexed addresses: 0x50-0x53 (memory + * module EEPROM) located on channels 1-4, and 0x4c (LM63) + * located on multiplexed channels 0 and 5-7. We define one + * virtual adapter per CPU, which corresponds to two multiplexed + * channels: + * CPU0: virtual adapter 1, channels 1 and 0 + * CPU1: virtual adapter 2, channels 2 and 5 + * CPU2: virtual adapter 3, channels 3 and 6 + * CPU3: virtual adapter 4, channels 4 and 7 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> + +extern struct i2c_adapter amd756_smbus; + +static struct i2c_adapter *s4882_adapter; +static struct i2c_algorithm *s4882_algo; + +/* Wrapper access functions for multiplexed SMBus */ +static DEFINE_MUTEX(amd756_lock); + +static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data * data) +{ + int error; + + /* We exclude the multiplexed addresses */ + if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30 + || addr == 0x18) + return -ENXIO; + + mutex_lock(&amd756_lock); + + error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write, + command, size, data); + + mutex_unlock(&amd756_lock); + + return error; +} + +/* We remember the last used channels combination so as to only switch + channels when it is really needed. This greatly reduces the SMBus + overhead, but also assumes that nobody will be writing to the PCA9556 + in our back. */ +static u8 last_channels; + +static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data * data, + u8 channels) +{ + int error; + + /* We exclude the non-multiplexed addresses */ + if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30) + return -ENXIO; + + mutex_lock(&amd756_lock); + + if (last_channels != channels) { + union i2c_smbus_data mplxdata; + mplxdata.byte = channels; + + error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0, + I2C_SMBUS_WRITE, 0x01, + I2C_SMBUS_BYTE_DATA, + &mplxdata); + if (error) + goto UNLOCK; + last_channels = channels; + } + error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write, + command, size, data); + +UNLOCK: + mutex_unlock(&amd756_lock); + return error; +} + +static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data * data) +{ + /* CPU0: channels 1 and 0 enabled */ + return amd756_access_channel(adap, addr, flags, read_write, command, + size, data, 0x03); +} + +static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data * data) +{ + /* CPU1: channels 2 and 5 enabled */ + return amd756_access_channel(adap, addr, flags, read_write, command, + size, data, 0x24); +} + +static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data * data) +{ + /* CPU2: channels 3 and 6 enabled */ + return amd756_access_channel(adap, addr, flags, read_write, command, + size, data, 0x48); +} + +static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data * data) +{ + /* CPU3: channels 4 and 7 enabled */ + return amd756_access_channel(adap, addr, flags, read_write, command, + size, data, 0x90); +} + +static int __init amd756_s4882_init(void) +{ + int i, error; + union i2c_smbus_data ioconfig; + + if (!amd756_smbus.dev.parent) + return -ENODEV; + + /* Configure the PCA9556 multiplexer */ + ioconfig.byte = 0x00; /* All I/O to output mode */ + error = i2c_smbus_xfer(&amd756_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03, + I2C_SMBUS_BYTE_DATA, &ioconfig); + if (error) { + dev_err(&amd756_smbus.dev, "PCA9556 configuration failed\n"); + error = -EIO; + goto ERROR0; + } + + /* Unregister physical bus */ + error = i2c_del_adapter(&amd756_smbus); + if (error) { + dev_err(&amd756_smbus.dev, "Physical bus removal failed\n"); + goto ERROR0; + } + + printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n"); + /* Define the 5 virtual adapters and algorithms structures */ + if (!(s4882_adapter = kzalloc(5 * sizeof(struct i2c_adapter), + GFP_KERNEL))) { + error = -ENOMEM; + goto ERROR1; + } + if (!(s4882_algo = kzalloc(5 * sizeof(struct i2c_algorithm), + GFP_KERNEL))) { + error = -ENOMEM; + goto ERROR2; + } + + /* Fill in the new structures */ + s4882_algo[0] = *(amd756_smbus.algo); + s4882_algo[0].smbus_xfer = amd756_access_virt0; + s4882_adapter[0] = amd756_smbus; + s4882_adapter[0].algo = s4882_algo; + s4882_adapter[0].dev.parent = amd756_smbus.dev.parent; + for (i = 1; i < 5; i++) { + s4882_algo[i] = *(amd756_smbus.algo); + s4882_adapter[i] = amd756_smbus; + snprintf(s4882_adapter[i].name, sizeof(s4882_adapter[i].name), + "SMBus 8111 adapter (CPU%d)", i-1); + s4882_adapter[i].algo = s4882_algo+i; + s4882_adapter[i].dev.parent = amd756_smbus.dev.parent; + } + s4882_algo[1].smbus_xfer = amd756_access_virt1; + s4882_algo[2].smbus_xfer = amd756_access_virt2; + s4882_algo[3].smbus_xfer = amd756_access_virt3; + s4882_algo[4].smbus_xfer = amd756_access_virt4; + + /* Register virtual adapters */ + for (i = 0; i < 5; i++) { + error = i2c_add_adapter(s4882_adapter+i); + if (error) { + printk(KERN_ERR "i2c-amd756-s4882: " + "Virtual adapter %d registration " + "failed, module not inserted\n", i); + for (i--; i >= 0; i--) + i2c_del_adapter(s4882_adapter+i); + goto ERROR3; + } + } + + return 0; + +ERROR3: + kfree(s4882_algo); + s4882_algo = NULL; +ERROR2: + kfree(s4882_adapter); + s4882_adapter = NULL; +ERROR1: + /* Restore physical bus */ + i2c_add_adapter(&amd756_smbus); +ERROR0: + return error; +} + +static void __exit amd756_s4882_exit(void) +{ + if (s4882_adapter) { + int i; + + for (i = 0; i < 5; i++) + i2c_del_adapter(s4882_adapter+i); + kfree(s4882_adapter); + s4882_adapter = NULL; + } + kfree(s4882_algo); + s4882_algo = NULL; + + /* Restore physical bus */ + if (i2c_add_adapter(&amd756_smbus)) + printk(KERN_ERR "i2c-amd756-s4882: " + "Physical bus restoration failed\n"); +} + +MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("S4882 SMBus multiplexing"); +MODULE_LICENSE("GPL"); + +module_init(amd756_s4882_init); +module_exit(amd756_s4882_exit); diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c new file mode 100644 index 00000000..03bcd07c --- /dev/null +++ b/drivers/i2c/busses/i2c-amd756.c @@ -0,0 +1,430 @@ +/* + Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org> + + Shamelessly ripped from i2c-piix4.c: + + Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and + Philip Edelbrock <phil@netroedge.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. + + 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. +*/ + +/* + 2002-04-08: Added nForce support. (Csaba Halasz) + 2002-10-03: Fixed nForce PnP I/O port. (Michael Steil) + 2002-12-28: Rewritten into something that resembles a Linux driver (hch) + 2003-11-29: Added back AMD8111 removed by the previous rewrite. + (Philip Pokorny) +*/ + +/* + Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/io.h> + +/* AMD756 SMBus address offsets */ +#define SMB_ADDR_OFFSET 0xE0 +#define SMB_IOSIZE 16 +#define SMB_GLOBAL_STATUS (0x0 + amd756_ioport) +#define SMB_GLOBAL_ENABLE (0x2 + amd756_ioport) +#define SMB_HOST_ADDRESS (0x4 + amd756_ioport) +#define SMB_HOST_DATA (0x6 + amd756_ioport) +#define SMB_HOST_COMMAND (0x8 + amd756_ioport) +#define SMB_HOST_BLOCK_DATA (0x9 + amd756_ioport) +#define SMB_HAS_DATA (0xA + amd756_ioport) +#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport) +#define SMB_HAS_HOST_ADDRESS (0xE + amd756_ioport) +#define SMB_SNOOP_ADDRESS (0xF + amd756_ioport) + +/* PCI Address Constants */ + +/* address of I/O space */ +#define SMBBA 0x058 /* mh */ +#define SMBBANFORCE 0x014 + +/* general configuration */ +#define SMBGCFG 0x041 /* mh */ + +/* silicon revision code */ +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* AMD756 constants */ +#define AMD756_QUICK 0x00 +#define AMD756_BYTE 0x01 +#define AMD756_BYTE_DATA 0x02 +#define AMD756_WORD_DATA 0x03 +#define AMD756_PROCESS_CALL 0x04 +#define AMD756_BLOCK_DATA 0x05 + +static struct pci_driver amd756_driver; +static unsigned short amd756_ioport; + +/* + SMBUS event = I/O 28-29 bit 11 + see E0 for the status bits and enabled in E2 + +*/ +#define GS_ABRT_STS (1 << 0) +#define GS_COL_STS (1 << 1) +#define GS_PRERR_STS (1 << 2) +#define GS_HST_STS (1 << 3) +#define GS_HCYC_STS (1 << 4) +#define GS_TO_STS (1 << 5) +#define GS_SMB_STS (1 << 11) + +#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \ + GS_HCYC_STS | GS_TO_STS ) + +#define GE_CYC_TYPE_MASK (7) +#define GE_HOST_STC (1 << 3) +#define GE_ABORT (1 << 5) + + +static int amd756_transaction(struct i2c_adapter *adap) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&adap->dev, "Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, " + "DAT=%04x\n", inw_p(SMB_GLOBAL_STATUS), + inw_p(SMB_GLOBAL_ENABLE), inw_p(SMB_HOST_ADDRESS), + inb_p(SMB_HOST_DATA)); + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) { + dev_dbg(&adap->dev, "SMBus busy (%04x). Waiting...\n", temp); + do { + msleep(1); + temp = inw_p(SMB_GLOBAL_STATUS); + } while ((temp & (GS_HST_STS | GS_SMB_STS)) && + (timeout++ < MAX_TIMEOUT)); + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "Busy wait timeout (%04x)\n", temp); + goto abort; + } + timeout = 0; + } + + /* start the transaction by setting the start bit */ + outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE); + + /* We will always wait for a fraction of a second! */ + do { + msleep(1); + temp = inw_p(SMB_GLOBAL_STATUS); + } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "Completion timeout!\n"); + goto abort; + } + + if (temp & GS_PRERR_STS) { + result = -ENXIO; + dev_dbg(&adap->dev, "SMBus Protocol error (no response)!\n"); + } + + if (temp & GS_COL_STS) { + result = -EIO; + dev_warn(&adap->dev, "SMBus collision!\n"); + } + + if (temp & GS_TO_STS) { + result = -ETIMEDOUT; + dev_dbg(&adap->dev, "SMBus protocol timeout!\n"); + } + + if (temp & GS_HCYC_STS) + dev_dbg(&adap->dev, "SMBus protocol success!\n"); + + outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); + +#ifdef DEBUG + if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) { + dev_dbg(&adap->dev, + "Failed reset at end of transaction (%04x)\n", temp); + } +#endif + + dev_dbg(&adap->dev, + "Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", + inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), + inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); + + return result; + + abort: + dev_warn(&adap->dev, "Sending abort\n"); + outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE); + msleep(100); + outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); + return -EIO; +} + +/* Return negative errno on error. */ +static s32 amd756_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + size = AMD756_QUICK; + break; + case I2C_SMBUS_BYTE: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMB_HOST_DATA); + size = AMD756_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) + outw_p(data->byte, SMB_HOST_DATA); + size = AMD756_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) + outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */ + size = AMD756_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outw_p(len, SMB_HOST_DATA); + /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], + SMB_HOST_BLOCK_DATA); + } + size = AMD756_BLOCK_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + /* How about enabling interrupts... */ + outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE); + + status = amd756_transaction(adap); + if (status) + return status; + + if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK)) + return 0; + + + switch (size) { + case AMD756_BYTE: + data->byte = inw_p(SMB_HOST_DATA); + break; + case AMD756_BYTE_DATA: + data->byte = inw_p(SMB_HOST_DATA); + break; + case AMD756_WORD_DATA: + data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */ + break; + case AMD756_BLOCK_DATA: + data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f; + if(data->block[0] > 32) + data->block[0] = 32; + /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMB_HOST_BLOCK_DATA); + break; + } + + return 0; +} + +static u32 amd756_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = amd756_access, + .functionality = amd756_func, +}; + +struct i2c_adapter amd756_smbus = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, +}; + +enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 }; +static const char* chipname[] = { + "AMD756", "AMD766", "AMD768", + "nVidia nForce", "AMD8111", +}; + +static const struct pci_device_id amd756_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B), + .driver_data = AMD756 }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413), + .driver_data = AMD766 }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7443), + .driver_data = AMD768 }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), + .driver_data = AMD8111 }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS), + .driver_data = NFORCE }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, amd756_ids); + +static int __devinit amd756_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int nforce = (id->driver_data == NFORCE); + int error; + u8 temp; + + if (amd756_ioport) { + dev_err(&pdev->dev, "Only one device supported " + "(you have a strange motherboard, btw)\n"); + return -ENODEV; + } + + if (nforce) { + if (PCI_FUNC(pdev->devfn) != 1) + return -ENODEV; + + pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport); + amd756_ioport &= 0xfffc; + } else { /* amd */ + if (PCI_FUNC(pdev->devfn) != 3) + return -ENODEV; + + pci_read_config_byte(pdev, SMBGCFG, &temp); + if ((temp & 128) == 0) { + dev_err(&pdev->dev, + "Error: SMBus controller I/O not enabled!\n"); + return -ENODEV; + } + + /* Determine the address of the SMBus areas */ + /* Technically it is a dword but... */ + pci_read_config_word(pdev, SMBBA, &amd756_ioport); + amd756_ioport &= 0xff00; + amd756_ioport += SMB_ADDR_OFFSET; + } + + error = acpi_check_region(amd756_ioport, SMB_IOSIZE, + amd756_driver.name); + if (error) + return -ENODEV; + + if (!request_region(amd756_ioport, SMB_IOSIZE, amd756_driver.name)) { + dev_err(&pdev->dev, "SMB region 0x%x already in use!\n", + amd756_ioport); + return -ENODEV; + } + + pci_read_config_byte(pdev, SMBREV, &temp); + dev_dbg(&pdev->dev, "SMBREV = 0x%X\n", temp); + dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport); + + /* set up the sysfs linkage to our parent device */ + amd756_smbus.dev.parent = &pdev->dev; + + snprintf(amd756_smbus.name, sizeof(amd756_smbus.name), + "SMBus %s adapter at %04x", chipname[id->driver_data], + amd756_ioport); + + error = i2c_add_adapter(&amd756_smbus); + if (error) { + dev_err(&pdev->dev, + "Adapter registration failed, module not inserted\n"); + goto out_err; + } + + return 0; + + out_err: + release_region(amd756_ioport, SMB_IOSIZE); + return error; +} + +static void __devexit amd756_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&amd756_smbus); + release_region(amd756_ioport, SMB_IOSIZE); +} + +static struct pci_driver amd756_driver = { + .name = "amd756_smbus", + .id_table = amd756_ids, + .probe = amd756_probe, + .remove = __devexit_p(amd756_remove), +}; + +static int __init amd756_init(void) +{ + return pci_register_driver(&amd756_driver); +} + +static void __exit amd756_exit(void) +{ + pci_unregister_driver(&amd756_driver); +} + +MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>"); +MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(amd756_smbus); + +module_init(amd756_init) +module_exit(amd756_exit) diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c new file mode 100644 index 00000000..6b6a6b1d --- /dev/null +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -0,0 +1,505 @@ +/* + * SMBus 2.0 driver for AMD-8111 IO-Hub. + * + * Copyright (c) 2002 Vojtech Pavlik + * + * 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. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/acpi.h> +#include <linux/slab.h> +#include <linux/io.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR ("Vojtech Pavlik <vojtech@suse.cz>"); +MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver"); + +struct amd_smbus { + struct pci_dev *dev; + struct i2c_adapter adapter; + int base; + int size; +}; + +static struct pci_driver amd8111_driver; + +/* + * AMD PCI control registers definitions. + */ + +#define AMD_PCI_MISC 0x48 + +#define AMD_PCI_MISC_SCI 0x04 /* deliver SCI */ +#define AMD_PCI_MISC_INT 0x02 /* deliver PCI IRQ */ +#define AMD_PCI_MISC_SPEEDUP 0x01 /* 16x clock speedup */ + +/* + * ACPI 2.0 chapter 13 PCI interface definitions. + */ + +#define AMD_EC_DATA 0x00 /* data register */ +#define AMD_EC_SC 0x04 /* status of controller */ +#define AMD_EC_CMD 0x04 /* command register */ +#define AMD_EC_ICR 0x08 /* interrupt control register */ + +#define AMD_EC_SC_SMI 0x04 /* smi event pending */ +#define AMD_EC_SC_SCI 0x02 /* sci event pending */ +#define AMD_EC_SC_BURST 0x01 /* burst mode enabled */ +#define AMD_EC_SC_CMD 0x08 /* byte in data reg is command */ +#define AMD_EC_SC_IBF 0x02 /* data ready for embedded controller */ +#define AMD_EC_SC_OBF 0x01 /* data ready for host */ + +#define AMD_EC_CMD_RD 0x80 /* read EC */ +#define AMD_EC_CMD_WR 0x81 /* write EC */ +#define AMD_EC_CMD_BE 0x82 /* enable burst mode */ +#define AMD_EC_CMD_BD 0x83 /* disable burst mode */ +#define AMD_EC_CMD_QR 0x84 /* query EC */ + +/* + * ACPI 2.0 chapter 13 access of registers of the EC + */ + +static int amd_ec_wait_write(struct amd_smbus *smbus) +{ + int timeout = 500; + + while ((inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF) && --timeout) + udelay(1); + + if (!timeout) { + dev_warn(&smbus->dev->dev, + "Timeout while waiting for IBF to clear\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int amd_ec_wait_read(struct amd_smbus *smbus) +{ + int timeout = 500; + + while ((~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF) && --timeout) + udelay(1); + + if (!timeout) { + dev_warn(&smbus->dev->dev, + "Timeout while waiting for OBF to set\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int amd_ec_read(struct amd_smbus *smbus, unsigned char address, + unsigned char *data) +{ + int status; + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD); + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(address, smbus->base + AMD_EC_DATA); + + status = amd_ec_wait_read(smbus); + if (status) + return status; + *data = inb(smbus->base + AMD_EC_DATA); + + return 0; +} + +static int amd_ec_write(struct amd_smbus *smbus, unsigned char address, + unsigned char data) +{ + int status; + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD); + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(address, smbus->base + AMD_EC_DATA); + + status = amd_ec_wait_write(smbus); + if (status) + return status; + outb(data, smbus->base + AMD_EC_DATA); + + return 0; +} + +/* + * ACPI 2.0 chapter 13 SMBus 2.0 EC register model + */ + +#define AMD_SMB_PRTCL 0x00 /* protocol, PEC */ +#define AMD_SMB_STS 0x01 /* status */ +#define AMD_SMB_ADDR 0x02 /* address */ +#define AMD_SMB_CMD 0x03 /* command */ +#define AMD_SMB_DATA 0x04 /* 32 data registers */ +#define AMD_SMB_BCNT 0x24 /* number of data bytes */ +#define AMD_SMB_ALRM_A 0x25 /* alarm address */ +#define AMD_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ + +#define AMD_SMB_STS_DONE 0x80 +#define AMD_SMB_STS_ALRM 0x40 +#define AMD_SMB_STS_RES 0x20 +#define AMD_SMB_STS_STATUS 0x1f + +#define AMD_SMB_STATUS_OK 0x00 +#define AMD_SMB_STATUS_FAIL 0x07 +#define AMD_SMB_STATUS_DNAK 0x10 +#define AMD_SMB_STATUS_DERR 0x11 +#define AMD_SMB_STATUS_CMD_DENY 0x12 +#define AMD_SMB_STATUS_UNKNOWN 0x13 +#define AMD_SMB_STATUS_ACC_DENY 0x17 +#define AMD_SMB_STATUS_TIMEOUT 0x18 +#define AMD_SMB_STATUS_NOTSUP 0x19 +#define AMD_SMB_STATUS_BUSY 0x1A +#define AMD_SMB_STATUS_PEC 0x1F + +#define AMD_SMB_PRTCL_WRITE 0x00 +#define AMD_SMB_PRTCL_READ 0x01 +#define AMD_SMB_PRTCL_QUICK 0x02 +#define AMD_SMB_PRTCL_BYTE 0x04 +#define AMD_SMB_PRTCL_BYTE_DATA 0x06 +#define AMD_SMB_PRTCL_WORD_DATA 0x08 +#define AMD_SMB_PRTCL_BLOCK_DATA 0x0a +#define AMD_SMB_PRTCL_PROC_CALL 0x0c +#define AMD_SMB_PRTCL_BLOCK_PROC_CALL 0x0d +#define AMD_SMB_PRTCL_I2C_BLOCK_DATA 0x4a +#define AMD_SMB_PRTCL_PEC 0x80 + + +static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + struct amd_smbus *smbus = adap->algo_data; + unsigned char protocol, len, pec, temp[2]; + int i, status; + + protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ + : AMD_SMB_PRTCL_WRITE; + pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0; + + switch (size) { + case I2C_SMBUS_QUICK: + protocol |= AMD_SMB_PRTCL_QUICK; + read_write = I2C_SMBUS_WRITE; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + status = amd_ec_write(smbus, AMD_SMB_CMD, + command); + if (status) + return status; + } + protocol |= AMD_SMB_PRTCL_BYTE; + break; + + case I2C_SMBUS_BYTE_DATA: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) { + status = amd_ec_write(smbus, AMD_SMB_DATA, + data->byte); + if (status) + return status; + } + protocol |= AMD_SMB_PRTCL_BYTE_DATA; + break; + + case I2C_SMBUS_WORD_DATA: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) { + status = amd_ec_write(smbus, AMD_SMB_DATA, + data->word & 0xff); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_DATA + 1, + data->word >> 8); + if (status) + return status; + } + protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; + break; + + case I2C_SMBUS_BLOCK_DATA: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) { + len = min_t(u8, data->block[0], + I2C_SMBUS_BLOCK_MAX); + status = amd_ec_write(smbus, AMD_SMB_BCNT, len); + if (status) + return status; + for (i = 0; i < len; i++) { + status = + amd_ec_write(smbus, AMD_SMB_DATA + i, + data->block[i + 1]); + if (status) + return status; + } + } + protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = min_t(u8, data->block[0], + I2C_SMBUS_BLOCK_MAX); + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_BCNT, len); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) + for (i = 0; i < len; i++) { + status = + amd_ec_write(smbus, AMD_SMB_DATA + i, + data->block[i + 1]); + if (status) + return status; + } + protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; + break; + + case I2C_SMBUS_PROC_CALL: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_DATA, + data->word & 0xff); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_DATA + 1, + data->word >> 8); + if (status) + return status; + protocol = AMD_SMB_PRTCL_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + case I2C_SMBUS_BLOCK_PROC_CALL: + len = min_t(u8, data->block[0], + I2C_SMBUS_BLOCK_MAX - 1); + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_BCNT, len); + if (status) + return status; + for (i = 0; i < len; i++) { + status = amd_ec_write(smbus, AMD_SMB_DATA + i, + data->block[i + 1]); + if (status) + return status; + } + protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + status = amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_PRTCL, protocol); + if (status) + return status; + + status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0); + if (status) + return status; + + if (~temp[0] & AMD_SMB_STS_DONE) { + udelay(500); + status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0); + if (status) + return status; + } + + if (~temp[0] & AMD_SMB_STS_DONE) { + msleep(1); + status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0); + if (status) + return status; + } + + if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS)) + return -EIO; + + if (read_write == I2C_SMBUS_WRITE) + return 0; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte); + if (status) + return status; + break; + + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); + if (status) + return status; + status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); + if (status) + return status; + data->word = (temp[1] << 8) | temp[0]; + break; + + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + status = amd_ec_read(smbus, AMD_SMB_BCNT, &len); + if (status) + return status; + len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); + case I2C_SMBUS_I2C_BLOCK_DATA: + for (i = 0; i < len; i++) { + status = amd_ec_read(smbus, AMD_SMB_DATA + i, + data->block + i + 1); + if (status) + return status; + } + data->block[0] = len; + break; + } + + return 0; +} + + +static u32 amd8111_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_PEC; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = amd8111_access, + .functionality = amd8111_func, +}; + + +static const struct pci_device_id amd8111_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, amd8111_ids); + +static int __devinit amd8111_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct amd_smbus *smbus; + int error; + + if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO)) + return -ENODEV; + + smbus = kzalloc(sizeof(struct amd_smbus), GFP_KERNEL); + if (!smbus) + return -ENOMEM; + + smbus->dev = dev; + smbus->base = pci_resource_start(dev, 0); + smbus->size = pci_resource_len(dev, 0); + + error = acpi_check_resource_conflict(&dev->resource[0]); + if (error) { + error = -ENODEV; + goto out_kfree; + } + + if (!request_region(smbus->base, smbus->size, amd8111_driver.name)) { + error = -EBUSY; + goto out_kfree; + } + + smbus->adapter.owner = THIS_MODULE; + snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), + "SMBus2 AMD8111 adapter at %04x", smbus->base); + smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus->adapter.algo = &smbus_algorithm; + smbus->adapter.algo_data = smbus; + + /* set up the sysfs linkage to our parent device */ + smbus->adapter.dev.parent = &dev->dev; + + pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0); + error = i2c_add_adapter(&smbus->adapter); + if (error) + goto out_release_region; + + pci_set_drvdata(dev, smbus); + return 0; + + out_release_region: + release_region(smbus->base, smbus->size); + out_kfree: + kfree(smbus); + return error; +} + +static void __devexit amd8111_remove(struct pci_dev *dev) +{ + struct amd_smbus *smbus = pci_get_drvdata(dev); + + i2c_del_adapter(&smbus->adapter); + release_region(smbus->base, smbus->size); + kfree(smbus); +} + +static struct pci_driver amd8111_driver = { + .name = "amd8111_smbus2", + .id_table = amd8111_ids, + .probe = amd8111_probe, + .remove = __devexit_p(amd8111_remove), +}; + +static int __init i2c_amd8111_init(void) +{ + return pci_register_driver(&amd8111_driver); +} + +static void __exit i2c_amd8111_exit(void) +{ + pci_unregister_driver(&amd8111_driver); +} + +module_init(i2c_amd8111_init); +module_exit(i2c_amd8111_exit); diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c new file mode 100644 index 00000000..305c0750 --- /dev/null +++ b/drivers/i2c/busses/i2c-at91.c @@ -0,0 +1,327 @@ +/* + i2c Support for Atmel's AT91 Two-Wire Interface (TWI) + + Copyright (C) 2004 Rick Bronson + Converted to 2.6 by Andrew Victor <andrew@sanpeople.com> + + Borrowed heavily from original work by: + Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.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/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#include <mach/at91_twi.h> +#include <mach/board.h> +#include <mach/cpu.h> + +#define TWI_CLOCK 100000 /* Hz. max 400 Kbits/sec */ + + +static struct clk *twi_clk; +static void __iomem *twi_base; + +#define at91_twi_read(reg) __raw_readl(twi_base + (reg)) +#define at91_twi_write(reg, val) __raw_writel((val), twi_base + (reg)) + + +/* + * Initialize the TWI hardware registers. + */ +static void __devinit at91_twi_hwinit(void) +{ + unsigned long cdiv, ckdiv; + + at91_twi_write(AT91_TWI_IDR, 0xffffffff); /* Disable all interrupts */ + at91_twi_write(AT91_TWI_CR, AT91_TWI_SWRST); /* Reset peripheral */ + at91_twi_write(AT91_TWI_CR, AT91_TWI_MSEN); /* Set Master mode */ + + /* Calcuate clock dividers */ + cdiv = (clk_get_rate(twi_clk) / (2 * TWI_CLOCK)) - 3; + cdiv = cdiv + 1; /* round up */ + ckdiv = 0; + while (cdiv > 255) { + ckdiv++; + cdiv = cdiv >> 1; + } + + if (cpu_is_at91rm9200()) { /* AT91RM9200 Errata #22 */ + if (ckdiv > 5) { + printk(KERN_ERR "AT91 I2C: Invalid TWI_CLOCK value!\n"); + ckdiv = 5; + } + } + + at91_twi_write(AT91_TWI_CWGR, (ckdiv << 16) | (cdiv << 8) | cdiv); +} + +/* + * Poll the i2c status register until the specified bit is set. + * Returns 0 if timed out (100 msec). + */ +static short at91_poll_status(unsigned long bit) +{ + int loop_cntr = 10000; + + do { + udelay(10); + } while (!(at91_twi_read(AT91_TWI_SR) & bit) && (--loop_cntr > 0)); + + return (loop_cntr > 0); +} + +static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length) +{ + /* Send Start */ + at91_twi_write(AT91_TWI_CR, AT91_TWI_START); + + /* Read data */ + while (length--) { + if (!length) /* need to send Stop before reading last byte */ + at91_twi_write(AT91_TWI_CR, AT91_TWI_STOP); + if (!at91_poll_status(AT91_TWI_RXRDY)) { + dev_dbg(&adap->dev, "RXRDY timeout\n"); + return -ETIMEDOUT; + } + *buf++ = (at91_twi_read(AT91_TWI_RHR) & 0xff); + } + + return 0; +} + +static int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length) +{ + /* Load first byte into transmitter */ + at91_twi_write(AT91_TWI_THR, *buf++); + + /* Send Start */ + at91_twi_write(AT91_TWI_CR, AT91_TWI_START); + + do { + if (!at91_poll_status(AT91_TWI_TXRDY)) { + dev_dbg(&adap->dev, "TXRDY timeout\n"); + return -ETIMEDOUT; + } + + length--; /* byte was transmitted */ + + if (length > 0) /* more data to send? */ + at91_twi_write(AT91_TWI_THR, *buf++); + } while (length); + + /* Send Stop */ + at91_twi_write(AT91_TWI_CR, AT91_TWI_STOP); + + return 0; +} + +/* + * Generic i2c master transfer entrypoint. + * + * Note: We do not use Atmel's feature of storing the "internal device address". + * Instead the "internal device address" has to be written using a separate + * i2c message. + * http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html + */ +static int at91_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num) +{ + int i, ret; + + dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num); + + for (i = 0; i < num; i++) { + dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i, + pmsg->flags & I2C_M_RD ? "read" : "writ", + pmsg->len, pmsg->len > 1 ? "s" : "", + pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr); + + at91_twi_write(AT91_TWI_MMR, (pmsg->addr << 16) + | ((pmsg->flags & I2C_M_RD) ? AT91_TWI_MREAD : 0)); + + if (pmsg->len && pmsg->buf) { /* sanity check */ + if (pmsg->flags & I2C_M_RD) + ret = xfer_read(adap, pmsg->buf, pmsg->len); + else + ret = xfer_write(adap, pmsg->buf, pmsg->len); + + if (ret) + return ret; + + /* Wait until transfer is finished */ + if (!at91_poll_status(AT91_TWI_TXCOMP)) { + dev_dbg(&adap->dev, "TXCOMP timeout\n"); + return -ETIMEDOUT; + } + } + dev_dbg(&adap->dev, "transfer complete\n"); + pmsg++; /* next message */ + } + return i; +} + +/* + * Return list of supported functionality. + */ +static u32 at91_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm at91_algorithm = { + .master_xfer = at91_xfer, + .functionality = at91_func, +}; + +/* + * Main initialization routine. + */ +static int __devinit at91_i2c_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adapter; + struct resource *res; + int rc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!request_mem_region(res->start, resource_size(res), "at91_i2c")) + return -EBUSY; + + twi_base = ioremap(res->start, resource_size(res)); + if (!twi_base) { + rc = -ENOMEM; + goto fail0; + } + + twi_clk = clk_get(NULL, "twi_clk"); + if (IS_ERR(twi_clk)) { + dev_err(&pdev->dev, "no clock defined\n"); + rc = -ENODEV; + goto fail1; + } + + adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (adapter == NULL) { + dev_err(&pdev->dev, "can't allocate inteface!\n"); + rc = -ENOMEM; + goto fail2; + } + snprintf(adapter->name, sizeof(adapter->name), "AT91"); + adapter->algo = &at91_algorithm; + adapter->class = I2C_CLASS_HWMON; + adapter->dev.parent = &pdev->dev; + /* adapter->id == 0 ... only one TWI controller for now */ + + platform_set_drvdata(pdev, adapter); + + clk_enable(twi_clk); /* enable peripheral clock */ + at91_twi_hwinit(); /* initialize TWI controller */ + + rc = i2c_add_numbered_adapter(adapter); + if (rc) { + dev_err(&pdev->dev, "Adapter %s registration failed\n", + adapter->name); + goto fail3; + } + + dev_info(&pdev->dev, "AT91 i2c bus driver.\n"); + return 0; + +fail3: + platform_set_drvdata(pdev, NULL); + kfree(adapter); + clk_disable(twi_clk); +fail2: + clk_put(twi_clk); +fail1: + iounmap(twi_base); +fail0: + release_mem_region(res->start, resource_size(res)); + + return rc; +} + +static int __devexit at91_i2c_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct resource *res; + int rc; + + rc = i2c_del_adapter(adapter); + platform_set_drvdata(pdev, NULL); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(twi_base); + release_mem_region(res->start, resource_size(res)); + + clk_disable(twi_clk); /* disable peripheral clock */ + clk_put(twi_clk); + + return rc; +} + +#ifdef CONFIG_PM + +/* NOTE: could save a few mA by keeping clock off outside of at91_xfer... */ + +static int at91_i2c_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + clk_disable(twi_clk); + return 0; +} + +static int at91_i2c_resume(struct platform_device *pdev) +{ + return clk_enable(twi_clk); +} + +#else +#define at91_i2c_suspend NULL +#define at91_i2c_resume NULL +#endif + +/* work with "modprobe at91_i2c" from hotplugging or coldplugging */ +MODULE_ALIAS("platform:at91_i2c"); + +static struct platform_driver at91_i2c_driver = { + .probe = at91_i2c_probe, + .remove = __devexit_p(at91_i2c_remove), + .suspend = at91_i2c_suspend, + .resume = at91_i2c_resume, + .driver = { + .name = "at91_i2c", + .owner = THIS_MODULE, + }, +}; + +static int __init at91_i2c_init(void) +{ + return platform_driver_register(&at91_i2c_driver); +} + +static void __exit at91_i2c_exit(void) +{ + platform_driver_unregister(&at91_i2c_driver); +} + +module_init(at91_i2c_init); +module_exit(at91_i2c_exit); + +MODULE_AUTHOR("Rick Bronson"); +MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c new file mode 100644 index 00000000..532828bc --- /dev/null +++ b/drivers/i2c/busses/i2c-au1550.c @@ -0,0 +1,497 @@ +/* + * i2c-au1550.c: SMBus (i2c) adapter for Alchemy PSC interface + * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com> + * + * 2.6 port by Matt Porter <mporter@kernel.crashing.org> + * + * The documentation describes this as an SMBus controller, but it doesn't + * understand any of the SMBus protocol in hardware. It's really an I2C + * controller that could emulate most of the SMBus in software. + * + * This is just a skeleton adapter to use with the Au1550 PSC + * algorithm. It was developed for the Pb1550, but will work with + * any Au1550 board that has a similar PSC configuration. + * + * 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/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/slab.h> + +#include <asm/mach-au1x00/au1xxx.h> +#include <asm/mach-au1x00/au1xxx_psc.h> + +struct i2c_au1550_data { + u32 psc_base; + int xfer_timeout; + int ack_timeout; + struct i2c_adapter adap; + struct resource *ioarea; +}; + +static int +wait_xfer_done(struct i2c_au1550_data *adap) +{ + u32 stat; + int i; + volatile psc_smb_t *sp; + + sp = (volatile psc_smb_t *)(adap->psc_base); + + /* Wait for Tx Buffer Empty + */ + for (i = 0; i < adap->xfer_timeout; i++) { + stat = sp->psc_smbstat; + au_sync(); + if ((stat & PSC_SMBSTAT_TE) != 0) + return 0; + + udelay(1); + } + + return -ETIMEDOUT; +} + +static int +wait_ack(struct i2c_au1550_data *adap) +{ + u32 stat; + volatile psc_smb_t *sp; + + if (wait_xfer_done(adap)) + return -ETIMEDOUT; + + sp = (volatile psc_smb_t *)(adap->psc_base); + + stat = sp->psc_smbevnt; + au_sync(); + + if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0) + return -ETIMEDOUT; + + return 0; +} + +static int +wait_master_done(struct i2c_au1550_data *adap) +{ + u32 stat; + int i; + volatile psc_smb_t *sp; + + sp = (volatile psc_smb_t *)(adap->psc_base); + + /* Wait for Master Done. + */ + for (i = 0; i < adap->xfer_timeout; i++) { + stat = sp->psc_smbevnt; + au_sync(); + if ((stat & PSC_SMBEVNT_MD) != 0) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int +do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q) +{ + volatile psc_smb_t *sp; + u32 stat; + + sp = (volatile psc_smb_t *)(adap->psc_base); + + /* Reset the FIFOs, clear events. + */ + stat = sp->psc_smbstat; + sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR; + au_sync(); + + if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) { + sp->psc_smbpcr = PSC_SMBPCR_DC; + au_sync(); + do { + stat = sp->psc_smbpcr; + au_sync(); + } while ((stat & PSC_SMBPCR_DC) != 0); + udelay(50); + } + + /* Write out the i2c chip address and specify operation + */ + addr <<= 1; + if (rd) + addr |= 1; + + /* zero-byte xfers stop immediately */ + if (q) + addr |= PSC_SMBTXRX_STP; + + /* Put byte into fifo, start up master. + */ + sp->psc_smbtxrx = addr; + au_sync(); + sp->psc_smbpcr = PSC_SMBPCR_MS; + au_sync(); + if (wait_ack(adap)) + return -EIO; + return (q) ? wait_master_done(adap) : 0; +} + +static u32 +wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data) +{ + int j; + u32 data, stat; + volatile psc_smb_t *sp; + + if (wait_xfer_done(adap)) + return -EIO; + + sp = (volatile psc_smb_t *)(adap->psc_base); + + j = adap->xfer_timeout * 100; + do { + j--; + if (j <= 0) + return -EIO; + + stat = sp->psc_smbstat; + au_sync(); + if ((stat & PSC_SMBSTAT_RE) == 0) + j = 0; + else + udelay(1); + } while (j > 0); + data = sp->psc_smbtxrx; + au_sync(); + *ret_data = data; + + return 0; +} + +static int +i2c_read(struct i2c_au1550_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + u32 data; + volatile psc_smb_t *sp; + + if (len == 0) + return 0; + + /* A read is performed by stuffing the transmit fifo with + * zero bytes for timing, waiting for bytes to appear in the + * receive fifo, then reading the bytes. + */ + + sp = (volatile psc_smb_t *)(adap->psc_base); + + i = 0; + while (i < (len-1)) { + sp->psc_smbtxrx = 0; + au_sync(); + if (wait_for_rx_byte(adap, &data)) + return -EIO; + + buf[i] = data; + i++; + } + + /* The last byte has to indicate transfer done. + */ + sp->psc_smbtxrx = PSC_SMBTXRX_STP; + au_sync(); + if (wait_master_done(adap)) + return -EIO; + + data = sp->psc_smbtxrx; + au_sync(); + buf[i] = data; + return 0; +} + +static int +i2c_write(struct i2c_au1550_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + u32 data; + volatile psc_smb_t *sp; + + if (len == 0) + return 0; + + sp = (volatile psc_smb_t *)(adap->psc_base); + + i = 0; + while (i < (len-1)) { + data = buf[i]; + sp->psc_smbtxrx = data; + au_sync(); + if (wait_ack(adap)) + return -EIO; + i++; + } + + /* The last byte has to indicate transfer done. + */ + data = buf[i]; + data |= PSC_SMBTXRX_STP; + sp->psc_smbtxrx = data; + au_sync(); + if (wait_master_done(adap)) + return -EIO; + return 0; +} + +static int +au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + struct i2c_au1550_data *adap = i2c_adap->algo_data; + volatile psc_smb_t *sp = (volatile psc_smb_t *)adap->psc_base; + struct i2c_msg *p; + int i, err = 0; + + sp->psc_ctrl = PSC_CTRL_ENABLE; + au_sync(); + + for (i = 0; !err && i < num; i++) { + p = &msgs[i]; + err = do_address(adap, p->addr, p->flags & I2C_M_RD, + (p->len == 0)); + if (err || !p->len) + continue; + if (p->flags & I2C_M_RD) + err = i2c_read(adap, p->buf, p->len); + else + err = i2c_write(adap, p->buf, p->len); + } + + /* Return the number of messages processed, or the error code. + */ + if (err == 0) + err = num; + + sp->psc_ctrl = PSC_CTRL_SUSPEND; + au_sync(); + + return err; +} + +static u32 +au1550_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm au1550_algo = { + .master_xfer = au1550_xfer, + .functionality = au1550_func, +}; + +static void i2c_au1550_setup(struct i2c_au1550_data *priv) +{ + volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; + u32 stat; + + sp->psc_ctrl = PSC_CTRL_DISABLE; + au_sync(); + sp->psc_sel = PSC_SEL_PS_SMBUSMODE; + sp->psc_smbcfg = 0; + au_sync(); + sp->psc_ctrl = PSC_CTRL_ENABLE; + au_sync(); + do { + stat = sp->psc_smbstat; + au_sync(); + } while ((stat & PSC_SMBSTAT_SR) == 0); + + sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 | + PSC_SMBCFG_DD_DISABLE); + + /* Divide by 8 to get a 6.25 MHz clock. The later protocol + * timings are based on this clock. + */ + sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8); + sp->psc_smbmsk = PSC_SMBMSK_ALLMASK; + au_sync(); + + /* Set the protocol timer values. See Table 71 in the + * Au1550 Data Book for standard timing values. + */ + sp->psc_smbtmr = PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \ + PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \ + PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \ + PSC_SMBTMR_SET_CH(15); + au_sync(); + + sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE; + do { + stat = sp->psc_smbstat; + au_sync(); + } while ((stat & PSC_SMBSTAT_SR) == 0); + + sp->psc_ctrl = PSC_CTRL_SUSPEND; + au_sync(); +} + +static void i2c_au1550_disable(struct i2c_au1550_data *priv) +{ + volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base; + + sp->psc_smbcfg = 0; + sp->psc_ctrl = PSC_CTRL_DISABLE; + au_sync(); +} + +/* + * registering functions to load algorithms at runtime + * Prior to calling us, the 50MHz clock frequency and routing + * must have been set up for the PSC indicated by the adapter. + */ +static int __devinit +i2c_au1550_probe(struct platform_device *pdev) +{ + struct i2c_au1550_data *priv; + struct resource *r; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -ENODEV; + goto out; + } + + priv = kzalloc(sizeof(struct i2c_au1550_data), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto out; + } + + priv->ioarea = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!priv->ioarea) { + ret = -EBUSY; + goto out_mem; + } + + priv->psc_base = CKSEG1ADDR(r->start); + priv->xfer_timeout = 200; + priv->ack_timeout = 200; + + priv->adap.nr = pdev->id; + priv->adap.algo = &au1550_algo; + priv->adap.algo_data = priv; + priv->adap.dev.parent = &pdev->dev; + strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name)); + + /* Now, set up the PSC for SMBus PIO mode. + */ + i2c_au1550_setup(priv); + + ret = i2c_add_numbered_adapter(&priv->adap); + if (ret == 0) { + platform_set_drvdata(pdev, priv); + return 0; + } + + i2c_au1550_disable(priv); + + release_resource(priv->ioarea); + kfree(priv->ioarea); +out_mem: + kfree(priv); +out: + return ret; +} + +static int __devexit +i2c_au1550_remove(struct platform_device *pdev) +{ + struct i2c_au1550_data *priv = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&priv->adap); + i2c_au1550_disable(priv); + release_resource(priv->ioarea); + kfree(priv->ioarea); + kfree(priv); + return 0; +} + +#ifdef CONFIG_PM +static int +i2c_au1550_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct i2c_au1550_data *priv = platform_get_drvdata(pdev); + + i2c_au1550_disable(priv); + + return 0; +} + +static int +i2c_au1550_resume(struct platform_device *pdev) +{ + struct i2c_au1550_data *priv = platform_get_drvdata(pdev); + + i2c_au1550_setup(priv); + + return 0; +} +#else +#define i2c_au1550_suspend NULL +#define i2c_au1550_resume NULL +#endif + +static struct platform_driver au1xpsc_smbus_driver = { + .driver = { + .name = "au1xpsc_smbus", + .owner = THIS_MODULE, + }, + .probe = i2c_au1550_probe, + .remove = __devexit_p(i2c_au1550_remove), + .suspend = i2c_au1550_suspend, + .resume = i2c_au1550_resume, +}; + +static int __init +i2c_au1550_init(void) +{ + return platform_driver_register(&au1xpsc_smbus_driver); +} + +static void __exit +i2c_au1550_exit(void) +{ + platform_driver_unregister(&au1xpsc_smbus_driver); +} + +MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC."); +MODULE_DESCRIPTION("SMBus adapter Alchemy pb1550"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:au1xpsc_smbus"); + +module_init (i2c_au1550_init); +module_exit (i2c_au1550_exit); diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c new file mode 100644 index 00000000..cbc98aea --- /dev/null +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -0,0 +1,797 @@ +/* + * Blackfin On-Chip Two Wire Interface Driver + * + * Copyright 2005-2007 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/timer.h> +#include <linux/spinlock.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/delay.h> + +#include <asm/blackfin.h> +#include <asm/portmux.h> +#include <asm/irq.h> + +/* SMBus mode*/ +#define TWI_I2C_MODE_STANDARD 1 +#define TWI_I2C_MODE_STANDARDSUB 2 +#define TWI_I2C_MODE_COMBINED 3 +#define TWI_I2C_MODE_REPEAT 4 + +struct bfin_twi_iface { + int irq; + spinlock_t lock; + char read_write; + u8 command; + u8 *transPtr; + int readNum; + int writeNum; + int cur_mode; + int manual_stop; + int result; + struct i2c_adapter adap; + struct completion complete; + struct i2c_msg *pmsg; + int msg_num; + int cur_msg; + u16 saved_clkdiv; + u16 saved_control; + void __iomem *regs_base; +}; + + +#define DEFINE_TWI_REG(reg, off) \ +static inline u16 read_##reg(struct bfin_twi_iface *iface) \ + { return bfin_read16(iface->regs_base + (off)); } \ +static inline void write_##reg(struct bfin_twi_iface *iface, u16 v) \ + { bfin_write16(iface->regs_base + (off), v); } + +DEFINE_TWI_REG(CLKDIV, 0x00) +DEFINE_TWI_REG(CONTROL, 0x04) +DEFINE_TWI_REG(SLAVE_CTL, 0x08) +DEFINE_TWI_REG(SLAVE_STAT, 0x0C) +DEFINE_TWI_REG(SLAVE_ADDR, 0x10) +DEFINE_TWI_REG(MASTER_CTL, 0x14) +DEFINE_TWI_REG(MASTER_STAT, 0x18) +DEFINE_TWI_REG(MASTER_ADDR, 0x1C) +DEFINE_TWI_REG(INT_STAT, 0x20) +DEFINE_TWI_REG(INT_MASK, 0x24) +DEFINE_TWI_REG(FIFO_CTL, 0x28) +DEFINE_TWI_REG(FIFO_STAT, 0x2C) +DEFINE_TWI_REG(XMT_DATA8, 0x80) +DEFINE_TWI_REG(XMT_DATA16, 0x84) +DEFINE_TWI_REG(RCV_DATA8, 0x88) +DEFINE_TWI_REG(RCV_DATA16, 0x8C) + +static const u16 pin_req[2][3] = { + {P_TWI0_SCL, P_TWI0_SDA, 0}, + {P_TWI1_SCL, P_TWI1_SDA, 0}, +}; + +static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, + unsigned short twi_int_status) +{ + unsigned short mast_stat = read_MASTER_STAT(iface); + + if (twi_int_status & XMTSERV) { + /* Transmit next data */ + if (iface->writeNum > 0) { + SSYNC(); + write_XMT_DATA8(iface, *(iface->transPtr++)); + iface->writeNum--; + } + /* start receive immediately after complete sending in + * combine mode. + */ + else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | MDIR | RSTART); + else if (iface->manual_stop) + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | STOP); + else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | RSTART | MDIR); + else + write_MASTER_CTL(iface, + (read_MASTER_CTL(iface) | RSTART) & ~MDIR); + } + } + if (twi_int_status & RCVSERV) { + if (iface->readNum > 0) { + /* Receive next data */ + *(iface->transPtr) = read_RCV_DATA8(iface); + if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { + /* Change combine mode into sub mode after + * read first data. + */ + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + /* Get read number from first byte in block + * combine mode. + */ + if (iface->readNum == 1 && iface->manual_stop) + iface->readNum = *iface->transPtr + 1; + } + iface->transPtr++; + iface->readNum--; + } else if (iface->manual_stop) { + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | STOP); + } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | RSTART | MDIR); + else + write_MASTER_CTL(iface, + (read_MASTER_CTL(iface) | RSTART) & ~MDIR); + } + } + if (twi_int_status & MERR) { + write_INT_MASK(iface, 0); + write_MASTER_STAT(iface, 0x3e); + write_MASTER_CTL(iface, 0); + iface->result = -EIO; + + if (mast_stat & LOSTARB) + dev_dbg(&iface->adap.dev, "Lost Arbitration\n"); + if (mast_stat & ANAK) + dev_dbg(&iface->adap.dev, "Address Not Acknowledged\n"); + if (mast_stat & DNAK) + dev_dbg(&iface->adap.dev, "Data Not Acknowledged\n"); + if (mast_stat & BUFRDERR) + dev_dbg(&iface->adap.dev, "Buffer Read Error\n"); + if (mast_stat & BUFWRERR) + dev_dbg(&iface->adap.dev, "Buffer Write Error\n"); + + /* Faulty slave devices, may drive SDA low after a transfer + * finishes. To release the bus this code generates up to 9 + * extra clocks until SDA is released. + */ + + if (read_MASTER_STAT(iface) & SDASEN) { + int cnt = 9; + do { + write_MASTER_CTL(iface, SCLOVR); + udelay(6); + write_MASTER_CTL(iface, 0); + udelay(6); + } while ((read_MASTER_STAT(iface) & SDASEN) && cnt--); + + write_MASTER_CTL(iface, SDAOVR | SCLOVR); + udelay(6); + write_MASTER_CTL(iface, SDAOVR); + udelay(6); + write_MASTER_CTL(iface, 0); + } + + /* If it is a quick transfer, only address without data, + * not an err, return 1. + */ + if (iface->cur_mode == TWI_I2C_MODE_STANDARD && + iface->transPtr == NULL && + (twi_int_status & MCOMP) && (mast_stat & DNAK)) + iface->result = 1; + + complete(&iface->complete); + return; + } + if (twi_int_status & MCOMP) { + if ((read_MASTER_CTL(iface) & MEN) == 0 && + (iface->cur_mode == TWI_I2C_MODE_REPEAT || + iface->cur_mode == TWI_I2C_MODE_COMBINED)) { + iface->result = -1; + write_INT_MASK(iface, 0); + write_MASTER_CTL(iface, 0); + } else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { + if (iface->readNum == 0) { + /* set the read number to 1 and ask for manual + * stop in block combine mode + */ + iface->readNum = 1; + iface->manual_stop = 1; + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | (0xff << 6)); + } else { + /* set the readd number in other + * combine mode. + */ + write_MASTER_CTL(iface, + (read_MASTER_CTL(iface) & + (~(0xff << 6))) | + (iface->readNum << 6)); + } + /* remove restart bit and enable master receive */ + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) & ~RSTART); + } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg+1 < iface->msg_num) { + iface->cur_msg++; + iface->transPtr = iface->pmsg[iface->cur_msg].buf; + iface->writeNum = iface->readNum = + iface->pmsg[iface->cur_msg].len; + /* Set Transmit device address */ + write_MASTER_ADDR(iface, + iface->pmsg[iface->cur_msg].addr); + if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD) + iface->read_write = I2C_SMBUS_READ; + else { + iface->read_write = I2C_SMBUS_WRITE; + /* Transmit first data */ + if (iface->writeNum > 0) { + write_XMT_DATA8(iface, + *(iface->transPtr++)); + iface->writeNum--; + } + } + + if (iface->pmsg[iface->cur_msg].len <= 255) + write_MASTER_CTL(iface, + (read_MASTER_CTL(iface) & + (~(0xff << 6))) | + (iface->pmsg[iface->cur_msg].len << 6)); + else { + write_MASTER_CTL(iface, + (read_MASTER_CTL(iface) | + (0xff << 6))); + iface->manual_stop = 1; + } + /* remove restart bit and enable master receive */ + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) & ~RSTART); + } else { + iface->result = 1; + write_INT_MASK(iface, 0); + write_MASTER_CTL(iface, 0); + } + } + complete(&iface->complete); +} + +/* Interrupt handler */ +static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id) +{ + struct bfin_twi_iface *iface = dev_id; + unsigned long flags; + unsigned short twi_int_status; + + spin_lock_irqsave(&iface->lock, flags); + while (1) { + twi_int_status = read_INT_STAT(iface); + if (!twi_int_status) + break; + /* Clear interrupt status */ + write_INT_STAT(iface, twi_int_status); + bfin_twi_handle_interrupt(iface, twi_int_status); + SSYNC(); + } + spin_unlock_irqrestore(&iface->lock, flags); + return IRQ_HANDLED; +} + +/* + * One i2c master transfer + */ +static int bfin_twi_do_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct bfin_twi_iface *iface = adap->algo_data; + struct i2c_msg *pmsg; + int rc = 0; + + if (!(read_CONTROL(iface) & TWI_ENA)) + return -ENXIO; + + while (read_MASTER_STAT(iface) & BUSBUSY) + yield(); + + iface->pmsg = msgs; + iface->msg_num = num; + iface->cur_msg = 0; + + pmsg = &msgs[0]; + if (pmsg->flags & I2C_M_TEN) { + dev_err(&adap->dev, "10 bits addr not supported!\n"); + return -EINVAL; + } + + iface->cur_mode = TWI_I2C_MODE_REPEAT; + iface->manual_stop = 0; + iface->transPtr = pmsg->buf; + iface->writeNum = iface->readNum = pmsg->len; + iface->result = 0; + init_completion(&(iface->complete)); + /* Set Transmit device address */ + write_MASTER_ADDR(iface, pmsg->addr); + + /* FIFO Initiation. Data in FIFO should be + * discarded before start a new operation. + */ + write_FIFO_CTL(iface, 0x3); + SSYNC(); + write_FIFO_CTL(iface, 0); + SSYNC(); + + if (pmsg->flags & I2C_M_RD) + iface->read_write = I2C_SMBUS_READ; + else { + iface->read_write = I2C_SMBUS_WRITE; + /* Transmit first data */ + if (iface->writeNum > 0) { + write_XMT_DATA8(iface, *(iface->transPtr++)); + iface->writeNum--; + SSYNC(); + } + } + + /* clear int stat */ + write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV); + + /* Interrupt mask . Enable XMT, RCV interrupt */ + write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV); + SSYNC(); + + if (pmsg->len <= 255) + write_MASTER_CTL(iface, pmsg->len << 6); + else { + write_MASTER_CTL(iface, 0xff << 6); + iface->manual_stop = 1; + } + + /* Master enable */ + write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | + ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | + ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0)); + SSYNC(); + + while (!iface->result) { + if (!wait_for_completion_timeout(&iface->complete, + adap->timeout)) { + iface->result = -1; + dev_err(&adap->dev, "master transfer timeout\n"); + } + } + + if (iface->result == 1) + rc = iface->cur_msg + 1; + else + rc = iface->result; + + return rc; +} + +/* + * Generic i2c master transfer entrypoint + */ +static int bfin_twi_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + return bfin_twi_do_master_xfer(adap, msgs, num); +} + +/* + * One I2C SMBus transfer + */ +int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct bfin_twi_iface *iface = adap->algo_data; + int rc = 0; + + if (!(read_CONTROL(iface) & TWI_ENA)) + return -ENXIO; + + while (read_MASTER_STAT(iface) & BUSBUSY) + yield(); + + iface->writeNum = 0; + iface->readNum = 0; + + /* Prepare datas & select mode */ + switch (size) { + case I2C_SMBUS_QUICK: + iface->transPtr = NULL; + iface->cur_mode = TWI_I2C_MODE_STANDARD; + break; + case I2C_SMBUS_BYTE: + if (data == NULL) + iface->transPtr = NULL; + else { + if (read_write == I2C_SMBUS_READ) + iface->readNum = 1; + else + iface->writeNum = 1; + iface->transPtr = &data->byte; + } + iface->cur_mode = TWI_I2C_MODE_STANDARD; + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = 1; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = 1; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = &data->byte; + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = 2; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = 2; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = (u8 *)&data->word; + break; + case I2C_SMBUS_PROC_CALL: + iface->writeNum = 2; + iface->readNum = 2; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + iface->transPtr = (u8 *)&data->word; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = 0; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = data->block[0] + 1; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = data->block; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + iface->readNum = data->block[0]; + iface->cur_mode = TWI_I2C_MODE_COMBINED; + } else { + iface->writeNum = data->block[0]; + iface->cur_mode = TWI_I2C_MODE_STANDARDSUB; + } + iface->transPtr = (u8 *)&data->block[1]; + break; + default: + return -1; + } + + iface->result = 0; + iface->manual_stop = 0; + iface->read_write = read_write; + iface->command = command; + init_completion(&(iface->complete)); + + /* FIFO Initiation. Data in FIFO should be discarded before + * start a new operation. + */ + write_FIFO_CTL(iface, 0x3); + SSYNC(); + write_FIFO_CTL(iface, 0); + + /* clear int stat */ + write_INT_STAT(iface, MERR | MCOMP | XMTSERV | RCVSERV); + + /* Set Transmit device address */ + write_MASTER_ADDR(iface, addr); + SSYNC(); + + switch (iface->cur_mode) { + case TWI_I2C_MODE_STANDARDSUB: + write_XMT_DATA8(iface, iface->command); + write_INT_MASK(iface, MCOMP | MERR | + ((iface->read_write == I2C_SMBUS_READ) ? + RCVSERV : XMTSERV)); + SSYNC(); + + if (iface->writeNum + 1 <= 255) + write_MASTER_CTL(iface, (iface->writeNum + 1) << 6); + else { + write_MASTER_CTL(iface, 0xff << 6); + iface->manual_stop = 1; + } + /* Master enable */ + write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | + ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0)); + break; + case TWI_I2C_MODE_COMBINED: + write_XMT_DATA8(iface, iface->command); + write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV); + SSYNC(); + + if (iface->writeNum > 0) + write_MASTER_CTL(iface, (iface->writeNum + 1) << 6); + else + write_MASTER_CTL(iface, 0x1 << 6); + /* Master enable */ + write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | + ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0)); + break; + default: + write_MASTER_CTL(iface, 0); + if (size != I2C_SMBUS_QUICK) { + /* Don't access xmit data register when this is a + * read operation. + */ + if (iface->read_write != I2C_SMBUS_READ) { + if (iface->writeNum > 0) { + write_XMT_DATA8(iface, + *(iface->transPtr++)); + if (iface->writeNum <= 255) + write_MASTER_CTL(iface, + iface->writeNum << 6); + else { + write_MASTER_CTL(iface, + 0xff << 6); + iface->manual_stop = 1; + } + iface->writeNum--; + } else { + write_XMT_DATA8(iface, iface->command); + write_MASTER_CTL(iface, 1 << 6); + } + } else { + if (iface->readNum > 0 && iface->readNum <= 255) + write_MASTER_CTL(iface, + iface->readNum << 6); + else if (iface->readNum > 255) { + write_MASTER_CTL(iface, 0xff << 6); + iface->manual_stop = 1; + } else + break; + } + } + write_INT_MASK(iface, MCOMP | MERR | + ((iface->read_write == I2C_SMBUS_READ) ? + RCVSERV : XMTSERV)); + SSYNC(); + + /* Master enable */ + write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN | + ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | + ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0)); + break; + } + SSYNC(); + + while (!iface->result) { + if (!wait_for_completion_timeout(&iface->complete, + adap->timeout)) { + iface->result = -1; + dev_err(&adap->dev, "smbus transfer timeout\n"); + } + } + + rc = (iface->result >= 0) ? 0 : -1; + + return rc; +} + +/* + * Generic I2C SMBus transfer entrypoint + */ +int bfin_twi_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + return bfin_twi_do_smbus_xfer(adap, addr, flags, + read_write, command, size, data); +} + +/* + * Return what the adapter supports + */ +static u32 bfin_twi_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static struct i2c_algorithm bfin_twi_algorithm = { + .master_xfer = bfin_twi_master_xfer, + .smbus_xfer = bfin_twi_smbus_xfer, + .functionality = bfin_twi_functionality, +}; + +static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct bfin_twi_iface *iface = platform_get_drvdata(pdev); + + iface->saved_clkdiv = read_CLKDIV(iface); + iface->saved_control = read_CONTROL(iface); + + free_irq(iface->irq, iface); + + /* Disable TWI */ + write_CONTROL(iface, iface->saved_control & ~TWI_ENA); + + return 0; +} + +static int i2c_bfin_twi_resume(struct platform_device *pdev) +{ + struct bfin_twi_iface *iface = platform_get_drvdata(pdev); + + int rc = request_irq(iface->irq, bfin_twi_interrupt_entry, + IRQF_DISABLED, pdev->name, iface); + if (rc) { + dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); + return -ENODEV; + } + + /* Resume TWI interface clock as specified */ + write_CLKDIV(iface, iface->saved_clkdiv); + + /* Resume TWI */ + write_CONTROL(iface, iface->saved_control); + + return 0; +} + +static int i2c_bfin_twi_probe(struct platform_device *pdev) +{ + struct bfin_twi_iface *iface; + struct i2c_adapter *p_adap; + struct resource *res; + int rc; + unsigned int clkhilow; + + iface = kzalloc(sizeof(struct bfin_twi_iface), GFP_KERNEL); + if (!iface) { + dev_err(&pdev->dev, "Cannot allocate memory\n"); + rc = -ENOMEM; + goto out_error_nomem; + } + + spin_lock_init(&(iface->lock)); + + /* Find and map our resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + rc = -ENOENT; + goto out_error_get_res; + } + + iface->regs_base = ioremap(res->start, resource_size(res)); + if (iface->regs_base == NULL) { + dev_err(&pdev->dev, "Cannot map IO\n"); + rc = -ENXIO; + goto out_error_ioremap; + } + + iface->irq = platform_get_irq(pdev, 0); + if (iface->irq < 0) { + dev_err(&pdev->dev, "No IRQ specified\n"); + rc = -ENOENT; + goto out_error_no_irq; + } + + p_adap = &iface->adap; + p_adap->nr = pdev->id; + strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name)); + p_adap->algo = &bfin_twi_algorithm; + p_adap->algo_data = iface; + p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + p_adap->dev.parent = &pdev->dev; + p_adap->timeout = 5 * HZ; + p_adap->retries = 3; + + rc = peripheral_request_list(pin_req[pdev->id], "i2c-bfin-twi"); + if (rc) { + dev_err(&pdev->dev, "Can't setup pin mux!\n"); + goto out_error_pin_mux; + } + + rc = request_irq(iface->irq, bfin_twi_interrupt_entry, + IRQF_DISABLED, pdev->name, iface); + if (rc) { + dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); + rc = -ENODEV; + goto out_error_req_irq; + } + + /* Set TWI internal clock as 10MHz */ + write_CONTROL(iface, ((get_sclk() / 1000 / 1000 + 5) / 10) & 0x7F); + + /* + * We will not end up with a CLKDIV=0 because no one will specify + * 20kHz SCL or less in Kconfig now. (5 * 1000 / 20 = 250) + */ + clkhilow = ((10 * 1000 / CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ) + 1) / 2; + + /* Set Twi interface clock as specified */ + write_CLKDIV(iface, (clkhilow << 8) | clkhilow); + + /* Enable TWI */ + write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA); + SSYNC(); + + rc = i2c_add_numbered_adapter(p_adap); + if (rc < 0) { + dev_err(&pdev->dev, "Can't add i2c adapter!\n"); + goto out_error_add_adapter; + } + + platform_set_drvdata(pdev, iface); + + dev_info(&pdev->dev, "Blackfin BF5xx on-chip I2C TWI Contoller, " + "regs_base@%p\n", iface->regs_base); + + return 0; + +out_error_add_adapter: + free_irq(iface->irq, iface); +out_error_req_irq: +out_error_no_irq: + peripheral_free_list(pin_req[pdev->id]); +out_error_pin_mux: + iounmap(iface->regs_base); +out_error_ioremap: +out_error_get_res: + kfree(iface); +out_error_nomem: + return rc; +} + +static int i2c_bfin_twi_remove(struct platform_device *pdev) +{ + struct bfin_twi_iface *iface = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + i2c_del_adapter(&(iface->adap)); + free_irq(iface->irq, iface); + peripheral_free_list(pin_req[pdev->id]); + iounmap(iface->regs_base); + kfree(iface); + + return 0; +} + +static struct platform_driver i2c_bfin_twi_driver = { + .probe = i2c_bfin_twi_probe, + .remove = i2c_bfin_twi_remove, + .suspend = i2c_bfin_twi_suspend, + .resume = i2c_bfin_twi_resume, + .driver = { + .name = "i2c-bfin-twi", + .owner = THIS_MODULE, + }, +}; + +static int __init i2c_bfin_twi_init(void) +{ + return platform_driver_register(&i2c_bfin_twi_driver); +} + +static void __exit i2c_bfin_twi_exit(void) +{ + platform_driver_unregister(&i2c_bfin_twi_driver); +} + +subsys_initcall(i2c_bfin_twi_init); +module_exit(i2c_bfin_twi_exit); + +MODULE_AUTHOR("Bryan Wu, Sonic Zhang"); +MODULE_DESCRIPTION("Blackfin BF5xx on-chip I2C TWI Contoller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-bfin-twi"); diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c new file mode 100644 index 00000000..3a20961b --- /dev/null +++ b/drivers/i2c/busses/i2c-cpm.c @@ -0,0 +1,745 @@ +/* + * Freescale CPM1/CPM2 I2C interface. + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net). + * + * moved into proper i2c interface; + * Brad Parker (brad@heeltoe.com) + * + * Parts from dbox2_i2c.c (cvs.tuxbox.org) + * (C) 2000-2001 Felix Domke (tmbinc@gmx.net), Gillem (htoa@gmx.net) + * + * (C) 2007 Montavista Software, Inc. + * Vitaly Bordug <vitb@kernel.crashing.org> + * + * Converted to of_platform_device. Renamed to i2c-cpm.c. + * (C) 2007,2008 Jochen Friedrich <jochen@scram.de> + * + * 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/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/stddef.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_i2c.h> +#include <sysdev/fsl_soc.h> +#include <asm/cpm.h> + +/* Try to define this if you have an older CPU (earlier than rev D4) */ +/* However, better use a GPIO based bitbang driver in this case :/ */ +#undef I2C_CHIP_ERRATA + +#define CPM_MAX_READ 513 +#define CPM_MAXBD 4 + +#define I2C_EB (0x10) /* Big endian mode */ +#define I2C_EB_CPM2 (0x30) /* Big endian mode, memory snoop */ + +#define DPRAM_BASE ((u8 __iomem __force *)cpm_muram_addr(0)) + +/* I2C parameter RAM. */ +struct i2c_ram { + ushort rbase; /* Rx Buffer descriptor base address */ + ushort tbase; /* Tx Buffer descriptor base address */ + u_char rfcr; /* Rx function code */ + u_char tfcr; /* Tx function code */ + ushort mrblr; /* Max receive buffer length */ + uint rstate; /* Internal */ + uint rdp; /* Internal */ + ushort rbptr; /* Rx Buffer descriptor pointer */ + ushort rbc; /* Internal */ + uint rxtmp; /* Internal */ + uint tstate; /* Internal */ + uint tdp; /* Internal */ + ushort tbptr; /* Tx Buffer descriptor pointer */ + ushort tbc; /* Internal */ + uint txtmp; /* Internal */ + char res1[4]; /* Reserved */ + ushort rpbase; /* Relocation pointer */ + char res2[2]; /* Reserved */ +}; + +#define I2COM_START 0x80 +#define I2COM_MASTER 0x01 +#define I2CER_TXE 0x10 +#define I2CER_BUSY 0x04 +#define I2CER_TXB 0x02 +#define I2CER_RXB 0x01 +#define I2MOD_EN 0x01 + +/* I2C Registers */ +struct i2c_reg { + u8 i2mod; + u8 res1[3]; + u8 i2add; + u8 res2[3]; + u8 i2brg; + u8 res3[3]; + u8 i2com; + u8 res4[3]; + u8 i2cer; + u8 res5[3]; + u8 i2cmr; +}; + +struct cpm_i2c { + char *base; + struct platform_device *ofdev; + struct i2c_adapter adap; + uint dp_addr; + int version; /* CPM1=1, CPM2=2 */ + int irq; + int cp_command; + int freq; + struct i2c_reg __iomem *i2c_reg; + struct i2c_ram __iomem *i2c_ram; + u16 i2c_addr; + wait_queue_head_t i2c_wait; + cbd_t __iomem *tbase; + cbd_t __iomem *rbase; + u_char *txbuf[CPM_MAXBD]; + u_char *rxbuf[CPM_MAXBD]; + u32 txdma[CPM_MAXBD]; + u32 rxdma[CPM_MAXBD]; +}; + +static irqreturn_t cpm_i2c_interrupt(int irq, void *dev_id) +{ + struct cpm_i2c *cpm; + struct i2c_reg __iomem *i2c_reg; + struct i2c_adapter *adap = dev_id; + int i; + + cpm = i2c_get_adapdata(dev_id); + i2c_reg = cpm->i2c_reg; + + /* Clear interrupt. */ + i = in_8(&i2c_reg->i2cer); + out_8(&i2c_reg->i2cer, i); + + dev_dbg(&adap->dev, "Interrupt: %x\n", i); + + wake_up(&cpm->i2c_wait); + + return i ? IRQ_HANDLED : IRQ_NONE; +} + +static void cpm_reset_i2c_params(struct cpm_i2c *cpm) +{ + struct i2c_ram __iomem *i2c_ram = cpm->i2c_ram; + + /* Set up the I2C parameters in the parameter ram. */ + out_be16(&i2c_ram->tbase, (u8 __iomem *)cpm->tbase - DPRAM_BASE); + out_be16(&i2c_ram->rbase, (u8 __iomem *)cpm->rbase - DPRAM_BASE); + + if (cpm->version == 1) { + out_8(&i2c_ram->tfcr, I2C_EB); + out_8(&i2c_ram->rfcr, I2C_EB); + } else { + out_8(&i2c_ram->tfcr, I2C_EB_CPM2); + out_8(&i2c_ram->rfcr, I2C_EB_CPM2); + } + + out_be16(&i2c_ram->mrblr, CPM_MAX_READ); + + out_be32(&i2c_ram->rstate, 0); + out_be32(&i2c_ram->rdp, 0); + out_be16(&i2c_ram->rbptr, 0); + out_be16(&i2c_ram->rbc, 0); + out_be32(&i2c_ram->rxtmp, 0); + out_be32(&i2c_ram->tstate, 0); + out_be32(&i2c_ram->tdp, 0); + out_be16(&i2c_ram->tbptr, 0); + out_be16(&i2c_ram->tbc, 0); + out_be32(&i2c_ram->txtmp, 0); +} + +static void cpm_i2c_force_close(struct i2c_adapter *adap) +{ + struct cpm_i2c *cpm = i2c_get_adapdata(adap); + struct i2c_reg __iomem *i2c_reg = cpm->i2c_reg; + + dev_dbg(&adap->dev, "cpm_i2c_force_close()\n"); + + cpm_command(cpm->cp_command, CPM_CR_CLOSE_RX_BD); + + out_8(&i2c_reg->i2cmr, 0x00); /* Disable all interrupts */ + out_8(&i2c_reg->i2cer, 0xff); +} + +static void cpm_i2c_parse_message(struct i2c_adapter *adap, + struct i2c_msg *pmsg, int num, int tx, int rx) +{ + cbd_t __iomem *tbdf; + cbd_t __iomem *rbdf; + u_char addr; + u_char *tb; + u_char *rb; + struct cpm_i2c *cpm = i2c_get_adapdata(adap); + + tbdf = cpm->tbase + tx; + rbdf = cpm->rbase + rx; + + addr = pmsg->addr << 1; + if (pmsg->flags & I2C_M_RD) + addr |= 1; + + tb = cpm->txbuf[tx]; + rb = cpm->rxbuf[rx]; + + /* Align read buffer */ + rb = (u_char *) (((ulong) rb + 1) & ~1); + + tb[0] = addr; /* Device address byte w/rw flag */ + + out_be16(&tbdf->cbd_datlen, pmsg->len + 1); + out_be16(&tbdf->cbd_sc, 0); + + if (!(pmsg->flags & I2C_M_NOSTART)) + setbits16(&tbdf->cbd_sc, BD_I2C_START); + + if (tx + 1 == num) + setbits16(&tbdf->cbd_sc, BD_SC_LAST | BD_SC_WRAP); + + if (pmsg->flags & I2C_M_RD) { + /* + * To read, we need an empty buffer of the proper length. + * All that is used is the first byte for address, the remainder + * is just used for timing (and doesn't really have to exist). + */ + + dev_dbg(&adap->dev, "cpm_i2c_read(abyte=0x%x)\n", addr); + + out_be16(&rbdf->cbd_datlen, 0); + out_be16(&rbdf->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT); + + if (rx + 1 == CPM_MAXBD) + setbits16(&rbdf->cbd_sc, BD_SC_WRAP); + + eieio(); + setbits16(&tbdf->cbd_sc, BD_SC_READY); + } else { + dev_dbg(&adap->dev, "cpm_i2c_write(abyte=0x%x)\n", addr); + + memcpy(tb+1, pmsg->buf, pmsg->len); + + eieio(); + setbits16(&tbdf->cbd_sc, BD_SC_READY | BD_SC_INTRPT); + } +} + +static int cpm_i2c_check_message(struct i2c_adapter *adap, + struct i2c_msg *pmsg, int tx, int rx) +{ + cbd_t __iomem *tbdf; + cbd_t __iomem *rbdf; + u_char *tb; + u_char *rb; + struct cpm_i2c *cpm = i2c_get_adapdata(adap); + + tbdf = cpm->tbase + tx; + rbdf = cpm->rbase + rx; + + tb = cpm->txbuf[tx]; + rb = cpm->rxbuf[rx]; + + /* Align read buffer */ + rb = (u_char *) (((uint) rb + 1) & ~1); + + eieio(); + if (pmsg->flags & I2C_M_RD) { + dev_dbg(&adap->dev, "tx sc 0x%04x, rx sc 0x%04x\n", + in_be16(&tbdf->cbd_sc), in_be16(&rbdf->cbd_sc)); + + if (in_be16(&tbdf->cbd_sc) & BD_SC_NAK) { + dev_dbg(&adap->dev, "I2C read; No ack\n"); + return -ENXIO; + } + if (in_be16(&rbdf->cbd_sc) & BD_SC_EMPTY) { + dev_err(&adap->dev, + "I2C read; complete but rbuf empty\n"); + return -EREMOTEIO; + } + if (in_be16(&rbdf->cbd_sc) & BD_SC_OV) { + dev_err(&adap->dev, "I2C read; Overrun\n"); + return -EREMOTEIO; + } + memcpy(pmsg->buf, rb, pmsg->len); + } else { + dev_dbg(&adap->dev, "tx sc %d 0x%04x\n", tx, + in_be16(&tbdf->cbd_sc)); + + if (in_be16(&tbdf->cbd_sc) & BD_SC_NAK) { + dev_dbg(&adap->dev, "I2C write; No ack\n"); + return -ENXIO; + } + if (in_be16(&tbdf->cbd_sc) & BD_SC_UN) { + dev_err(&adap->dev, "I2C write; Underrun\n"); + return -EIO; + } + if (in_be16(&tbdf->cbd_sc) & BD_SC_CL) { + dev_err(&adap->dev, "I2C write; Collision\n"); + return -EIO; + } + } + return 0; +} + +static int cpm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct cpm_i2c *cpm = i2c_get_adapdata(adap); + struct i2c_reg __iomem *i2c_reg = cpm->i2c_reg; + struct i2c_ram __iomem *i2c_ram = cpm->i2c_ram; + struct i2c_msg *pmsg; + int ret, i; + int tptr; + int rptr; + cbd_t __iomem *tbdf; + cbd_t __iomem *rbdf; + + if (num > CPM_MAXBD) + return -EINVAL; + + /* Check if we have any oversized READ requests */ + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + if (pmsg->len >= CPM_MAX_READ) + return -EINVAL; + } + + /* Reset to use first buffer */ + out_be16(&i2c_ram->rbptr, in_be16(&i2c_ram->rbase)); + out_be16(&i2c_ram->tbptr, in_be16(&i2c_ram->tbase)); + + tbdf = cpm->tbase; + rbdf = cpm->rbase; + + tptr = 0; + rptr = 0; + + while (tptr < num) { + pmsg = &msgs[tptr]; + dev_dbg(&adap->dev, "R: %d T: %d\n", rptr, tptr); + + cpm_i2c_parse_message(adap, pmsg, num, tptr, rptr); + if (pmsg->flags & I2C_M_RD) + rptr++; + tptr++; + } + /* Start transfer now */ + /* Enable RX/TX/Error interupts */ + out_8(&i2c_reg->i2cmr, I2CER_TXE | I2CER_TXB | I2CER_RXB); + out_8(&i2c_reg->i2cer, 0xff); /* Clear interrupt status */ + /* Chip bug, set enable here */ + setbits8(&i2c_reg->i2mod, I2MOD_EN); /* Enable */ + /* Begin transmission */ + setbits8(&i2c_reg->i2com, I2COM_START); + + tptr = 0; + rptr = 0; + + while (tptr < num) { + /* Check for outstanding messages */ + dev_dbg(&adap->dev, "test ready.\n"); + pmsg = &msgs[tptr]; + if (pmsg->flags & I2C_M_RD) + ret = wait_event_timeout(cpm->i2c_wait, + (in_be16(&tbdf[tptr].cbd_sc) & BD_SC_NAK) || + !(in_be16(&rbdf[rptr].cbd_sc) & BD_SC_EMPTY), + 1 * HZ); + else + ret = wait_event_timeout(cpm->i2c_wait, + !(in_be16(&tbdf[tptr].cbd_sc) & BD_SC_READY), + 1 * HZ); + if (ret == 0) { + ret = -EREMOTEIO; + dev_err(&adap->dev, "I2C transfer: timeout\n"); + goto out_err; + } + if (ret > 0) { + dev_dbg(&adap->dev, "ready.\n"); + ret = cpm_i2c_check_message(adap, pmsg, tptr, rptr); + tptr++; + if (pmsg->flags & I2C_M_RD) + rptr++; + if (ret) + goto out_err; + } + } +#ifdef I2C_CHIP_ERRATA + /* + * Chip errata, clear enable. This is not needed on rev D4 CPUs. + * Disabling I2C too early may cause too short stop condition + */ + udelay(4); + clrbits8(&i2c_reg->i2mod, I2MOD_EN); +#endif + return (num); + +out_err: + cpm_i2c_force_close(adap); +#ifdef I2C_CHIP_ERRATA + /* + * Chip errata, clear enable. This is not needed on rev D4 CPUs. + */ + clrbits8(&i2c_reg->i2mod, I2MOD_EN); +#endif + return ret; +} + +static u32 cpm_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +/* -----exported algorithm data: ------------------------------------- */ + +static const struct i2c_algorithm cpm_i2c_algo = { + .master_xfer = cpm_i2c_xfer, + .functionality = cpm_i2c_func, +}; + +static const struct i2c_adapter cpm_ops = { + .owner = THIS_MODULE, + .name = "i2c-cpm", + .algo = &cpm_i2c_algo, +}; + +static int __devinit cpm_i2c_setup(struct cpm_i2c *cpm) +{ + struct platform_device *ofdev = cpm->ofdev; + const u32 *data; + int len, ret, i; + void __iomem *i2c_base; + cbd_t __iomem *tbdf; + cbd_t __iomem *rbdf; + unsigned char brg; + + dev_dbg(&cpm->ofdev->dev, "cpm_i2c_setup()\n"); + + init_waitqueue_head(&cpm->i2c_wait); + + cpm->irq = of_irq_to_resource(ofdev->dev.of_node, 0, NULL); + if (!cpm->irq) + return -EINVAL; + + /* Install interrupt handler. */ + ret = request_irq(cpm->irq, cpm_i2c_interrupt, 0, "cpm_i2c", + &cpm->adap); + if (ret) + return ret; + + /* I2C parameter RAM */ + i2c_base = of_iomap(ofdev->dev.of_node, 1); + if (i2c_base == NULL) { + ret = -EINVAL; + goto out_irq; + } + + if (of_device_is_compatible(ofdev->dev.of_node, "fsl,cpm1-i2c")) { + + /* Check for and use a microcode relocation patch. */ + cpm->i2c_ram = i2c_base; + cpm->i2c_addr = in_be16(&cpm->i2c_ram->rpbase); + + /* + * Maybe should use cpm_muram_alloc instead of hardcoding + * this in micropatch.c + */ + if (cpm->i2c_addr) { + cpm->i2c_ram = cpm_muram_addr(cpm->i2c_addr); + iounmap(i2c_base); + } + + cpm->version = 1; + + } else if (of_device_is_compatible(ofdev->dev.of_node, "fsl,cpm2-i2c")) { + cpm->i2c_addr = cpm_muram_alloc(sizeof(struct i2c_ram), 64); + cpm->i2c_ram = cpm_muram_addr(cpm->i2c_addr); + out_be16(i2c_base, cpm->i2c_addr); + iounmap(i2c_base); + + cpm->version = 2; + + } else { + iounmap(i2c_base); + ret = -EINVAL; + goto out_irq; + } + + /* I2C control/status registers */ + cpm->i2c_reg = of_iomap(ofdev->dev.of_node, 0); + if (cpm->i2c_reg == NULL) { + ret = -EINVAL; + goto out_ram; + } + + data = of_get_property(ofdev->dev.of_node, "fsl,cpm-command", &len); + if (!data || len != 4) { + ret = -EINVAL; + goto out_reg; + } + cpm->cp_command = *data; + + data = of_get_property(ofdev->dev.of_node, "linux,i2c-class", &len); + if (data && len == 4) + cpm->adap.class = *data; + + data = of_get_property(ofdev->dev.of_node, "clock-frequency", &len); + if (data && len == 4) + cpm->freq = *data; + else + cpm->freq = 60000; /* use 60kHz i2c clock by default */ + + /* + * Allocate space for CPM_MAXBD transmit and receive buffer + * descriptors in the DP ram. + */ + cpm->dp_addr = cpm_muram_alloc(sizeof(cbd_t) * 2 * CPM_MAXBD, 8); + if (!cpm->dp_addr) { + ret = -ENOMEM; + goto out_reg; + } + + cpm->tbase = cpm_muram_addr(cpm->dp_addr); + cpm->rbase = cpm_muram_addr(cpm->dp_addr + sizeof(cbd_t) * CPM_MAXBD); + + /* Allocate TX and RX buffers */ + + tbdf = cpm->tbase; + rbdf = cpm->rbase; + + for (i = 0; i < CPM_MAXBD; i++) { + cpm->rxbuf[i] = dma_alloc_coherent(&cpm->ofdev->dev, + CPM_MAX_READ + 1, + &cpm->rxdma[i], GFP_KERNEL); + if (!cpm->rxbuf[i]) { + ret = -ENOMEM; + goto out_muram; + } + out_be32(&rbdf[i].cbd_bufaddr, ((cpm->rxdma[i] + 1) & ~1)); + + cpm->txbuf[i] = (unsigned char *)dma_alloc_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, &cpm->txdma[i], GFP_KERNEL); + if (!cpm->txbuf[i]) { + ret = -ENOMEM; + goto out_muram; + } + out_be32(&tbdf[i].cbd_bufaddr, cpm->txdma[i]); + } + + /* Initialize Tx/Rx parameters. */ + + cpm_reset_i2c_params(cpm); + + dev_dbg(&cpm->ofdev->dev, "i2c_ram 0x%p, i2c_addr 0x%04x, freq %d\n", + cpm->i2c_ram, cpm->i2c_addr, cpm->freq); + dev_dbg(&cpm->ofdev->dev, "tbase 0x%04x, rbase 0x%04x\n", + (u8 __iomem *)cpm->tbase - DPRAM_BASE, + (u8 __iomem *)cpm->rbase - DPRAM_BASE); + + cpm_command(cpm->cp_command, CPM_CR_INIT_TRX); + + /* + * Select an invalid address. Just make sure we don't use loopback mode + */ + out_8(&cpm->i2c_reg->i2add, 0x7f << 1); + + /* + * PDIV is set to 00 in i2mod, so brgclk/32 is used as input to the + * i2c baud rate generator. This is divided by 2 x (DIV + 3) to get + * the actual i2c bus frequency. + */ + brg = get_brgfreq() / (32 * 2 * cpm->freq) - 3; + out_8(&cpm->i2c_reg->i2brg, brg); + + out_8(&cpm->i2c_reg->i2mod, 0x00); + out_8(&cpm->i2c_reg->i2com, I2COM_MASTER); /* Master mode */ + + /* Disable interrupts. */ + out_8(&cpm->i2c_reg->i2cmr, 0); + out_8(&cpm->i2c_reg->i2cer, 0xff); + + return 0; + +out_muram: + for (i = 0; i < CPM_MAXBD; i++) { + if (cpm->rxbuf[i]) + dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, + cpm->rxbuf[i], cpm->rxdma[i]); + if (cpm->txbuf[i]) + dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, + cpm->txbuf[i], cpm->txdma[i]); + } + cpm_muram_free(cpm->dp_addr); +out_reg: + iounmap(cpm->i2c_reg); +out_ram: + if ((cpm->version == 1) && (!cpm->i2c_addr)) + iounmap(cpm->i2c_ram); + if (cpm->version == 2) + cpm_muram_free(cpm->i2c_addr); +out_irq: + free_irq(cpm->irq, &cpm->adap); + return ret; +} + +static void cpm_i2c_shutdown(struct cpm_i2c *cpm) +{ + int i; + + /* Shut down I2C. */ + clrbits8(&cpm->i2c_reg->i2mod, I2MOD_EN); + + /* Disable interrupts */ + out_8(&cpm->i2c_reg->i2cmr, 0); + out_8(&cpm->i2c_reg->i2cer, 0xff); + + free_irq(cpm->irq, &cpm->adap); + + /* Free all memory */ + for (i = 0; i < CPM_MAXBD; i++) { + dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, + cpm->rxbuf[i], cpm->rxdma[i]); + dma_free_coherent(&cpm->ofdev->dev, CPM_MAX_READ + 1, + cpm->txbuf[i], cpm->txdma[i]); + } + + cpm_muram_free(cpm->dp_addr); + iounmap(cpm->i2c_reg); + + if ((cpm->version == 1) && (!cpm->i2c_addr)) + iounmap(cpm->i2c_ram); + if (cpm->version == 2) + cpm_muram_free(cpm->i2c_addr); +} + +static int __devinit cpm_i2c_probe(struct platform_device *ofdev) +{ + int result, len; + struct cpm_i2c *cpm; + const u32 *data; + + cpm = kzalloc(sizeof(struct cpm_i2c), GFP_KERNEL); + if (!cpm) + return -ENOMEM; + + cpm->ofdev = ofdev; + + dev_set_drvdata(&ofdev->dev, cpm); + + cpm->adap = cpm_ops; + i2c_set_adapdata(&cpm->adap, cpm); + cpm->adap.dev.parent = &ofdev->dev; + cpm->adap.dev.of_node = of_node_get(ofdev->dev.of_node); + + result = cpm_i2c_setup(cpm); + if (result) { + dev_err(&ofdev->dev, "Unable to init hardware\n"); + goto out_free; + } + + /* register new adapter to i2c module... */ + + data = of_get_property(ofdev->dev.of_node, "linux,i2c-index", &len); + if (data && len == 4) { + cpm->adap.nr = *data; + result = i2c_add_numbered_adapter(&cpm->adap); + } else + result = i2c_add_adapter(&cpm->adap); + + if (result < 0) { + dev_err(&ofdev->dev, "Unable to register with I2C\n"); + goto out_shut; + } + + dev_dbg(&ofdev->dev, "hw routines for %s registered.\n", + cpm->adap.name); + + /* + * register OF I2C devices + */ + of_i2c_register_devices(&cpm->adap); + + return 0; +out_shut: + cpm_i2c_shutdown(cpm); +out_free: + dev_set_drvdata(&ofdev->dev, NULL); + kfree(cpm); + + return result; +} + +static int __devexit cpm_i2c_remove(struct platform_device *ofdev) +{ + struct cpm_i2c *cpm = dev_get_drvdata(&ofdev->dev); + + i2c_del_adapter(&cpm->adap); + + cpm_i2c_shutdown(cpm); + + dev_set_drvdata(&ofdev->dev, NULL); + kfree(cpm); + + return 0; +} + +static const struct of_device_id cpm_i2c_match[] = { + { + .compatible = "fsl,cpm1-i2c", + }, + { + .compatible = "fsl,cpm2-i2c", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, cpm_i2c_match); + +static struct platform_driver cpm_i2c_driver = { + .probe = cpm_i2c_probe, + .remove = __devexit_p(cpm_i2c_remove), + .driver = { + .name = "fsl-i2c-cpm", + .owner = THIS_MODULE, + .of_match_table = cpm_i2c_match, + }, +}; + +static int __init cpm_i2c_init(void) +{ + return platform_driver_register(&cpm_i2c_driver); +} + +static void __exit cpm_i2c_exit(void) +{ + platform_driver_unregister(&cpm_i2c_driver); +} + +module_init(cpm_i2c_init); +module_exit(cpm_i2c_exit); + +MODULE_AUTHOR("Jochen Friedrich <jochen@scram.de>"); +MODULE_DESCRIPTION("I2C-Bus adapter routines for CPM boards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c new file mode 100644 index 00000000..79b4bcb3 --- /dev/null +++ b/drivers/i2c/busses/i2c-davinci.c @@ -0,0 +1,830 @@ +/* + * TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * + * Updated by Vinod & Sudhakar Feb 2005 + * + * ---------------------------------------------------------------------------- + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/cpufreq.h> +#include <linux/gpio.h> + +#include <mach/hardware.h> +#include <mach/i2c.h> + +/* ----- global defines ----------------------------------------------- */ + +#define DAVINCI_I2C_TIMEOUT (1*HZ) +#define DAVINCI_I2C_MAX_TRIES 2 +#define I2C_DAVINCI_INTR_ALL (DAVINCI_I2C_IMR_AAS | \ + DAVINCI_I2C_IMR_SCD | \ + DAVINCI_I2C_IMR_ARDY | \ + DAVINCI_I2C_IMR_NACK | \ + DAVINCI_I2C_IMR_AL) + +#define DAVINCI_I2C_OAR_REG 0x00 +#define DAVINCI_I2C_IMR_REG 0x04 +#define DAVINCI_I2C_STR_REG 0x08 +#define DAVINCI_I2C_CLKL_REG 0x0c +#define DAVINCI_I2C_CLKH_REG 0x10 +#define DAVINCI_I2C_CNT_REG 0x14 +#define DAVINCI_I2C_DRR_REG 0x18 +#define DAVINCI_I2C_SAR_REG 0x1c +#define DAVINCI_I2C_DXR_REG 0x20 +#define DAVINCI_I2C_MDR_REG 0x24 +#define DAVINCI_I2C_IVR_REG 0x28 +#define DAVINCI_I2C_EMDR_REG 0x2c +#define DAVINCI_I2C_PSC_REG 0x30 + +#define DAVINCI_I2C_IVR_AAS 0x07 +#define DAVINCI_I2C_IVR_SCD 0x06 +#define DAVINCI_I2C_IVR_XRDY 0x05 +#define DAVINCI_I2C_IVR_RDR 0x04 +#define DAVINCI_I2C_IVR_ARDY 0x03 +#define DAVINCI_I2C_IVR_NACK 0x02 +#define DAVINCI_I2C_IVR_AL 0x01 + +#define DAVINCI_I2C_STR_BB BIT(12) +#define DAVINCI_I2C_STR_RSFULL BIT(11) +#define DAVINCI_I2C_STR_SCD BIT(5) +#define DAVINCI_I2C_STR_ARDY BIT(2) +#define DAVINCI_I2C_STR_NACK BIT(1) +#define DAVINCI_I2C_STR_AL BIT(0) + +#define DAVINCI_I2C_MDR_NACK BIT(15) +#define DAVINCI_I2C_MDR_STT BIT(13) +#define DAVINCI_I2C_MDR_STP BIT(11) +#define DAVINCI_I2C_MDR_MST BIT(10) +#define DAVINCI_I2C_MDR_TRX BIT(9) +#define DAVINCI_I2C_MDR_XA BIT(8) +#define DAVINCI_I2C_MDR_RM BIT(7) +#define DAVINCI_I2C_MDR_IRS BIT(5) + +#define DAVINCI_I2C_IMR_AAS BIT(6) +#define DAVINCI_I2C_IMR_SCD BIT(5) +#define DAVINCI_I2C_IMR_XRDY BIT(4) +#define DAVINCI_I2C_IMR_RRDY BIT(3) +#define DAVINCI_I2C_IMR_ARDY BIT(2) +#define DAVINCI_I2C_IMR_NACK BIT(1) +#define DAVINCI_I2C_IMR_AL BIT(0) + +struct davinci_i2c_dev { + struct device *dev; + void __iomem *base; + struct completion cmd_complete; + struct clk *clk; + int cmd_err; + u8 *buf; + size_t buf_len; + int irq; + int stop; + u8 terminate; + struct i2c_adapter adapter; +#ifdef CONFIG_CPU_FREQ + struct completion xfr_complete; + struct notifier_block freq_transition; +#endif +}; + +/* default platform data to use if not supplied in the platform_device */ +static struct davinci_i2c_platform_data davinci_i2c_platform_data_default = { + .bus_freq = 100, + .bus_delay = 0, +}; + +static inline void davinci_i2c_write_reg(struct davinci_i2c_dev *i2c_dev, + int reg, u16 val) +{ + __raw_writew(val, i2c_dev->base + reg); +} + +static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2c_dev, int reg) +{ + return __raw_readw(i2c_dev->base + reg); +} + +/* Generate a pulse on the i2c clock pin. */ +static void generic_i2c_clock_pulse(unsigned int scl_pin) +{ + u16 i; + + if (scl_pin) { + /* Send high and low on the SCL line */ + for (i = 0; i < 9; i++) { + gpio_set_value(scl_pin, 0); + udelay(20); + gpio_set_value(scl_pin, 1); + udelay(20); + } + } +} + +/* This routine does i2c bus recovery as specified in the + * i2c protocol Rev. 03 section 3.16 titled "Bus clear" + */ +static void i2c_recover_bus(struct davinci_i2c_dev *dev) +{ + u32 flag = 0; + struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; + + dev_err(dev->dev, "initiating i2c bus recovery\n"); + /* Send NACK to the slave */ + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + flag |= DAVINCI_I2C_MDR_NACK; + /* write the data into mode register */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); + if (pdata) + generic_i2c_clock_pulse(pdata->scl_pin); + /* Send STOP */ + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + flag |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); +} + +static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev, + int val) +{ + u16 w; + + w = davinci_i2c_read_reg(i2c_dev, DAVINCI_I2C_MDR_REG); + if (!val) /* put I2C into reset */ + w &= ~DAVINCI_I2C_MDR_IRS; + else /* take I2C out of reset */ + w |= DAVINCI_I2C_MDR_IRS; + + davinci_i2c_write_reg(i2c_dev, DAVINCI_I2C_MDR_REG, w); +} + +static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev) +{ + struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; + u16 psc; + u32 clk; + u32 d; + u32 clkh; + u32 clkl; + u32 input_clock = clk_get_rate(dev->clk); + + /* NOTE: I2C Clock divider programming info + * As per I2C specs the following formulas provide prescaler + * and low/high divider values + * input clk --> PSC Div -----------> ICCL/H Div --> output clock + * module clk + * + * output clk = module clk / (PSC + 1) [ (ICCL + d) + (ICCH + d) ] + * + * Thus, + * (ICCL + ICCH) = clk = (input clk / ((psc +1) * output clk)) - 2d; + * + * where if PSC == 0, d = 7, + * if PSC == 1, d = 6 + * if PSC > 1 , d = 5 + */ + + /* get minimum of 7 MHz clock, but max of 12 MHz */ + psc = (input_clock / 7000000) - 1; + if ((input_clock / (psc + 1)) > 12000000) + psc++; /* better to run under spec than over */ + d = (psc >= 2) ? 5 : 7 - psc; + + clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000)) - (d << 1); + clkh = clk >> 1; + clkl = clk - clkh; + + davinci_i2c_write_reg(dev, DAVINCI_I2C_PSC_REG, psc); + davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKH_REG, clkh); + davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKL_REG, clkl); + + dev_dbg(dev->dev, "input_clock = %d, CLK = %d\n", input_clock, clk); +} + +/* + * This function configures I2C and brings I2C out of reset. + * This function is called during I2C init function. This function + * also gets called if I2C encounters any errors. + */ +static int i2c_davinci_init(struct davinci_i2c_dev *dev) +{ + struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; + + if (!pdata) + pdata = &davinci_i2c_platform_data_default; + + /* put I2C into reset */ + davinci_i2c_reset_ctrl(dev, 0); + + /* compute clock dividers */ + i2c_davinci_calc_clk_dividers(dev); + + /* Respond at reserved "SMBus Host" slave address" (and zero); + * we seem to have no option to not respond... + */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_OAR_REG, 0x08); + + dev_dbg(dev->dev, "PSC = %d\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG)); + dev_dbg(dev->dev, "CLKL = %d\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKL_REG)); + dev_dbg(dev->dev, "CLKH = %d\n", + davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKH_REG)); + dev_dbg(dev->dev, "bus_freq = %dkHz, bus_delay = %d\n", + pdata->bus_freq, pdata->bus_delay); + + /* Take the I2C module out of reset: */ + davinci_i2c_reset_ctrl(dev, 1); + + /* Enable interrupts */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, I2C_DAVINCI_INTR_ALL); + + return 0; +} + +/* + * Waiting for bus not busy + */ +static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev, + char allow_sleep) +{ + unsigned long timeout; + static u16 to_cnt; + + timeout = jiffies + dev->adapter.timeout; + while (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) + & DAVINCI_I2C_STR_BB) { + if (to_cnt <= DAVINCI_I2C_MAX_TRIES) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, + "timeout waiting for bus ready\n"); + to_cnt++; + return -ETIMEDOUT; + } else { + to_cnt = 0; + i2c_recover_bus(dev); + i2c_davinci_init(dev); + } + } + if (allow_sleep) + schedule_timeout(1); + } + + return 0; +} + +/* + * Low level master read/write transaction. This function is called + * from i2c_davinci_xfer. + */ +static int +i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; + u32 flag; + u16 w; + int r; + + if (!pdata) + pdata = &davinci_i2c_platform_data_default; + /* Introduce a delay, required for some boards (e.g Davinci EVM) */ + if (pdata->bus_delay) + udelay(pdata->bus_delay); + + /* set the slave address */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_SAR_REG, msg->addr); + + dev->buf = msg->buf; + dev->buf_len = msg->len; + dev->stop = stop; + + davinci_i2c_write_reg(dev, DAVINCI_I2C_CNT_REG, dev->buf_len); + + INIT_COMPLETION(dev->cmd_complete); + dev->cmd_err = 0; + + /* Take I2C out of reset and configure it as master */ + flag = DAVINCI_I2C_MDR_IRS | DAVINCI_I2C_MDR_MST; + + /* if the slave address is ten bit address, enable XA bit */ + if (msg->flags & I2C_M_TEN) + flag |= DAVINCI_I2C_MDR_XA; + if (!(msg->flags & I2C_M_RD)) + flag |= DAVINCI_I2C_MDR_TRX; + if (msg->len == 0) + flag |= DAVINCI_I2C_MDR_RM; + + /* Enable receive or transmit interrupts */ + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG); + if (msg->flags & I2C_M_RD) + w |= DAVINCI_I2C_IMR_RRDY; + else + w |= DAVINCI_I2C_IMR_XRDY; + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w); + + dev->terminate = 0; + + /* + * Write mode register first as needed for correct behaviour + * on OMAP-L138, but don't set STT yet to avoid a race with XRDY + * occurring before we have loaded DXR + */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); + + /* + * First byte should be set here, not after interrupt, + * because transmit-data-ready interrupt can come before + * NACK-interrupt during sending of previous message and + * ICDXR may have wrong data + * It also saves us one interrupt, slightly faster + */ + if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) { + davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, *dev->buf++); + dev->buf_len--; + } + + /* Set STT to begin transmit now DXR is loaded */ + flag |= DAVINCI_I2C_MDR_STT; + if (stop && msg->len != 0) + flag |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); + + r = wait_for_completion_interruptible_timeout(&dev->cmd_complete, + dev->adapter.timeout); + if (r == 0) { + dev_err(dev->dev, "controller timed out\n"); + i2c_recover_bus(dev); + i2c_davinci_init(dev); + dev->buf_len = 0; + return -ETIMEDOUT; + } + if (dev->buf_len) { + /* This should be 0 if all bytes were transferred + * or dev->cmd_err denotes an error. + * A signal may have aborted the transfer. + */ + if (r >= 0) { + dev_err(dev->dev, "abnormal termination buf_len=%i\n", + dev->buf_len); + r = -EREMOTEIO; + } + dev->terminate = 1; + wmb(); + dev->buf_len = 0; + } + if (r < 0) + return r; + + /* no error */ + if (likely(!dev->cmd_err)) + return msg->len; + + /* We have an error */ + if (dev->cmd_err & DAVINCI_I2C_STR_AL) { + i2c_davinci_init(dev); + return -EIO; + } + + if (dev->cmd_err & DAVINCI_I2C_STR_NACK) { + if (msg->flags & I2C_M_IGNORE_NAK) + return msg->len; + if (stop) { + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); + } + return -EREMOTEIO; + } + return -EIO; +} + +/* + * Prepare controller for a transaction and call i2c_davinci_xfer_msg + */ +static int +i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + int i; + int ret; + + dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + + ret = i2c_davinci_wait_bus_not_busy(dev, 1); + if (ret < 0) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return ret; + } + + for (i = 0; i < num; i++) { + ret = i2c_davinci_xfer_msg(adap, &msgs[i], (i == (num - 1))); + dev_dbg(dev->dev, "%s [%d/%d] ret: %d\n", __func__, i + 1, num, + ret); + if (ret < 0) + return ret; + } + +#ifdef CONFIG_CPU_FREQ + complete(&dev->xfr_complete); +#endif + + return num; +} + +static u32 i2c_davinci_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static void terminate_read(struct davinci_i2c_dev *dev) +{ + u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_NACK; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); + + /* Throw away data */ + davinci_i2c_read_reg(dev, DAVINCI_I2C_DRR_REG); + if (!dev->terminate) + dev_err(dev->dev, "RDR IRQ while no data requested\n"); +} +static void terminate_write(struct davinci_i2c_dev *dev) +{ + u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_RM | DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); + + if (!dev->terminate) + dev_dbg(dev->dev, "TDR IRQ while no data to send\n"); +} + +/* + * Interrupt service routine. This gets called whenever an I2C interrupt + * occurs. + */ +static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id) +{ + struct davinci_i2c_dev *dev = dev_id; + u32 stat; + int count = 0; + u16 w; + + while ((stat = davinci_i2c_read_reg(dev, DAVINCI_I2C_IVR_REG))) { + dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat); + if (count++ == 100) { + dev_warn(dev->dev, "Too much work in one IRQ\n"); + break; + } + + switch (stat) { + case DAVINCI_I2C_IVR_AL: + /* Arbitration lost, must retry */ + dev->cmd_err |= DAVINCI_I2C_STR_AL; + dev->buf_len = 0; + complete(&dev->cmd_complete); + break; + + case DAVINCI_I2C_IVR_NACK: + dev->cmd_err |= DAVINCI_I2C_STR_NACK; + dev->buf_len = 0; + complete(&dev->cmd_complete); + break; + + case DAVINCI_I2C_IVR_ARDY: + davinci_i2c_write_reg(dev, + DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_ARDY); + if (((dev->buf_len == 0) && (dev->stop != 0)) || + (dev->cmd_err & DAVINCI_I2C_STR_NACK)) { + w = davinci_i2c_read_reg(dev, + DAVINCI_I2C_MDR_REG); + w |= DAVINCI_I2C_MDR_STP; + davinci_i2c_write_reg(dev, + DAVINCI_I2C_MDR_REG, w); + } + complete(&dev->cmd_complete); + break; + + case DAVINCI_I2C_IVR_RDR: + if (dev->buf_len) { + *dev->buf++ = + davinci_i2c_read_reg(dev, + DAVINCI_I2C_DRR_REG); + dev->buf_len--; + if (dev->buf_len) + continue; + + davinci_i2c_write_reg(dev, + DAVINCI_I2C_STR_REG, + DAVINCI_I2C_IMR_RRDY); + } else { + /* signal can terminate transfer */ + terminate_read(dev); + } + break; + + case DAVINCI_I2C_IVR_XRDY: + if (dev->buf_len) { + davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, + *dev->buf++); + dev->buf_len--; + if (dev->buf_len) + continue; + + w = davinci_i2c_read_reg(dev, + DAVINCI_I2C_IMR_REG); + w &= ~DAVINCI_I2C_IMR_XRDY; + davinci_i2c_write_reg(dev, + DAVINCI_I2C_IMR_REG, + w); + } else { + /* signal can terminate transfer */ + terminate_write(dev); + } + break; + + case DAVINCI_I2C_IVR_SCD: + davinci_i2c_write_reg(dev, + DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_SCD); + complete(&dev->cmd_complete); + break; + + case DAVINCI_I2C_IVR_AAS: + dev_dbg(dev->dev, "Address as slave interrupt\n"); + break; + + default: + dev_warn(dev->dev, "Unrecognized irq stat %d\n", stat); + break; + } + } + + return count ? IRQ_HANDLED : IRQ_NONE; +} + +#ifdef CONFIG_CPU_FREQ +static int i2c_davinci_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct davinci_i2c_dev *dev; + + dev = container_of(nb, struct davinci_i2c_dev, freq_transition); + if (val == CPUFREQ_PRECHANGE) { + wait_for_completion(&dev->xfr_complete); + davinci_i2c_reset_ctrl(dev, 0); + } else if (val == CPUFREQ_POSTCHANGE) { + i2c_davinci_calc_clk_dividers(dev); + davinci_i2c_reset_ctrl(dev, 1); + } + + return 0; +} + +static inline int i2c_davinci_cpufreq_register(struct davinci_i2c_dev *dev) +{ + dev->freq_transition.notifier_call = i2c_davinci_cpufreq_transition; + + return cpufreq_register_notifier(&dev->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev) +{ + cpufreq_unregister_notifier(&dev->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} +#else +static inline int i2c_davinci_cpufreq_register(struct davinci_i2c_dev *dev) +{ + return 0; +} + +static inline void i2c_davinci_cpufreq_deregister(struct davinci_i2c_dev *dev) +{ +} +#endif + +static struct i2c_algorithm i2c_davinci_algo = { + .master_xfer = i2c_davinci_xfer, + .functionality = i2c_davinci_func, +}; + +static int davinci_i2c_probe(struct platform_device *pdev) +{ + struct davinci_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *mem, *irq, *ioarea; + int r; + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + ioarea = request_mem_region(mem->start, resource_size(mem), + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct davinci_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto err_release_region; + } + + init_completion(&dev->cmd_complete); +#ifdef CONFIG_CPU_FREQ + init_completion(&dev->xfr_complete); +#endif + dev->dev = get_device(&pdev->dev); + dev->irq = irq->start; + platform_set_drvdata(pdev, dev); + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) { + r = -ENODEV; + goto err_free_mem; + } + clk_enable(dev->clk); + + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + r = -EBUSY; + goto err_mem_ioremap; + } + + i2c_davinci_init(dev); + + r = request_irq(dev->irq, i2c_davinci_isr, 0, pdev->name, dev); + if (r) { + dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); + goto err_unuse_clocks; + } + + r = i2c_davinci_cpufreq_register(dev); + if (r) { + dev_err(&pdev->dev, "failed to register cpufreq\n"); + goto err_free_irq; + } + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name)); + adap->algo = &i2c_davinci_algo; + adap->dev.parent = &pdev->dev; + adap->timeout = DAVINCI_I2C_TIMEOUT; + + adap->nr = pdev->id; + r = i2c_add_numbered_adapter(adap); + if (r) { + dev_err(&pdev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(dev->irq, dev); +err_unuse_clocks: + iounmap(dev->base); +err_mem_ioremap: + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; +err_free_mem: + platform_set_drvdata(pdev, NULL); + put_device(&pdev->dev); + kfree(dev); +err_release_region: + release_mem_region(mem->start, resource_size(mem)); + + return r; +} + +static int davinci_i2c_remove(struct platform_device *pdev) +{ + struct davinci_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + i2c_davinci_cpufreq_deregister(dev); + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&dev->adapter); + put_device(&pdev->dev); + + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; + + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, 0); + free_irq(dev->irq, dev); + iounmap(dev->base); + kfree(dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + return 0; +} + +#ifdef CONFIG_PM +static int davinci_i2c_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct davinci_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + /* put I2C into reset */ + davinci_i2c_reset_ctrl(i2c_dev, 0); + clk_disable(i2c_dev->clk); + + return 0; +} + +static int davinci_i2c_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct davinci_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + clk_enable(i2c_dev->clk); + /* take I2C out of reset */ + davinci_i2c_reset_ctrl(i2c_dev, 1); + + return 0; +} + +static const struct dev_pm_ops davinci_i2c_pm = { + .suspend = davinci_i2c_suspend, + .resume = davinci_i2c_resume, +}; + +#define davinci_i2c_pm_ops (&davinci_i2c_pm) +#else +#define davinci_i2c_pm_ops NULL +#endif + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:i2c_davinci"); + +static struct platform_driver davinci_i2c_driver = { + .probe = davinci_i2c_probe, + .remove = davinci_i2c_remove, + .driver = { + .name = "i2c_davinci", + .owner = THIS_MODULE, + .pm = davinci_i2c_pm_ops, + }, +}; + +/* I2C may be needed to bring up other drivers */ +static int __init davinci_i2c_init_driver(void) +{ + return platform_driver_register(&davinci_i2c_driver); +} +subsys_initcall(davinci_i2c_init_driver); + +static void __exit davinci_i2c_exit_driver(void) +{ + platform_driver_unregister(&davinci_i2c_driver); +} +module_exit(davinci_i2c_exit_driver); + +MODULE_AUTHOR("Texas Instruments India"); +MODULE_DESCRIPTION("TI DaVinci I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c new file mode 100644 index 00000000..b7a51c43 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware.c @@ -0,0 +1,847 @@ +/* + * Synopsys DesignWare I2C adapter driver (master only). + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * + * ---------------------------------------------------------------------------- + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> + +/* + * Registers offset + */ +#define DW_IC_CON 0x0 +#define DW_IC_TAR 0x4 +#define DW_IC_DATA_CMD 0x10 +#define DW_IC_SS_SCL_HCNT 0x14 +#define DW_IC_SS_SCL_LCNT 0x18 +#define DW_IC_FS_SCL_HCNT 0x1c +#define DW_IC_FS_SCL_LCNT 0x20 +#define DW_IC_INTR_STAT 0x2c +#define DW_IC_INTR_MASK 0x30 +#define DW_IC_RAW_INTR_STAT 0x34 +#define DW_IC_RX_TL 0x38 +#define DW_IC_TX_TL 0x3c +#define DW_IC_CLR_INTR 0x40 +#define DW_IC_CLR_RX_UNDER 0x44 +#define DW_IC_CLR_RX_OVER 0x48 +#define DW_IC_CLR_TX_OVER 0x4c +#define DW_IC_CLR_RD_REQ 0x50 +#define DW_IC_CLR_TX_ABRT 0x54 +#define DW_IC_CLR_RX_DONE 0x58 +#define DW_IC_CLR_ACTIVITY 0x5c +#define DW_IC_CLR_STOP_DET 0x60 +#define DW_IC_CLR_START_DET 0x64 +#define DW_IC_CLR_GEN_CALL 0x68 +#define DW_IC_ENABLE 0x6c +#define DW_IC_STATUS 0x70 +#define DW_IC_TXFLR 0x74 +#define DW_IC_RXFLR 0x78 +#define DW_IC_COMP_PARAM_1 0xf4 +#define DW_IC_TX_ABRT_SOURCE 0x80 + +#define DW_IC_CON_MASTER 0x1 +#define DW_IC_CON_SPEED_STD 0x2 +#define DW_IC_CON_SPEED_FAST 0x4 +#define DW_IC_CON_10BITADDR_MASTER 0x10 +#define DW_IC_CON_RESTART_EN 0x20 +#define DW_IC_CON_SLAVE_DISABLE 0x40 + +#define DW_IC_INTR_RX_UNDER 0x001 +#define DW_IC_INTR_RX_OVER 0x002 +#define DW_IC_INTR_RX_FULL 0x004 +#define DW_IC_INTR_TX_OVER 0x008 +#define DW_IC_INTR_TX_EMPTY 0x010 +#define DW_IC_INTR_RD_REQ 0x020 +#define DW_IC_INTR_TX_ABRT 0x040 +#define DW_IC_INTR_RX_DONE 0x080 +#define DW_IC_INTR_ACTIVITY 0x100 +#define DW_IC_INTR_STOP_DET 0x200 +#define DW_IC_INTR_START_DET 0x400 +#define DW_IC_INTR_GEN_CALL 0x800 + +#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \ + DW_IC_INTR_TX_EMPTY | \ + DW_IC_INTR_TX_ABRT | \ + DW_IC_INTR_STOP_DET) + +#define DW_IC_STATUS_ACTIVITY 0x1 + +#define DW_IC_ERR_TX_ABRT 0x1 + +/* + * status codes + */ +#define STATUS_IDLE 0x0 +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 + +#define TIMEOUT 20 /* ms */ + +/* + * hardware abort codes from the DW_IC_TX_ABRT_SOURCE register + * + * only expected abort codes are listed here + * refer to the datasheet for the full list + */ +#define ABRT_7B_ADDR_NOACK 0 +#define ABRT_10ADDR1_NOACK 1 +#define ABRT_10ADDR2_NOACK 2 +#define ABRT_TXDATA_NOACK 3 +#define ABRT_GCALL_NOACK 4 +#define ABRT_GCALL_READ 5 +#define ABRT_SBYTE_ACKDET 7 +#define ABRT_SBYTE_NORSTRT 9 +#define ABRT_10B_RD_NORSTRT 10 +#define ABRT_MASTER_DIS 11 +#define ARB_LOST 12 + +#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK) +#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK) +#define DW_IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK) +#define DW_IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK) +#define DW_IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK) +#define DW_IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ) +#define DW_IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET) +#define DW_IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT) +#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT) +#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS) +#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST) + +#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \ + DW_IC_TX_ABRT_10ADDR1_NOACK | \ + DW_IC_TX_ABRT_10ADDR2_NOACK | \ + DW_IC_TX_ABRT_TXDATA_NOACK | \ + DW_IC_TX_ABRT_GCALL_NOACK) + +static char *abort_sources[] = { + [ABRT_7B_ADDR_NOACK] = + "slave address not acknowledged (7bit mode)", + [ABRT_10ADDR1_NOACK] = + "first address byte not acknowledged (10bit mode)", + [ABRT_10ADDR2_NOACK] = + "second address byte not acknowledged (10bit mode)", + [ABRT_TXDATA_NOACK] = + "data not acknowledged", + [ABRT_GCALL_NOACK] = + "no acknowledgement for a general call", + [ABRT_GCALL_READ] = + "read after general call", + [ABRT_SBYTE_ACKDET] = + "start byte acknowledged", + [ABRT_SBYTE_NORSTRT] = + "trying to send start byte when restart is disabled", + [ABRT_10B_RD_NORSTRT] = + "trying to read when restart is disabled (10bit mode)", + [ABRT_MASTER_DIS] = + "trying to use disabled adapter", + [ARB_LOST] = + "lost arbitration", +}; + +/** + * struct dw_i2c_dev - private i2c-designware data + * @dev: driver model device node + * @base: IO registers pointer + * @cmd_complete: tx completion indicator + * @lock: protect this struct and IO registers + * @clk: input reference clock + * @cmd_err: run time hadware error code + * @msgs: points to an array of messages currently being transferred + * @msgs_num: the number of elements in msgs + * @msg_write_idx: the element index of the current tx message in the msgs + * array + * @tx_buf_len: the length of the current tx buffer + * @tx_buf: the current tx buffer + * @msg_read_idx: the element index of the current rx message in the msgs + * array + * @rx_buf_len: the length of the current rx buffer + * @rx_buf: the current rx buffer + * @msg_err: error status of the current transfer + * @status: i2c master status, one of STATUS_* + * @abort_source: copy of the TX_ABRT_SOURCE register + * @irq: interrupt number for the i2c master + * @adapter: i2c subsystem adapter node + * @tx_fifo_depth: depth of the hardware tx fifo + * @rx_fifo_depth: depth of the hardware rx fifo + */ +struct dw_i2c_dev { + struct device *dev; + void __iomem *base; + struct completion cmd_complete; + struct mutex lock; + struct clk *clk; + int cmd_err; + struct i2c_msg *msgs; + int msgs_num; + int msg_write_idx; + u32 tx_buf_len; + u8 *tx_buf; + int msg_read_idx; + u32 rx_buf_len; + u8 *rx_buf; + int msg_err; + unsigned int status; + u32 abort_source; + int irq; + struct i2c_adapter adapter; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; +}; + +static u32 +i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) +{ + /* + * DesignWare I2C core doesn't seem to have solid strategy to meet + * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec + * will result in violation of the tHD;STA spec. + */ + if (cond) + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH + * + * This is based on the DW manuals, and represents an ideal + * configuration. The resulting I2C bus speed will be + * faster than any of the others. + * + * If your hardware is free from tHD;STA issue, try this one. + */ + return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset; + else + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) + * + * This is just experimental rule; the tHD;STA period turned + * out to be proportinal to (_HCNT + 3). With this setting, + * we could meet both tHIGH and tHD;STA timing specs. + * + * If unsure, you'd better to take this alternative. + * + * The reason why we need to take into account "tf" here, + * is the same as described in i2c_dw_scl_lcnt(). + */ + return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset; +} + +static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) +{ + /* + * Conditional expression: + * + * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) + * + * DW I2C core starts counting the SCL CNTs for the LOW period + * of the SCL clock (tLOW) as soon as it pulls the SCL line. + * In order to meet the tLOW timing spec, we need to take into + * account the fall time of SCL signal (tf). Default tf value + * should be 0.3 us, for safety. + */ + return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset; +} + +/** + * i2c_dw_init() - initialize the designware i2c master hardware + * @dev: device private data + * + * This functions configures and enables the I2C master. + * This function is called during I2C init function, and in case of timeout at + * run time. + */ +static void i2c_dw_init(struct dw_i2c_dev *dev) +{ + u32 input_clock_khz = clk_get_rate(dev->clk) / 1000; + u32 ic_con, hcnt, lcnt; + + /* Disable the adapter */ + writel(0, dev->base + DW_IC_ENABLE); + + /* set standard and fast speed deviders for high/low periods */ + + /* Standard-mode */ + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 40, /* tHD;STA = tHIGH = 4.0 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 47, /* tLOW = 4.7 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT); + writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT); + dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + + /* Fast-mode */ + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 6, /* tHD;STA = tHIGH = 0.6 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 13, /* tLOW = 1.3 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT); + writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT); + dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + + /* Configure Tx/Rx FIFO threshold levels */ + writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL); + writel(0, dev->base + DW_IC_RX_TL); + + /* configure the i2c master */ + ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | + DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; + writel(ic_con, dev->base + DW_IC_CON); +} + +/* + * Waiting for bus not busy + */ +static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) +{ + int timeout = TIMEOUT; + + while (readl(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { + if (timeout <= 0) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + timeout--; + mdelay(1); + } + + return 0; +} + +static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 ic_con; + + /* Disable the adapter */ + writel(0, dev->base + DW_IC_ENABLE); + + /* set the slave (target) address */ + writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR); + + /* if the slave address is ten bit address, enable 10BITADDR */ + ic_con = readl(dev->base + DW_IC_CON); + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) + ic_con |= DW_IC_CON_10BITADDR_MASTER; + else + ic_con &= ~DW_IC_CON_10BITADDR_MASTER; + writel(ic_con, dev->base + DW_IC_CON); + + /* Enable the adapter */ + writel(1, dev->base + DW_IC_ENABLE); + + /* Enable interrupts */ + writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK); +} + +/* + * Initiate (and continue) low level master read/write transaction. + * This function is only called from i2c_dw_isr, and pumping i2c_msg + * messages into the tx buffer. Even if the size of i2c_msg data is + * longer than the size of the tx buffer, it handles everything. + */ +static void +i2c_dw_xfer_msg(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 intr_mask; + int tx_limit, rx_limit; + u32 addr = msgs[dev->msg_write_idx].addr; + u32 buf_len = dev->tx_buf_len; + u8 *buf = dev->tx_buf;; + + intr_mask = DW_IC_INTR_DEFAULT_MASK; + + for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { + /* + * if target address has changed, we need to + * reprogram the target address in the i2c + * adapter when we are done with this transfer + */ + if (msgs[dev->msg_write_idx].addr != addr) { + dev_err(dev->dev, + "%s: invalid target address\n", __func__); + dev->msg_err = -EINVAL; + break; + } + + if (msgs[dev->msg_write_idx].len == 0) { + dev_err(dev->dev, + "%s: invalid message length\n", __func__); + dev->msg_err = -EINVAL; + break; + } + + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* new i2c_msg */ + buf = msgs[dev->msg_write_idx].buf; + buf_len = msgs[dev->msg_write_idx].len; + } + + tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR); + rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR); + + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { + writel(0x100, dev->base + DW_IC_DATA_CMD); + rx_limit--; + } else + writel(*buf++, dev->base + DW_IC_DATA_CMD); + tx_limit--; buf_len--; + } + + dev->tx_buf = buf; + dev->tx_buf_len = buf_len; + + if (buf_len > 0) { + /* more bytes to be written */ + dev->status |= STATUS_WRITE_IN_PROGRESS; + break; + } else + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + } + + /* + * If i2c_msg index search is completed, we don't need TX_EMPTY + * interrupt any more. + */ + if (dev->msg_write_idx == dev->msgs_num) + intr_mask &= ~DW_IC_INTR_TX_EMPTY; + + if (dev->msg_err) + intr_mask = 0; + + writel(intr_mask, dev->base + DW_IC_INTR_MASK); +} + +static void +i2c_dw_read(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + int rx_valid; + + for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) { + u32 len; + u8 *buf; + + if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD)) + continue; + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + len = msgs[dev->msg_read_idx].len; + buf = msgs[dev->msg_read_idx].buf; + } else { + len = dev->rx_buf_len; + buf = dev->rx_buf; + } + + rx_valid = readl(dev->base + DW_IC_RXFLR); + + for (; len > 0 && rx_valid > 0; len--, rx_valid--) + *buf++ = readl(dev->base + DW_IC_DATA_CMD); + + if (len > 0) { + dev->status |= STATUS_READ_IN_PROGRESS; + dev->rx_buf_len = len; + dev->rx_buf = buf; + return; + } else + dev->status &= ~STATUS_READ_IN_PROGRESS; + } +} + +static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) +{ + unsigned long abort_source = dev->abort_source; + int i; + + if (abort_source & DW_IC_TX_ABRT_NOACK) { + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_dbg(dev->dev, + "%s: %s\n", __func__, abort_sources[i]); + return -EREMOTEIO; + } + + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); + + if (abort_source & DW_IC_TX_ARB_LOST) + return -EAGAIN; + else if (abort_source & DW_IC_TX_ABRT_GCALL_READ) + return -EINVAL; /* wrong msgs[] data */ + else + return -EIO; +} + +/* + * Prepare controller for a transaction and call i2c_dw_xfer_msg + */ +static int +i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + int ret; + + dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + + mutex_lock(&dev->lock); + + INIT_COMPLETION(dev->cmd_complete); + dev->msgs = msgs; + dev->msgs_num = num; + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + + ret = i2c_dw_wait_bus_not_busy(dev); + if (ret < 0) + goto done; + + /* start the transfers */ + i2c_dw_xfer_init(dev); + + /* wait for tx to complete */ + ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ); + if (ret == 0) { + dev_err(dev->dev, "controller timed out\n"); + i2c_dw_init(dev); + ret = -ETIMEDOUT; + goto done; + } else if (ret < 0) + goto done; + + if (dev->msg_err) { + ret = dev->msg_err; + goto done; + } + + /* no error */ + if (likely(!dev->cmd_err)) { + /* Disable the adapter */ + writel(0, dev->base + DW_IC_ENABLE); + ret = num; + goto done; + } + + /* We have an error */ + if (dev->cmd_err == DW_IC_ERR_TX_ABRT) { + ret = i2c_dw_handle_tx_abort(dev); + goto done; + } + ret = -EIO; + +done: + mutex_unlock(&dev->lock); + + return ret; +} + +static u32 i2c_dw_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | + I2C_FUNC_10BIT_ADDR | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) +{ + u32 stat; + + /* + * The IC_INTR_STAT register just indicates "enabled" interrupts. + * Ths unmasked raw version of interrupt status bits are available + * in the IC_RAW_INTR_STAT register. + * + * That is, + * stat = readl(IC_INTR_STAT); + * equals to, + * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK); + * + * The raw version might be useful for debugging purposes. + */ + stat = readl(dev->base + DW_IC_INTR_STAT); + + /* + * Do not use the IC_CLR_INTR register to clear interrupts, or + * you'll miss some interrupts, triggered during the period from + * readl(IC_INTR_STAT) to readl(IC_CLR_INTR). + * + * Instead, use the separately-prepared IC_CLR_* registers. + */ + if (stat & DW_IC_INTR_RX_UNDER) + readl(dev->base + DW_IC_CLR_RX_UNDER); + if (stat & DW_IC_INTR_RX_OVER) + readl(dev->base + DW_IC_CLR_RX_OVER); + if (stat & DW_IC_INTR_TX_OVER) + readl(dev->base + DW_IC_CLR_TX_OVER); + if (stat & DW_IC_INTR_RD_REQ) + readl(dev->base + DW_IC_CLR_RD_REQ); + if (stat & DW_IC_INTR_TX_ABRT) { + /* + * The IC_TX_ABRT_SOURCE register is cleared whenever + * the IC_CLR_TX_ABRT is read. Preserve it beforehand. + */ + dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE); + readl(dev->base + DW_IC_CLR_TX_ABRT); + } + if (stat & DW_IC_INTR_RX_DONE) + readl(dev->base + DW_IC_CLR_RX_DONE); + if (stat & DW_IC_INTR_ACTIVITY) + readl(dev->base + DW_IC_CLR_ACTIVITY); + if (stat & DW_IC_INTR_STOP_DET) + readl(dev->base + DW_IC_CLR_STOP_DET); + if (stat & DW_IC_INTR_START_DET) + readl(dev->base + DW_IC_CLR_START_DET); + if (stat & DW_IC_INTR_GEN_CALL) + readl(dev->base + DW_IC_CLR_GEN_CALL); + + return stat; +} + +/* + * Interrupt service routine. This gets called whenever an I2C interrupt + * occurs. + */ +static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) +{ + struct dw_i2c_dev *dev = dev_id; + u32 stat; + + stat = i2c_dw_read_clear_intrbits(dev); + dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat); + + if (stat & DW_IC_INTR_TX_ABRT) { + dev->cmd_err |= DW_IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + + /* + * Anytime TX_ABRT is set, the contents of the tx/rx + * buffers are flushed. Make sure to skip them. + */ + writel(0, dev->base + DW_IC_INTR_MASK); + goto tx_aborted; + } + + if (stat & DW_IC_INTR_RX_FULL) + i2c_dw_read(dev); + + if (stat & DW_IC_INTR_TX_EMPTY) + i2c_dw_xfer_msg(dev); + + /* + * No need to modify or disable the interrupt mask here. + * i2c_dw_xfer_msg() will take care of it according to + * the current transmit status. + */ + +tx_aborted: + if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err) + complete(&dev->cmd_complete); + + return IRQ_HANDLED; +} + +static struct i2c_algorithm i2c_dw_algo = { + .master_xfer = i2c_dw_xfer, + .functionality = i2c_dw_func, +}; + +static int __devinit dw_i2c_probe(struct platform_device *pdev) +{ + struct dw_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *mem, *ioarea; + int irq, r; + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return irq; /* -ENXIO */ + } + + ioarea = request_mem_region(mem->start, resource_size(mem), + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto err_release_region; + } + + init_completion(&dev->cmd_complete); + mutex_init(&dev->lock); + dev->dev = get_device(&pdev->dev); + dev->irq = irq; + platform_set_drvdata(pdev, dev); + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) { + r = -ENODEV; + goto err_free_mem; + } + clk_enable(dev->clk); + + dev->base = ioremap(mem->start, resource_size(mem)); + if (dev->base == NULL) { + dev_err(&pdev->dev, "failure mapping io resources\n"); + r = -EBUSY; + goto err_unuse_clocks; + } + { + u32 param1 = readl(dev->base + DW_IC_COMP_PARAM_1); + + dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1; + dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1; + } + i2c_dw_init(dev); + + writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */ + r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev); + if (r) { + dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); + goto err_iounmap; + } + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "Synopsys DesignWare I2C adapter", + sizeof(adap->name)); + adap->algo = &i2c_dw_algo; + adap->dev.parent = &pdev->dev; + + adap->nr = pdev->id; + r = i2c_add_numbered_adapter(adap); + if (r) { + dev_err(&pdev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(dev->irq, dev); +err_iounmap: + iounmap(dev->base); +err_unuse_clocks: + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; +err_free_mem: + platform_set_drvdata(pdev, NULL); + put_device(&pdev->dev); + kfree(dev); +err_release_region: + release_mem_region(mem->start, resource_size(mem)); + + return r; +} + +static int __devexit dw_i2c_remove(struct platform_device *pdev) +{ + struct dw_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&dev->adapter); + put_device(&pdev->dev); + + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; + + writel(0, dev->base + DW_IC_ENABLE); + free_irq(dev->irq, dev); + kfree(dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:i2c_designware"); + +static struct platform_driver dw_i2c_driver = { + .remove = __devexit_p(dw_i2c_remove), + .driver = { + .name = "i2c_designware", + .owner = THIS_MODULE, + }, +}; + +static int __init dw_i2c_init_driver(void) +{ + return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe); +} +module_init(dw_i2c_init_driver); + +static void __exit dw_i2c_exit_driver(void) +{ + platform_driver_unregister(&dw_i2c_driver); +} +module_exit(dw_i2c_exit_driver); + +MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); +MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-diolan-u2c.c b/drivers/i2c/busses/i2c-diolan-u2c.c new file mode 100644 index 00000000..76366716 --- /dev/null +++ b/drivers/i2c/busses/i2c-diolan-u2c.c @@ -0,0 +1,535 @@ +/* + * Driver for the Diolan u2c-12 USB-I2C adapter + * + * Copyright (c) 2010-2011 Ericsson AB + * + * Derived from: + * i2c-tiny-usb.c + * Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.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, version 2. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/i2c.h> + +#define DRIVER_NAME "i2c-diolan-u2c" + +#define USB_VENDOR_ID_DIOLAN 0x0abf +#define USB_DEVICE_ID_DIOLAN_U2C 0x3370 + +#define DIOLAN_OUT_EP 0x02 +#define DIOLAN_IN_EP 0x84 + +/* commands via USB, must match command ids in the firmware */ +#define CMD_I2C_READ 0x01 +#define CMD_I2C_WRITE 0x02 +#define CMD_I2C_SCAN 0x03 /* Returns list of detected devices */ +#define CMD_I2C_RELEASE_SDA 0x04 +#define CMD_I2C_RELEASE_SCL 0x05 +#define CMD_I2C_DROP_SDA 0x06 +#define CMD_I2C_DROP_SCL 0x07 +#define CMD_I2C_READ_SDA 0x08 +#define CMD_I2C_READ_SCL 0x09 +#define CMD_GET_FW_VERSION 0x0a +#define CMD_GET_SERIAL 0x0b +#define CMD_I2C_START 0x0c +#define CMD_I2C_STOP 0x0d +#define CMD_I2C_REPEATED_START 0x0e +#define CMD_I2C_PUT_BYTE 0x0f +#define CMD_I2C_GET_BYTE 0x10 +#define CMD_I2C_PUT_ACK 0x11 +#define CMD_I2C_GET_ACK 0x12 +#define CMD_I2C_PUT_BYTE_ACK 0x13 +#define CMD_I2C_GET_BYTE_ACK 0x14 +#define CMD_I2C_SET_SPEED 0x1b +#define CMD_I2C_GET_SPEED 0x1c +#define CMD_I2C_SET_CLK_SYNC 0x24 +#define CMD_I2C_GET_CLK_SYNC 0x25 +#define CMD_I2C_SET_CLK_SYNC_TO 0x26 +#define CMD_I2C_GET_CLK_SYNC_TO 0x27 + +#define RESP_OK 0x00 +#define RESP_FAILED 0x01 +#define RESP_BAD_MEMADDR 0x04 +#define RESP_DATA_ERR 0x05 +#define RESP_NOT_IMPLEMENTED 0x06 +#define RESP_NACK 0x07 +#define RESP_TIMEOUT 0x09 + +#define U2C_I2C_SPEED_FAST 0 /* 400 kHz */ +#define U2C_I2C_SPEED_STD 1 /* 100 kHz */ +#define U2C_I2C_SPEED_2KHZ 242 /* 2 kHz, minimum speed */ +#define U2C_I2C_SPEED(f) ((DIV_ROUND_UP(1000000, (f)) - 10) / 2 + 1) + +#define U2C_I2C_FREQ_FAST 400000 +#define U2C_I2C_FREQ_STD 100000 +#define U2C_I2C_FREQ(s) (1000000 / (2 * (s - 1) + 10)) + +#define DIOLAN_USB_TIMEOUT 100 /* in ms */ +#define DIOLAN_SYNC_TIMEOUT 20 /* in ms */ + +#define DIOLAN_OUTBUF_LEN 128 +#define DIOLAN_FLUSH_LEN (DIOLAN_OUTBUF_LEN - 4) +#define DIOLAN_INBUF_LEN 256 /* Maximum supported receive length */ + +/* Structure to hold all of our device specific stuff */ +struct i2c_diolan_u2c { + u8 obuffer[DIOLAN_OUTBUF_LEN]; /* output buffer */ + u8 ibuffer[DIOLAN_INBUF_LEN]; /* input buffer */ + struct usb_device *usb_dev; /* the usb device for this device */ + struct usb_interface *interface;/* the interface for this device */ + struct i2c_adapter adapter; /* i2c related things */ + int olen; /* Output buffer length */ + int ocount; /* Number of enqueued messages */ +}; + +static uint frequency = U2C_I2C_FREQ_STD; /* I2C clock frequency in Hz */ + +module_param(frequency, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(frequency, "I2C clock frequency in hertz"); + +/* usb layer */ + +/* Send command to device, and get response. */ +static int diolan_usb_transfer(struct i2c_diolan_u2c *dev) +{ + int ret = 0; + int actual; + int i; + + if (!dev->olen || !dev->ocount) + return -EINVAL; + + ret = usb_bulk_msg(dev->usb_dev, + usb_sndbulkpipe(dev->usb_dev, DIOLAN_OUT_EP), + dev->obuffer, dev->olen, &actual, + DIOLAN_USB_TIMEOUT); + if (!ret) { + for (i = 0; i < dev->ocount; i++) { + int tmpret; + + tmpret = usb_bulk_msg(dev->usb_dev, + usb_rcvbulkpipe(dev->usb_dev, + DIOLAN_IN_EP), + dev->ibuffer, + sizeof(dev->ibuffer), &actual, + DIOLAN_USB_TIMEOUT); + /* + * Stop command processing if a previous command + * returned an error. + * Note that we still need to retrieve all messages. + */ + if (ret < 0) + continue; + ret = tmpret; + if (ret == 0 && actual > 0) { + switch (dev->ibuffer[actual - 1]) { + case RESP_NACK: + /* + * Return ENXIO if NACK was received as + * response to the address phase, + * EIO otherwise + */ + ret = i == 1 ? -ENXIO : -EIO; + break; + case RESP_TIMEOUT: + ret = -ETIMEDOUT; + break; + case RESP_OK: + /* strip off return code */ + ret = actual - 1; + break; + default: + ret = -EIO; + break; + } + } + } + } + dev->olen = 0; + dev->ocount = 0; + return ret; +} + +static int diolan_write_cmd(struct i2c_diolan_u2c *dev, bool flush) +{ + if (flush || dev->olen >= DIOLAN_FLUSH_LEN) + return diolan_usb_transfer(dev); + return 0; +} + +/* Send command (no data) */ +static int diolan_usb_cmd(struct i2c_diolan_u2c *dev, u8 command, bool flush) +{ + dev->obuffer[dev->olen++] = command; + dev->ocount++; + return diolan_write_cmd(dev, flush); +} + +/* Send command with one byte of data */ +static int diolan_usb_cmd_data(struct i2c_diolan_u2c *dev, u8 command, u8 data, + bool flush) +{ + dev->obuffer[dev->olen++] = command; + dev->obuffer[dev->olen++] = data; + dev->ocount++; + return diolan_write_cmd(dev, flush); +} + +/* Send command with two bytes of data */ +static int diolan_usb_cmd_data2(struct i2c_diolan_u2c *dev, u8 command, u8 d1, + u8 d2, bool flush) +{ + dev->obuffer[dev->olen++] = command; + dev->obuffer[dev->olen++] = d1; + dev->obuffer[dev->olen++] = d2; + dev->ocount++; + return diolan_write_cmd(dev, flush); +} + +/* + * Flush input queue. + * If we don't do this at startup and the controller has queued up + * messages which were not retrieved, it will stop responding + * at some point. + */ +static void diolan_flush_input(struct i2c_diolan_u2c *dev) +{ + int i; + + for (i = 0; i < 10; i++) { + int actual = 0; + int ret; + + ret = usb_bulk_msg(dev->usb_dev, + usb_rcvbulkpipe(dev->usb_dev, DIOLAN_IN_EP), + dev->ibuffer, sizeof(dev->ibuffer), &actual, + DIOLAN_USB_TIMEOUT); + if (ret < 0 || actual == 0) + break; + } + if (i == 10) + dev_err(&dev->interface->dev, "Failed to flush input buffer\n"); +} + +static int diolan_i2c_start(struct i2c_diolan_u2c *dev) +{ + return diolan_usb_cmd(dev, CMD_I2C_START, false); +} + +static int diolan_i2c_repeated_start(struct i2c_diolan_u2c *dev) +{ + return diolan_usb_cmd(dev, CMD_I2C_REPEATED_START, false); +} + +static int diolan_i2c_stop(struct i2c_diolan_u2c *dev) +{ + return diolan_usb_cmd(dev, CMD_I2C_STOP, true); +} + +static int diolan_i2c_get_byte_ack(struct i2c_diolan_u2c *dev, bool ack, + u8 *byte) +{ + int ret; + + ret = diolan_usb_cmd_data(dev, CMD_I2C_GET_BYTE_ACK, ack, true); + if (ret > 0) + *byte = dev->ibuffer[0]; + else if (ret == 0) + ret = -EIO; + + return ret; +} + +static int diolan_i2c_put_byte_ack(struct i2c_diolan_u2c *dev, u8 byte) +{ + return diolan_usb_cmd_data(dev, CMD_I2C_PUT_BYTE_ACK, byte, false); +} + +static int diolan_set_speed(struct i2c_diolan_u2c *dev, u8 speed) +{ + return diolan_usb_cmd_data(dev, CMD_I2C_SET_SPEED, speed, true); +} + +/* Enable or disable clock synchronization (stretching) */ +static int diolan_set_clock_synch(struct i2c_diolan_u2c *dev, bool enable) +{ + return diolan_usb_cmd_data(dev, CMD_I2C_SET_CLK_SYNC, enable, true); +} + +/* Set clock synchronization timeout in ms */ +static int diolan_set_clock_synch_timeout(struct i2c_diolan_u2c *dev, int ms) +{ + int to_val = ms * 10; + + return diolan_usb_cmd_data2(dev, CMD_I2C_SET_CLK_SYNC_TO, + to_val & 0xff, (to_val >> 8) & 0xff, true); +} + +static void diolan_fw_version(struct i2c_diolan_u2c *dev) +{ + int ret; + + ret = diolan_usb_cmd(dev, CMD_GET_FW_VERSION, true); + if (ret >= 2) + dev_info(&dev->interface->dev, + "Diolan U2C firmware version %u.%u\n", + (unsigned int)dev->ibuffer[0], + (unsigned int)dev->ibuffer[1]); +} + +static void diolan_get_serial(struct i2c_diolan_u2c *dev) +{ + int ret; + u32 serial; + + ret = diolan_usb_cmd(dev, CMD_GET_SERIAL, true); + if (ret >= 4) { + serial = le32_to_cpu(*(u32 *)dev->ibuffer); + dev_info(&dev->interface->dev, + "Diolan U2C serial number %u\n", serial); + } +} + +static int diolan_init(struct i2c_diolan_u2c *dev) +{ + int speed, ret; + + if (frequency >= 200000) { + speed = U2C_I2C_SPEED_FAST; + frequency = U2C_I2C_FREQ_FAST; + } else if (frequency >= 100000 || frequency == 0) { + speed = U2C_I2C_SPEED_STD; + frequency = U2C_I2C_FREQ_STD; + } else { + speed = U2C_I2C_SPEED(frequency); + if (speed > U2C_I2C_SPEED_2KHZ) + speed = U2C_I2C_SPEED_2KHZ; + frequency = U2C_I2C_FREQ(speed); + } + + dev_info(&dev->interface->dev, + "Diolan U2C at USB bus %03d address %03d speed %d Hz\n", + dev->usb_dev->bus->busnum, dev->usb_dev->devnum, frequency); + + diolan_flush_input(dev); + diolan_fw_version(dev); + diolan_get_serial(dev); + + /* Set I2C speed */ + ret = diolan_set_speed(dev, speed); + if (ret < 0) + return ret; + + /* Configure I2C clock synchronization */ + ret = diolan_set_clock_synch(dev, speed != U2C_I2C_SPEED_FAST); + if (ret < 0) + return ret; + + if (speed != U2C_I2C_SPEED_FAST) + ret = diolan_set_clock_synch_timeout(dev, DIOLAN_SYNC_TIMEOUT); + + return ret; +} + +/* i2c layer */ + +static int diolan_usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, + int num) +{ + struct i2c_diolan_u2c *dev = i2c_get_adapdata(adapter); + struct i2c_msg *pmsg; + int i, j; + int ret, sret; + + ret = diolan_i2c_start(dev); + if (ret < 0) + return ret; + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + if (i) { + ret = diolan_i2c_repeated_start(dev); + if (ret < 0) + goto abort; + } + if (pmsg->flags & I2C_M_RD) { + ret = + diolan_i2c_put_byte_ack(dev, (pmsg->addr << 1) | 1); + if (ret < 0) + goto abort; + for (j = 0; j < pmsg->len; j++) { + u8 byte; + bool ack = j < pmsg->len - 1; + + /* + * Don't send NACK if this is the first byte + * of a SMBUS_BLOCK message. + */ + if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) + ack = true; + + ret = diolan_i2c_get_byte_ack(dev, ack, &byte); + if (ret < 0) + goto abort; + /* + * Adjust count if first received byte is length + */ + if (j == 0 && (pmsg->flags & I2C_M_RECV_LEN)) { + if (byte == 0 + || byte > I2C_SMBUS_BLOCK_MAX) { + ret = -EPROTO; + goto abort; + } + pmsg->len += byte; + } + pmsg->buf[j] = byte; + } + } else { + ret = diolan_i2c_put_byte_ack(dev, pmsg->addr << 1); + if (ret < 0) + goto abort; + for (j = 0; j < pmsg->len; j++) { + ret = diolan_i2c_put_byte_ack(dev, + pmsg->buf[j]); + if (ret < 0) + goto abort; + } + } + } +abort: + sret = diolan_i2c_stop(dev); + if (sret < 0 && ret >= 0) + ret = sret; + return ret; +} + +/* + * Return list of supported functionality. + */ +static u32 diolan_usb_func(struct i2c_adapter *a) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL; +} + +static const struct i2c_algorithm diolan_usb_algorithm = { + .master_xfer = diolan_usb_xfer, + .functionality = diolan_usb_func, +}; + +/* device layer */ + +static const struct usb_device_id diolan_u2c_table[] = { + { USB_DEVICE(USB_VENDOR_ID_DIOLAN, USB_DEVICE_ID_DIOLAN_U2C) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, diolan_u2c_table); + +static void diolan_u2c_free(struct i2c_diolan_u2c *dev) +{ + usb_put_dev(dev->usb_dev); + kfree(dev); +} + +static int diolan_u2c_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct i2c_diolan_u2c *dev; + int ret; + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&interface->dev, "no memory for device state\n"); + ret = -ENOMEM; + goto error; + } + + dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* setup i2c adapter description */ + dev->adapter.owner = THIS_MODULE; + dev->adapter.class = I2C_CLASS_HWMON; + dev->adapter.algo = &diolan_usb_algorithm; + i2c_set_adapdata(&dev->adapter, dev); + snprintf(dev->adapter.name, sizeof(dev->adapter.name), + DRIVER_NAME " at bus %03d device %03d", + dev->usb_dev->bus->busnum, dev->usb_dev->devnum); + + dev->adapter.dev.parent = &dev->interface->dev; + + /* initialize diolan i2c interface */ + ret = diolan_init(dev); + if (ret < 0) { + dev_err(&interface->dev, "failed to initialize adapter\n"); + goto error_free; + } + + /* and finally attach to i2c layer */ + ret = i2c_add_adapter(&dev->adapter); + if (ret < 0) { + dev_err(&interface->dev, "failed to add I2C adapter\n"); + goto error_free; + } + + dev_dbg(&interface->dev, "connected " DRIVER_NAME "\n"); + + return 0; + +error_free: + usb_set_intfdata(interface, NULL); + diolan_u2c_free(dev); +error: + return ret; +} + +static void diolan_u2c_disconnect(struct usb_interface *interface) +{ + struct i2c_diolan_u2c *dev = usb_get_intfdata(interface); + + i2c_del_adapter(&dev->adapter); + usb_set_intfdata(interface, NULL); + diolan_u2c_free(dev); + + dev_dbg(&interface->dev, "disconnected\n"); +} + +static struct usb_driver diolan_u2c_driver = { + .name = DRIVER_NAME, + .probe = diolan_u2c_probe, + .disconnect = diolan_u2c_disconnect, + .id_table = diolan_u2c_table, +}; + +static int __init diolan_u2c_init(void) +{ + /* register this driver with the USB subsystem */ + return usb_register(&diolan_u2c_driver); +} + +static void __exit diolan_u2c_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&diolan_u2c_driver); +} + +module_init(diolan_u2c_init); +module_exit(diolan_u2c_exit); + +MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); +MODULE_DESCRIPTION(DRIVER_NAME " driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c new file mode 100644 index 00000000..656b028d --- /dev/null +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -0,0 +1,940 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * + * 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. + * + * 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/kernel.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/pci.h> +#include <linux/mutex.h> +#include <linux/ktime.h> +#include <linux/slab.h> + +#define PCH_EVENT_SET 0 /* I2C Interrupt Event Set Status */ +#define PCH_EVENT_NONE 1 /* I2C Interrupt Event Clear Status */ +#define PCH_MAX_CLK 100000 /* Maximum Clock speed in MHz */ +#define PCH_BUFFER_MODE_ENABLE 0x0002 /* flag for Buffer mode enable */ +#define PCH_EEPROM_SW_RST_MODE_ENABLE 0x0008 /* EEPROM SW RST enable flag */ + +#define PCH_I2CSADR 0x00 /* I2C slave address register */ +#define PCH_I2CCTL 0x04 /* I2C control register */ +#define PCH_I2CSR 0x08 /* I2C status register */ +#define PCH_I2CDR 0x0C /* I2C data register */ +#define PCH_I2CMON 0x10 /* I2C bus monitor register */ +#define PCH_I2CBC 0x14 /* I2C bus transfer rate setup counter */ +#define PCH_I2CMOD 0x18 /* I2C mode register */ +#define PCH_I2CBUFSLV 0x1C /* I2C buffer mode slave address register */ +#define PCH_I2CBUFSUB 0x20 /* I2C buffer mode subaddress register */ +#define PCH_I2CBUFFOR 0x24 /* I2C buffer mode format register */ +#define PCH_I2CBUFCTL 0x28 /* I2C buffer mode control register */ +#define PCH_I2CBUFMSK 0x2C /* I2C buffer mode interrupt mask register */ +#define PCH_I2CBUFSTA 0x30 /* I2C buffer mode status register */ +#define PCH_I2CBUFLEV 0x34 /* I2C buffer mode level register */ +#define PCH_I2CESRFOR 0x38 /* EEPROM software reset mode format register */ +#define PCH_I2CESRCTL 0x3C /* EEPROM software reset mode ctrl register */ +#define PCH_I2CESRMSK 0x40 /* EEPROM software reset mode */ +#define PCH_I2CESRSTA 0x44 /* EEPROM software reset mode status register */ +#define PCH_I2CTMR 0x48 /* I2C timer register */ +#define PCH_I2CSRST 0xFC /* I2C reset register */ +#define PCH_I2CNF 0xF8 /* I2C noise filter register */ + +#define BUS_IDLE_TIMEOUT 20 +#define PCH_I2CCTL_I2CMEN 0x0080 +#define TEN_BIT_ADDR_DEFAULT 0xF000 +#define TEN_BIT_ADDR_MASK 0xF0 +#define PCH_START 0x0020 +#define PCH_ESR_START 0x0001 +#define PCH_BUFF_START 0x1 +#define PCH_REPSTART 0x0004 +#define PCH_ACK 0x0008 +#define PCH_GETACK 0x0001 +#define CLR_REG 0x0 +#define I2C_RD 0x1 +#define I2CMCF_BIT 0x0080 +#define I2CMIF_BIT 0x0002 +#define I2CMAL_BIT 0x0010 +#define I2CBMFI_BIT 0x0001 +#define I2CBMAL_BIT 0x0002 +#define I2CBMNA_BIT 0x0004 +#define I2CBMTO_BIT 0x0008 +#define I2CBMIS_BIT 0x0010 +#define I2CESRFI_BIT 0X0001 +#define I2CESRTO_BIT 0x0002 +#define I2CESRFIIE_BIT 0x1 +#define I2CESRTOIE_BIT 0x2 +#define I2CBMDZ_BIT 0x0040 +#define I2CBMAG_BIT 0x0020 +#define I2CMBB_BIT 0x0020 +#define BUFFER_MODE_MASK (I2CBMFI_BIT | I2CBMAL_BIT | I2CBMNA_BIT | \ + I2CBMTO_BIT | I2CBMIS_BIT) +#define I2C_ADDR_MSK 0xFF +#define I2C_MSB_2B_MSK 0x300 +#define FAST_MODE_CLK 400 +#define FAST_MODE_EN 0x0001 +#define SUB_ADDR_LEN_MAX 4 +#define BUF_LEN_MAX 32 +#define PCH_BUFFER_MODE 0x1 +#define EEPROM_SW_RST_MODE 0x0002 +#define NORMAL_INTR_ENBL 0x0300 +#define EEPROM_RST_INTR_ENBL (I2CESRFIIE_BIT | I2CESRTOIE_BIT) +#define EEPROM_RST_INTR_DISBL 0x0 +#define BUFFER_MODE_INTR_ENBL 0x001F +#define BUFFER_MODE_INTR_DISBL 0x0 +#define NORMAL_MODE 0x0 +#define BUFFER_MODE 0x1 +#define EEPROM_SR_MODE 0x2 +#define I2C_TX_MODE 0x0010 +#define PCH_BUF_TX 0xFFF7 +#define PCH_BUF_RD 0x0008 +#define I2C_ERROR_MASK (I2CESRTO_EVENT | I2CBMIS_EVENT | I2CBMTO_EVENT | \ + I2CBMNA_EVENT | I2CBMAL_EVENT | I2CMAL_EVENT) +#define I2CMAL_EVENT 0x0001 +#define I2CMCF_EVENT 0x0002 +#define I2CBMFI_EVENT 0x0004 +#define I2CBMAL_EVENT 0x0008 +#define I2CBMNA_EVENT 0x0010 +#define I2CBMTO_EVENT 0x0020 +#define I2CBMIS_EVENT 0x0040 +#define I2CESRFI_EVENT 0x0080 +#define I2CESRTO_EVENT 0x0100 +#define PCI_DEVICE_ID_PCH_I2C 0x8817 + +#define pch_dbg(adap, fmt, arg...) \ + dev_dbg(adap->pch_adapter.dev.parent, "%s :" fmt, __func__, ##arg) + +#define pch_err(adap, fmt, arg...) \ + dev_err(adap->pch_adapter.dev.parent, "%s :" fmt, __func__, ##arg) + +#define pch_pci_err(pdev, fmt, arg...) \ + dev_err(&pdev->dev, "%s :" fmt, __func__, ##arg) + +#define pch_pci_dbg(pdev, fmt, arg...) \ + dev_dbg(&pdev->dev, "%s :" fmt, __func__, ##arg) + +/* +Set the number of I2C instance max +Intel EG20T PCH : 1ch +OKI SEMICONDUCTOR ML7213 IOH : 2ch +*/ +#define PCH_I2C_MAX_DEV 2 + +/** + * struct i2c_algo_pch_data - for I2C driver functionalities + * @pch_adapter: stores the reference to i2c_adapter structure + * @p_adapter_info: stores the reference to adapter_info structure + * @pch_base_address: specifies the remapped base address + * @pch_buff_mode_en: specifies if buffer mode is enabled + * @pch_event_flag: specifies occurrence of interrupt events + * @pch_i2c_xfer_in_progress: specifies whether the transfer is completed + */ +struct i2c_algo_pch_data { + struct i2c_adapter pch_adapter; + struct adapter_info *p_adapter_info; + void __iomem *pch_base_address; + int pch_buff_mode_en; + u32 pch_event_flag; + bool pch_i2c_xfer_in_progress; +}; + +/** + * struct adapter_info - This structure holds the adapter information for the + PCH i2c controller + * @pch_data: stores a list of i2c_algo_pch_data + * @pch_i2c_suspended: specifies whether the system is suspended or not + * perhaps with more lines and words. + * @ch_num: specifies the number of i2c instance + * + * pch_data has as many elements as maximum I2C channels + */ +struct adapter_info { + struct i2c_algo_pch_data pch_data[PCH_I2C_MAX_DEV]; + bool pch_i2c_suspended; + int ch_num; +}; + + +static int pch_i2c_speed = 100; /* I2C bus speed in Kbps */ +static int pch_clk = 50000; /* specifies I2C clock speed in KHz */ +static wait_queue_head_t pch_event; +static DEFINE_MUTEX(pch_mutex); + +/* Definition for ML7213 by OKI SEMICONDUCTOR */ +#define PCI_VENDOR_ID_ROHM 0x10DB +#define PCI_DEVICE_ID_ML7213_I2C 0x802D +#define PCI_DEVICE_ID_ML7223_I2C 0x8010 + +static struct pci_device_id __devinitdata pch_pcidev_id[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, }, + {0,} +}; + +static irqreturn_t pch_i2c_handler(int irq, void *pData); + +static inline void pch_setbit(void __iomem *addr, u32 offset, u32 bitmask) +{ + u32 val; + val = ioread32(addr + offset); + val |= bitmask; + iowrite32(val, addr + offset); +} + +static inline void pch_clrbit(void __iomem *addr, u32 offset, u32 bitmask) +{ + u32 val; + val = ioread32(addr + offset); + val &= (~bitmask); + iowrite32(val, addr + offset); +} + +/** + * pch_i2c_init() - hardware initialization of I2C module + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_init(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + u32 pch_i2cbc; + u32 pch_i2ctmr; + u32 reg_value; + + /* reset I2C controller */ + iowrite32(0x01, p + PCH_I2CSRST); + msleep(20); + iowrite32(0x0, p + PCH_I2CSRST); + + /* Initialize I2C registers */ + iowrite32(0x21, p + PCH_I2CNF); + + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_I2CCTL_I2CMEN); + + if (pch_i2c_speed != 400) + pch_i2c_speed = 100; + + reg_value = PCH_I2CCTL_I2CMEN; + if (pch_i2c_speed == FAST_MODE_CLK) { + reg_value |= FAST_MODE_EN; + pch_dbg(adap, "Fast mode enabled\n"); + } + + if (pch_clk > PCH_MAX_CLK) + pch_clk = 62500; + + pch_i2cbc = (pch_clk + (pch_i2c_speed * 4)) / (pch_i2c_speed * 8); + /* Set transfer speed in I2CBC */ + iowrite32(pch_i2cbc, p + PCH_I2CBC); + + pch_i2ctmr = (pch_clk) / 8; + iowrite32(pch_i2ctmr, p + PCH_I2CTMR); + + reg_value |= NORMAL_INTR_ENBL; /* Enable interrupts in normal mode */ + iowrite32(reg_value, p + PCH_I2CCTL); + + pch_dbg(adap, + "I2CCTL=%x pch_i2cbc=%x pch_i2ctmr=%x Enable interrupts\n", + ioread32(p + PCH_I2CCTL), pch_i2cbc, pch_i2ctmr); + + init_waitqueue_head(&pch_event); +} + +static inline bool ktime_lt(const ktime_t cmp1, const ktime_t cmp2) +{ + return cmp1.tv64 < cmp2.tv64; +} + +/** + * pch_i2c_wait_for_bus_idle() - check the status of bus. + * @adap: Pointer to struct i2c_algo_pch_data. + * @timeout: waiting time counter (us). + */ +static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap, + s32 timeout) +{ + void __iomem *p = adap->pch_base_address; + + /* MAX timeout value is timeout*1000*1000nsec */ + ktime_t ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000); + do { + if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0) + break; + msleep(20); + } while (ktime_lt(ktime_get(), ns_val)); + + pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR)); + + if (timeout == 0) { + pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME); + return -ETIME; + } + + return 0; +} + +/** + * pch_i2c_start() - Generate I2C start condition in normal mode. + * @adap: Pointer to struct i2c_algo_pch_data. + * + * Generate I2C start condition in normal mode by setting I2CCTL.I2CMSTA to 1. + */ +static void pch_i2c_start(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_START); +} + +/** + * pch_i2c_wait_for_xfer_complete() - initiates a wait for the tx complete event + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static s32 pch_i2c_wait_for_xfer_complete(struct i2c_algo_pch_data *adap) +{ + s32 ret; + ret = wait_event_timeout(pch_event, + (adap->pch_event_flag != 0), msecs_to_jiffies(50)); + if (ret < 0) { + pch_err(adap, "timeout: %x\n", adap->pch_event_flag); + return ret; + } + + if (ret == 0) { + pch_err(adap, "timeout: %x\n", adap->pch_event_flag); + return -ETIMEDOUT; + } + + if (adap->pch_event_flag & I2C_ERROR_MASK) { + pch_err(adap, "error bits set: %x\n", adap->pch_event_flag); + return -EIO; + } + + adap->pch_event_flag = 0; + + return 0; +} + +/** + * pch_i2c_getack() - to confirm ACK/NACK + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static s32 pch_i2c_getack(struct i2c_algo_pch_data *adap) +{ + u32 reg_val; + void __iomem *p = adap->pch_base_address; + reg_val = ioread32(p + PCH_I2CSR) & PCH_GETACK; + + if (reg_val != 0) { + pch_err(adap, "return%d\n", -EPROTO); + return -EPROTO; + } + + return 0; +} + +/** + * pch_i2c_stop() - generate stop condition in normal mode. + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_stop(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + /* clear the start bit */ + pch_clrbit(adap->pch_base_address, PCH_I2CCTL, PCH_START); +} + +/** + * pch_i2c_repstart() - generate repeated start condition in normal mode + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_repstart(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_REPSTART); +} + +/** + * pch_i2c_writebytes() - write data to I2C bus in normal mode + * @i2c_adap: Pointer to the struct i2c_adapter. + * @last: specifies whether last message or not. + * In the case of compound mode it will be 1 for last message, + * otherwise 0. + * @first: specifies whether first message or not. + * 1 for first message otherwise 0. + */ +static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, u32 last, u32 first) +{ + struct i2c_algo_pch_data *adap = i2c_adap->algo_data; + u8 *buf; + u32 length; + u32 addr; + u32 addr_2_msb; + u32 addr_8_lsb; + s32 wrcount; + void __iomem *p = adap->pch_base_address; + + length = msgs->len; + buf = msgs->buf; + addr = msgs->addr; + + /* enable master tx */ + pch_setbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE); + + pch_dbg(adap, "I2CCTL = %x msgs->len = %d\n", ioread32(p + PCH_I2CCTL), + length); + + if (first) { + if (pch_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME) + return -ETIME; + } + + if (msgs->flags & I2C_M_TEN) { + addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7); + iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); + if (first) + pch_i2c_start(adap); + if (pch_i2c_wait_for_xfer_complete(adap) == 0 && + pch_i2c_getack(adap) == 0) { + addr_8_lsb = (addr & I2C_ADDR_MSK); + iowrite32(addr_8_lsb, p + PCH_I2CDR); + } else { + pch_i2c_stop(adap); + return -ETIME; + } + } else { + /* set 7 bit slave address and R/W bit as 0 */ + iowrite32(addr << 1, p + PCH_I2CDR); + if (first) + pch_i2c_start(adap); + } + + if ((pch_i2c_wait_for_xfer_complete(adap) == 0) && + (pch_i2c_getack(adap) == 0)) { + for (wrcount = 0; wrcount < length; ++wrcount) { + /* write buffer value to I2C data register */ + iowrite32(buf[wrcount], p + PCH_I2CDR); + pch_dbg(adap, "writing %x to Data register\n", + buf[wrcount]); + + if (pch_i2c_wait_for_xfer_complete(adap) != 0) + return -ETIME; + + if (pch_i2c_getack(adap)) + return -EIO; + } + + /* check if this is the last message */ + if (last) + pch_i2c_stop(adap); + else + pch_i2c_repstart(adap); + } else { + pch_i2c_stop(adap); + return -EIO; + } + + pch_dbg(adap, "return=%d\n", wrcount); + + return wrcount; +} + +/** + * pch_i2c_sendack() - send ACK + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_sendack(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_clrbit(adap->pch_base_address, PCH_I2CCTL, PCH_ACK); +} + +/** + * pch_i2c_sendnack() - send NACK + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL)); + pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_ACK); +} + +/** + * pch_i2c_readbytes() - read data from I2C bus in normal mode. + * @i2c_adap: Pointer to the struct i2c_adapter. + * @msgs: Pointer to i2c_msg structure. + * @last: specifies whether last message or not. + * @first: specifies whether first message or not. + */ +static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + u32 last, u32 first) +{ + struct i2c_algo_pch_data *adap = i2c_adap->algo_data; + + u8 *buf; + u32 count; + u32 length; + u32 addr; + u32 addr_2_msb; + void __iomem *p = adap->pch_base_address; + + length = msgs->len; + buf = msgs->buf; + addr = msgs->addr; + + /* enable master reception */ + pch_clrbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE); + + if (first) { + if (pch_i2c_wait_for_bus_idle(adap, BUS_IDLE_TIMEOUT) == -ETIME) + return -ETIME; + } + + if (msgs->flags & I2C_M_TEN) { + addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD)); + iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); + + } else { + /* 7 address bits + R/W bit */ + addr = (((addr) << 1) | (I2C_RD)); + iowrite32(addr, p + PCH_I2CDR); + } + + /* check if it is the first message */ + if (first) + pch_i2c_start(adap); + + if ((pch_i2c_wait_for_xfer_complete(adap) == 0) && + (pch_i2c_getack(adap) == 0)) { + pch_dbg(adap, "return %d\n", 0); + + if (length == 0) { + pch_i2c_stop(adap); + ioread32(p + PCH_I2CDR); /* Dummy read needs */ + + count = length; + } else { + int read_index; + int loop; + pch_i2c_sendack(adap); + + /* Dummy read */ + for (loop = 1, read_index = 0; loop < length; loop++) { + buf[read_index] = ioread32(p + PCH_I2CDR); + + if (loop != 1) + read_index++; + + if (pch_i2c_wait_for_xfer_complete(adap) != 0) { + pch_i2c_stop(adap); + return -ETIME; + } + } /* end for */ + + pch_i2c_sendnack(adap); + + buf[read_index] = ioread32(p + PCH_I2CDR); + + if (length != 1) + read_index++; + + if (pch_i2c_wait_for_xfer_complete(adap) == 0) { + if (last) + pch_i2c_stop(adap); + else + pch_i2c_repstart(adap); + + buf[read_index++] = ioread32(p + PCH_I2CDR); + count = read_index; + } else { + count = -ETIME; + } + + } + } else { + count = -ETIME; + pch_i2c_stop(adap); + } + + return count; +} + +/** + * pch_i2c_cb() - Interrupt handler Call back function + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_cb(struct i2c_algo_pch_data *adap) +{ + u32 sts; + void __iomem *p = adap->pch_base_address; + + sts = ioread32(p + PCH_I2CSR); + sts &= (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT); + if (sts & I2CMAL_BIT) + adap->pch_event_flag |= I2CMAL_EVENT; + + if (sts & I2CMCF_BIT) + adap->pch_event_flag |= I2CMCF_EVENT; + + /* clear the applicable bits */ + pch_clrbit(adap->pch_base_address, PCH_I2CSR, sts); + + pch_dbg(adap, "PCH_I2CSR = %x\n", ioread32(p + PCH_I2CSR)); + + wake_up(&pch_event); +} + +/** + * pch_i2c_handler() - interrupt handler for the PCH I2C controller + * @irq: irq number. + * @pData: cookie passed back to the handler function. + */ +static irqreturn_t pch_i2c_handler(int irq, void *pData) +{ + u32 reg_val; + int flag; + int i; + struct adapter_info *adap_info = pData; + void __iomem *p; + u32 mode; + + for (i = 0, flag = 0; i < adap_info->ch_num; i++) { + p = adap_info->pch_data[i].pch_base_address; + mode = ioread32(p + PCH_I2CMOD); + mode &= BUFFER_MODE | EEPROM_SR_MODE; + if (mode != NORMAL_MODE) { + pch_err(adap_info->pch_data, + "I2C-%d mode(%d) is not supported\n", mode, i); + continue; + } + reg_val = ioread32(p + PCH_I2CSR); + if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT)) { + pch_i2c_cb(&adap_info->pch_data[i]); + flag = 1; + } + } + + return flag ? IRQ_HANDLED : IRQ_NONE; +} + +/** + * pch_i2c_xfer() - Reading adnd writing data through I2C bus + * @i2c_adap: Pointer to the struct i2c_adapter. + * @msgs: Pointer to i2c_msg structure. + * @num: number of messages. + */ +static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, s32 num) +{ + struct i2c_msg *pmsg; + u32 i = 0; + u32 status; + u32 msglen; + u32 subaddrlen; + s32 ret; + + struct i2c_algo_pch_data *adap = i2c_adap->algo_data; + + ret = mutex_lock_interruptible(&pch_mutex); + if (ret) + return -ERESTARTSYS; + + if (adap->p_adapter_info->pch_i2c_suspended) { + mutex_unlock(&pch_mutex); + return -EBUSY; + } + + pch_dbg(adap, "adap->p_adapter_info->pch_i2c_suspended is %d\n", + adap->p_adapter_info->pch_i2c_suspended); + /* transfer not completed */ + adap->pch_i2c_xfer_in_progress = true; + + pmsg = &msgs[0]; + pmsg->flags |= adap->pch_buff_mode_en; + status = pmsg->flags; + pch_dbg(adap, + "After invoking I2C_MODE_SEL :flag= 0x%x\n", status); + /* calculate sub address length and message length */ + /* these are applicable only for buffer mode */ + subaddrlen = pmsg->buf[0]; + /* calculate actual message length excluding + * the sub address fields */ + msglen = (pmsg->len) - (subaddrlen + 1); + if (status & (I2C_M_RD)) { + pch_dbg(adap, "invoking pch_i2c_readbytes\n"); + ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num), + (i == 0)); + } else { + pch_dbg(adap, "invoking pch_i2c_writebytes\n"); + ret = pch_i2c_writebytes(i2c_adap, pmsg, (i + 1 == num), + (i == 0)); + } + + adap->pch_i2c_xfer_in_progress = false; /* transfer completed */ + + mutex_unlock(&pch_mutex); + + return ret; +} + +/** + * pch_i2c_func() - return the functionality of the I2C driver + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static u32 pch_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + +static struct i2c_algorithm pch_algorithm = { + .master_xfer = pch_i2c_xfer, + .functionality = pch_i2c_func +}; + +/** + * pch_i2c_disbl_int() - Disable PCH I2C interrupts + * @adap: Pointer to struct i2c_algo_pch_data. + */ +static void pch_i2c_disbl_int(struct i2c_algo_pch_data *adap) +{ + void __iomem *p = adap->pch_base_address; + + pch_clrbit(adap->pch_base_address, PCH_I2CCTL, NORMAL_INTR_ENBL); + + iowrite32(EEPROM_RST_INTR_DISBL, p + PCH_I2CESRMSK); + + iowrite32(BUFFER_MODE_INTR_DISBL, p + PCH_I2CBUFMSK); +} + +static int __devinit pch_i2c_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem *base_addr; + int ret; + int i, j; + struct adapter_info *adap_info; + struct i2c_adapter *pch_adap; + + pch_pci_dbg(pdev, "Entered.\n"); + + adap_info = kzalloc((sizeof(struct adapter_info)), GFP_KERNEL); + if (adap_info == NULL) { + pch_pci_err(pdev, "Memory allocation FAILED\n"); + return -ENOMEM; + } + + ret = pci_enable_device(pdev); + if (ret) { + pch_pci_err(pdev, "pci_enable_device FAILED\n"); + goto err_pci_enable; + } + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) { + pch_pci_err(pdev, "pci_request_regions FAILED\n"); + goto err_pci_req; + } + + base_addr = pci_iomap(pdev, 1, 0); + + if (base_addr == NULL) { + pch_pci_err(pdev, "pci_iomap FAILED\n"); + ret = -ENOMEM; + goto err_pci_iomap; + } + + /* Set the number of I2C channel instance */ + adap_info->ch_num = id->driver_data; + + for (i = 0; i < adap_info->ch_num; i++) { + pch_adap = &adap_info->pch_data[i].pch_adapter; + adap_info->pch_i2c_suspended = false; + + adap_info->pch_data[i].p_adapter_info = adap_info; + + pch_adap->owner = THIS_MODULE; + pch_adap->class = I2C_CLASS_HWMON; + strcpy(pch_adap->name, KBUILD_MODNAME); + pch_adap->algo = &pch_algorithm; + pch_adap->algo_data = &adap_info->pch_data[i]; + + /* base_addr + offset; */ + adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i; + + pch_adap->dev.parent = &pdev->dev; + + ret = i2c_add_adapter(pch_adap); + if (ret) { + pch_pci_err(pdev, "i2c_add_adapter[ch:%d] FAILED\n", i); + goto err_i2c_add_adapter; + } + + pch_i2c_init(&adap_info->pch_data[i]); + } + ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED, + KBUILD_MODNAME, adap_info); + if (ret) { + pch_pci_err(pdev, "request_irq FAILED\n"); + goto err_i2c_add_adapter; + } + + pci_set_drvdata(pdev, adap_info); + pch_pci_dbg(pdev, "returns %d.\n", ret); + return 0; + +err_i2c_add_adapter: + for (j = 0; j < i; j++) + i2c_del_adapter(&adap_info->pch_data[j].pch_adapter); + pci_iounmap(pdev, base_addr); +err_pci_iomap: + pci_release_regions(pdev); +err_pci_req: + pci_disable_device(pdev); +err_pci_enable: + kfree(adap_info); + return ret; +} + +static void __devexit pch_i2c_remove(struct pci_dev *pdev) +{ + int i; + struct adapter_info *adap_info = pci_get_drvdata(pdev); + + free_irq(pdev->irq, adap_info); + + for (i = 0; i < adap_info->ch_num; i++) { + pch_i2c_disbl_int(&adap_info->pch_data[i]); + i2c_del_adapter(&adap_info->pch_data[i].pch_adapter); + } + + if (adap_info->pch_data[0].pch_base_address) + pci_iounmap(pdev, adap_info->pch_data[0].pch_base_address); + + for (i = 0; i < adap_info->ch_num; i++) + adap_info->pch_data[i].pch_base_address = 0; + + pci_set_drvdata(pdev, NULL); + + pci_release_regions(pdev); + + pci_disable_device(pdev); + kfree(adap_info); +} + +#ifdef CONFIG_PM +static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state) +{ + int ret; + int i; + struct adapter_info *adap_info = pci_get_drvdata(pdev); + void __iomem *p = adap_info->pch_data[0].pch_base_address; + + adap_info->pch_i2c_suspended = true; + + for (i = 0; i < adap_info->ch_num; i++) { + while ((adap_info->pch_data[i].pch_i2c_xfer_in_progress)) { + /* Wait until all channel transfers are completed */ + msleep(20); + } + } + + /* Disable the i2c interrupts */ + for (i = 0; i < adap_info->ch_num; i++) + pch_i2c_disbl_int(&adap_info->pch_data[i]); + + pch_pci_dbg(pdev, "I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x " + "invoked function pch_i2c_disbl_int successfully\n", + ioread32(p + PCH_I2CSR), ioread32(p + PCH_I2CBUFSTA), + ioread32(p + PCH_I2CESRSTA)); + + ret = pci_save_state(pdev); + + if (ret) { + pch_pci_err(pdev, "pci_save_state\n"); + return ret; + } + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int pch_i2c_resume(struct pci_dev *pdev) +{ + int i; + struct adapter_info *adap_info = pci_get_drvdata(pdev); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + if (pci_enable_device(pdev) < 0) { + pch_pci_err(pdev, "pch_i2c_resume:pci_enable_device FAILED\n"); + return -EIO; + } + + pci_enable_wake(pdev, PCI_D3hot, 0); + + for (i = 0; i < adap_info->ch_num; i++) + pch_i2c_init(&adap_info->pch_data[i]); + + adap_info->pch_i2c_suspended = false; + + return 0; +} +#else +#define pch_i2c_suspend NULL +#define pch_i2c_resume NULL +#endif + +static struct pci_driver pch_pcidriver = { + .name = KBUILD_MODNAME, + .id_table = pch_pcidev_id, + .probe = pch_i2c_probe, + .remove = __devexit_p(pch_i2c_remove), + .suspend = pch_i2c_suspend, + .resume = pch_i2c_resume +}; + +static int __init pch_pci_init(void) +{ + return pci_register_driver(&pch_pcidriver); +} +module_init(pch_pci_init); + +static void __exit pch_pci_exit(void) +{ + pci_unregister_driver(&pch_pcidriver); +} +module_exit(pch_pci_exit); + +MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH I2C Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomoya MORINAGA. <tomoya-linux@dsn.okisemi.com>"); +module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR)); +module_param(pch_clk, int, (S_IRUSR | S_IWUSR)); diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c new file mode 100644 index 00000000..37e2e82a --- /dev/null +++ b/drivers/i2c/busses/i2c-elektor.c @@ -0,0 +1,347 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even + Frodo Looijaard <frodol@dds.nl> */ + +/* Partially rewriten by Oleg I. Vdovikin for mmapped support of + for Alpha Processor Inc. UP-2000(+) boards */ + +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/wait.h> + +#include <linux/isa.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-pcf.h> +#include <linux/io.h> + +#include <asm/irq.h> + +#include "../algos/i2c-algo-pcf.h" + +#define DEFAULT_BASE 0x330 + +static int base; +static u8 __iomem *base_iomem; + +static int irq; +static int clock = 0x1c; +static int own = 0x55; +static int mmapped; + +/* vdovikin: removed static struct i2c_pcf_isa gpi; code - + this module in real supports only one device, due to missing arguments + in some functions, called from the algo-pcf module. Sometimes it's + need to be rewriten - but for now just remove this for simpler reading */ + +static wait_queue_head_t pcf_wait; +static int pcf_pending; +static spinlock_t lock; + +static struct i2c_adapter pcf_isa_ops; + +/* ----- local functions ---------------------------------------------- */ + +static void pcf_isa_setbyte(void *data, int ctl, int val) +{ + u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem; + + /* enable irq if any specified for serial operation */ + if (ctl && irq && (val & I2C_PCF_ESO)) { + val |= I2C_PCF_ENI; + } + + pr_debug("%s: Write %p 0x%02X\n", pcf_isa_ops.name, address, val); + iowrite8(val, address); +#ifdef __alpha__ + /* API UP2000 needs some hardware fudging to make the write stick */ + iowrite8(val, address); +#endif +} + +static int pcf_isa_getbyte(void *data, int ctl) +{ + u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem; + int val = ioread8(address); + + pr_debug("%s: Read %p 0x%02X\n", pcf_isa_ops.name, address, val); + return (val); +} + +static int pcf_isa_getown(void *data) +{ + return (own); +} + + +static int pcf_isa_getclock(void *data) +{ + return (clock); +} + +static void pcf_isa_waitforpin(void *data) +{ + DEFINE_WAIT(wait); + int timeout = 2; + unsigned long flags; + + if (irq > 0) { + spin_lock_irqsave(&lock, flags); + if (pcf_pending == 0) { + spin_unlock_irqrestore(&lock, flags); + prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE); + if (schedule_timeout(timeout*HZ)) { + spin_lock_irqsave(&lock, flags); + if (pcf_pending == 1) { + pcf_pending = 0; + } + spin_unlock_irqrestore(&lock, flags); + } + finish_wait(&pcf_wait, &wait); + } else { + pcf_pending = 0; + spin_unlock_irqrestore(&lock, flags); + } + } else { + udelay(100); + } +} + + +static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id) { + spin_lock(&lock); + pcf_pending = 1; + spin_unlock(&lock); + wake_up_interruptible(&pcf_wait); + return IRQ_HANDLED; +} + + +static int pcf_isa_init(void) +{ + spin_lock_init(&lock); + if (!mmapped) { + if (!request_region(base, 2, pcf_isa_ops.name)) { + printk(KERN_ERR "%s: requested I/O region (%#x:2) is " + "in use\n", pcf_isa_ops.name, base); + return -ENODEV; + } + base_iomem = ioport_map(base, 2); + if (!base_iomem) { + printk(KERN_ERR "%s: remap of I/O region %#x failed\n", + pcf_isa_ops.name, base); + release_region(base, 2); + return -ENODEV; + } + } else { + if (!request_mem_region(base, 2, pcf_isa_ops.name)) { + printk(KERN_ERR "%s: requested memory region (%#x:2) " + "is in use\n", pcf_isa_ops.name, base); + return -ENODEV; + } + base_iomem = ioremap(base, 2); + if (base_iomem == NULL) { + printk(KERN_ERR "%s: remap of memory region %#x " + "failed\n", pcf_isa_ops.name, base); + release_mem_region(base, 2); + return -ENODEV; + } + } + pr_debug("%s: registers %#x remapped to %p\n", pcf_isa_ops.name, base, + base_iomem); + + if (irq > 0) { + if (request_irq(irq, pcf_isa_handler, 0, pcf_isa_ops.name, + NULL) < 0) { + printk(KERN_ERR "%s: Request irq%d failed\n", + pcf_isa_ops.name, irq); + irq = 0; + } else + enable_irq(irq); + } + return 0; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_pcf_data pcf_isa_data = { + .setpcf = pcf_isa_setbyte, + .getpcf = pcf_isa_getbyte, + .getown = pcf_isa_getown, + .getclock = pcf_isa_getclock, + .waitforpin = pcf_isa_waitforpin, +}; + +static struct i2c_adapter pcf_isa_ops = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo_data = &pcf_isa_data, + .name = "i2c-elektor", +}; + +static int __devinit elektor_match(struct device *dev, unsigned int id) +{ +#ifdef __alpha__ + /* check to see we have memory mapped PCF8584 connected to the + Cypress cy82c693 PCI-ISA bridge as on UP2000 board */ + if (base == 0) { + struct pci_dev *cy693_dev; + + cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ, + PCI_DEVICE_ID_CONTAQ_82C693, NULL); + if (cy693_dev) { + unsigned char config; + /* yeap, we've found cypress, let's check config */ + if (!pci_read_config_byte(cy693_dev, 0x47, &config)) { + + dev_dbg(dev, "found cy82c693, config " + "register 0x47 = 0x%02x\n", config); + + /* UP2000 board has this register set to 0xe1, + but the most significant bit as seems can be + reset during the proper initialisation + sequence if guys from API decides to do that + (so, we can even enable Tsunami Pchip + window for the upper 1 Gb) */ + + /* so just check for ROMCS at 0xe0000, + ROMCS enabled for writes + and external XD Bus buffer in use. */ + if ((config & 0x7f) == 0x61) { + /* seems to be UP2000 like board */ + base = 0xe0000; + mmapped = 1; + /* UP2000 drives ISA with + 8.25 MHz (PCI/4) clock + (this can be read from cypress) */ + clock = I2C_PCF_CLK | I2C_PCF_TRNS90; + dev_info(dev, "found API UP2000 like " + "board, will probe PCF8584 " + "later\n"); + } + } + pci_dev_put(cy693_dev); + } + } +#endif + + /* sanity checks for mmapped I/O */ + if (mmapped && base < 0xc8000) { + dev_err(dev, "incorrect base address (%#x) specified " + "for mmapped I/O\n", base); + return 0; + } + + if (base == 0) { + base = DEFAULT_BASE; + } + return 1; +} + +static int __devinit elektor_probe(struct device *dev, unsigned int id) +{ + init_waitqueue_head(&pcf_wait); + if (pcf_isa_init()) + return -ENODEV; + pcf_isa_ops.dev.parent = dev; + if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) + goto fail; + + dev_info(dev, "found device at %#x\n", base); + + return 0; + + fail: + if (irq > 0) { + disable_irq(irq); + free_irq(irq, NULL); + } + + if (!mmapped) { + ioport_unmap(base_iomem); + release_region(base, 2); + } else { + iounmap(base_iomem); + release_mem_region(base, 2); + } + return -ENODEV; +} + +static int __devexit elektor_remove(struct device *dev, unsigned int id) +{ + i2c_del_adapter(&pcf_isa_ops); + + if (irq > 0) { + disable_irq(irq); + free_irq(irq, NULL); + } + + if (!mmapped) { + ioport_unmap(base_iomem); + release_region(base, 2); + } else { + iounmap(base_iomem); + release_mem_region(base, 2); + } + + return 0; +} + +static struct isa_driver i2c_elektor_driver = { + .match = elektor_match, + .probe = elektor_probe, + .remove = __devexit_p(elektor_remove), + .driver = { + .owner = THIS_MODULE, + .name = "i2c-elektor", + }, +}; + +static int __init i2c_pcfisa_init(void) +{ + return isa_register_driver(&i2c_elektor_driver, 1); +} + +static void __exit i2c_pcfisa_exit(void) +{ + isa_unregister_driver(&i2c_elektor_driver); +} + +MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); +MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); +MODULE_LICENSE("GPL"); + +module_param(base, int, 0); +module_param(irq, int, 0); +module_param(clock, int, 0); +module_param(own, int, 0); +module_param(mmapped, int, 0); + +module_init(i2c_pcfisa_init); +module_exit(i2c_pcfisa_exit); diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c new file mode 100644 index 00000000..a651779d --- /dev/null +++ b/drivers/i2c/busses/i2c-gpio.c @@ -0,0 +1,225 @@ +/* + * Bitbanging I2C bus driver using the GPIO API + * + * Copyright (C) 2007 Atmel Corporation + * + * 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. + */ +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/i2c-gpio.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +#include <asm/gpio.h> + +/* Toggle SDA by changing the direction of the pin */ +static void i2c_gpio_setsda_dir(void *data, int state) +{ + struct i2c_gpio_platform_data *pdata = data; + + if (state) + gpio_direction_input(pdata->sda_pin); + else + gpio_direction_output(pdata->sda_pin, 0); +} + +/* + * Toggle SDA by changing the output value of the pin. This is only + * valid for pins configured as open drain (i.e. setting the value + * high effectively turns off the output driver.) + */ +static void i2c_gpio_setsda_val(void *data, int state) +{ + struct i2c_gpio_platform_data *pdata = data; + + gpio_set_value(pdata->sda_pin, state); +} + +/* Toggle SCL by changing the direction of the pin. */ +static void i2c_gpio_setscl_dir(void *data, int state) +{ + struct i2c_gpio_platform_data *pdata = data; + + if (state) + gpio_direction_input(pdata->scl_pin); + else + gpio_direction_output(pdata->scl_pin, 0); +} + +/* + * Toggle SCL by changing the output value of the pin. This is used + * for pins that are configured as open drain and for output-only + * pins. The latter case will break the i2c protocol, but it will + * often work in practice. + */ +static void i2c_gpio_setscl_val(void *data, int state) +{ + struct i2c_gpio_platform_data *pdata = data; + + gpio_set_value(pdata->scl_pin, state); +} + +static int i2c_gpio_getsda(void *data) +{ + struct i2c_gpio_platform_data *pdata = data; + + return gpio_get_value(pdata->sda_pin); +} + +static int i2c_gpio_getscl(void *data) +{ + struct i2c_gpio_platform_data *pdata = data; + + return gpio_get_value(pdata->scl_pin); +} + +static int __devinit i2c_gpio_probe(struct platform_device *pdev) +{ + struct i2c_gpio_platform_data *pdata; + struct i2c_algo_bit_data *bit_data; + struct i2c_adapter *adap; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) + return -ENXIO; + + ret = -ENOMEM; + adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (!adap) + goto err_alloc_adap; + bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL); + if (!bit_data) + goto err_alloc_bit_data; + + ret = gpio_request(pdata->sda_pin, "sda"); + if (ret) + goto err_request_sda; + ret = gpio_request(pdata->scl_pin, "scl"); + if (ret) + goto err_request_scl; + + if (pdata->sda_is_open_drain) { + gpio_direction_output(pdata->sda_pin, 1); + bit_data->setsda = i2c_gpio_setsda_val; + } else { + gpio_direction_input(pdata->sda_pin); + bit_data->setsda = i2c_gpio_setsda_dir; + } + + if (pdata->scl_is_open_drain || pdata->scl_is_output_only) { + gpio_direction_output(pdata->scl_pin, 1); + bit_data->setscl = i2c_gpio_setscl_val; + } else { + gpio_direction_input(pdata->scl_pin); + bit_data->setscl = i2c_gpio_setscl_dir; + } + + if (!pdata->scl_is_output_only) + bit_data->getscl = i2c_gpio_getscl; + bit_data->getsda = i2c_gpio_getsda; + + if (pdata->udelay) + bit_data->udelay = pdata->udelay; + else if (pdata->scl_is_output_only) + bit_data->udelay = 50; /* 10 kHz */ + else + bit_data->udelay = 5; /* 100 kHz */ + + if (pdata->timeout) + bit_data->timeout = pdata->timeout; + else + bit_data->timeout = HZ / 10; /* 100 ms */ + + bit_data->data = pdata; + + adap->owner = THIS_MODULE; + snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); + adap->algo_data = bit_data; + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->dev.parent = &pdev->dev; + + /* + * If "dev->id" is negative we consider it as zero. + * The reason to do so is to avoid sysfs names that only make + * sense when there are multiple adapters. + */ + adap->nr = (pdev->id != -1) ? pdev->id : 0; + ret = i2c_bit_add_numbered_bus(adap); + if (ret) + goto err_add_bus; + + platform_set_drvdata(pdev, adap); + + dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n", + pdata->sda_pin, pdata->scl_pin, + pdata->scl_is_output_only + ? ", no clock stretching" : ""); + + return 0; + +err_add_bus: + gpio_free(pdata->scl_pin); +err_request_scl: + gpio_free(pdata->sda_pin); +err_request_sda: + kfree(bit_data); +err_alloc_bit_data: + kfree(adap); +err_alloc_adap: + return ret; +} + +static int __devexit i2c_gpio_remove(struct platform_device *pdev) +{ + struct i2c_gpio_platform_data *pdata; + struct i2c_adapter *adap; + + adap = platform_get_drvdata(pdev); + pdata = pdev->dev.platform_data; + + i2c_del_adapter(adap); + gpio_free(pdata->scl_pin); + gpio_free(pdata->sda_pin); + kfree(adap->algo_data); + kfree(adap); + + return 0; +} + +static struct platform_driver i2c_gpio_driver = { + .driver = { + .name = "i2c-gpio", + .owner = THIS_MODULE, + }, + .probe = i2c_gpio_probe, + .remove = __devexit_p(i2c_gpio_remove), +}; + +static int __init i2c_gpio_init(void) +{ + int ret; + + ret = platform_driver_register(&i2c_gpio_driver); + if (ret) + printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret); + + return ret; +} +subsys_initcall(i2c_gpio_init); + +static void __exit i2c_gpio_exit(void) +{ + platform_driver_unregister(&i2c_gpio_driver); +} +module_exit(i2c_gpio_exit); + +MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); +MODULE_DESCRIPTION("Platform-independent bitbanging I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-gpio"); diff --git a/drivers/i2c/busses/i2c-highlander.c b/drivers/i2c/busses/i2c-highlander.c new file mode 100644 index 00000000..3df1bc80 --- /dev/null +++ b/drivers/i2c/busses/i2c-highlander.c @@ -0,0 +1,498 @@ +/* + * Renesas Solutions Highlander FPGA I2C/SMBus support. + * + * Supported devices: R0P7780LC0011RL, R0P7785LC0011RL + * + * Copyright (C) 2008 Paul Mundt + * Copyright (C) 2008 Renesas Solutions Corp. + * Copyright (C) 2008 Atom Create Engineering Co., Ltd. + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file "COPYING" in the main directory + * of this archive for more details. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#define SMCR 0x00 +#define SMCR_START (1 << 0) +#define SMCR_IRIC (1 << 1) +#define SMCR_BBSY (1 << 2) +#define SMCR_ACKE (1 << 3) +#define SMCR_RST (1 << 4) +#define SMCR_IEIC (1 << 6) + +#define SMSMADR 0x02 + +#define SMMR 0x04 +#define SMMR_MODE0 (1 << 0) +#define SMMR_MODE1 (1 << 1) +#define SMMR_CAP (1 << 3) +#define SMMR_TMMD (1 << 4) +#define SMMR_SP (1 << 7) + +#define SMSADR 0x06 +#define SMTRDR 0x46 + +struct highlander_i2c_dev { + struct device *dev; + void __iomem *base; + struct i2c_adapter adapter; + struct completion cmd_complete; + unsigned long last_read_time; + int irq; + u8 *buf; + size_t buf_len; +}; + +static int iic_force_poll, iic_force_normal; +static int iic_timeout = 1000, iic_read_delay; + +static inline void highlander_i2c_irq_enable(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_IEIC, dev->base + SMCR); +} + +static inline void highlander_i2c_irq_disable(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) & ~SMCR_IEIC, dev->base + SMCR); +} + +static inline void highlander_i2c_start(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_START, dev->base + SMCR); +} + +static inline void highlander_i2c_done(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_IRIC, dev->base + SMCR); +} + +static void highlander_i2c_setup(struct highlander_i2c_dev *dev) +{ + u16 smmr; + + smmr = ioread16(dev->base + SMMR); + smmr |= SMMR_TMMD; + + if (iic_force_normal) + smmr &= ~SMMR_SP; + else + smmr |= SMMR_SP; + + iowrite16(smmr, dev->base + SMMR); +} + +static void smbus_write_data(u8 *src, u16 *dst, int len) +{ + for (; len > 1; len -= 2) { + *dst++ = be16_to_cpup((__be16 *)src); + src += 2; + } + + if (len) + *dst = *src << 8; +} + +static void smbus_read_data(u16 *src, u8 *dst, int len) +{ + for (; len > 1; len -= 2) { + *(__be16 *)dst = cpu_to_be16p(src++); + dst += 2; + } + + if (len) + *dst = *src >> 8; +} + +static void highlander_i2c_command(struct highlander_i2c_dev *dev, + u8 command, int len) +{ + unsigned int i; + u16 cmd = (command << 8) | command; + + for (i = 0; i < len; i += 2) { + if (len - i == 1) + cmd = command << 8; + iowrite16(cmd, dev->base + SMSADR + i); + dev_dbg(dev->dev, "command data[%x] 0x%04x\n", i/2, cmd); + } +} + +static int highlander_i2c_wait_for_bbsy(struct highlander_i2c_dev *dev) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(iic_timeout); + while (ioread16(dev->base + SMCR) & SMCR_BBSY) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + + msleep(1); + } + + return 0; +} + +static int highlander_i2c_reset(struct highlander_i2c_dev *dev) +{ + iowrite16(ioread16(dev->base + SMCR) | SMCR_RST, dev->base + SMCR); + return highlander_i2c_wait_for_bbsy(dev); +} + +static int highlander_i2c_wait_for_ack(struct highlander_i2c_dev *dev) +{ + u16 tmp = ioread16(dev->base + SMCR); + + if ((tmp & (SMCR_IRIC | SMCR_ACKE)) == SMCR_ACKE) { + dev_warn(dev->dev, "ack abnormality\n"); + return highlander_i2c_reset(dev); + } + + return 0; +} + +static irqreturn_t highlander_i2c_irq(int irq, void *dev_id) +{ + struct highlander_i2c_dev *dev = dev_id; + + highlander_i2c_done(dev); + complete(&dev->cmd_complete); + + return IRQ_HANDLED; +} + +static void highlander_i2c_poll(struct highlander_i2c_dev *dev) +{ + unsigned long timeout; + u16 smcr; + + timeout = jiffies + msecs_to_jiffies(iic_timeout); + for (;;) { + smcr = ioread16(dev->base + SMCR); + + /* + * Don't bother checking ACKE here, this and the reset + * are handled in highlander_i2c_wait_xfer_done() when + * waiting for the ACK. + */ + + if (smcr & SMCR_IRIC) + return; + if (time_after(jiffies, timeout)) + break; + + cpu_relax(); + cond_resched(); + } + + dev_err(dev->dev, "polling timed out\n"); +} + +static inline int highlander_i2c_wait_xfer_done(struct highlander_i2c_dev *dev) +{ + if (dev->irq) + wait_for_completion_timeout(&dev->cmd_complete, + msecs_to_jiffies(iic_timeout)); + else + /* busy looping, the IRQ of champions */ + highlander_i2c_poll(dev); + + return highlander_i2c_wait_for_ack(dev); +} + +static int highlander_i2c_read(struct highlander_i2c_dev *dev) +{ + int i, cnt; + u16 data[16]; + + if (highlander_i2c_wait_for_bbsy(dev)) + return -EAGAIN; + + highlander_i2c_start(dev); + + if (highlander_i2c_wait_xfer_done(dev)) { + dev_err(dev->dev, "Arbitration loss\n"); + return -EAGAIN; + } + + /* + * The R0P7780LC0011RL FPGA needs a significant delay between + * data read cycles, otherwise the transciever gets confused and + * garbage is returned when the read is subsequently aborted. + * + * It is not sufficient to wait for BBSY. + * + * While this generally only applies to the older SH7780-based + * Highlanders, the same issue can be observed on SH7785 ones, + * albeit less frequently. SH7780-based Highlanders may need + * this to be as high as 1000 ms. + */ + if (iic_read_delay && time_before(jiffies, dev->last_read_time + + msecs_to_jiffies(iic_read_delay))) + msleep(jiffies_to_msecs((dev->last_read_time + + msecs_to_jiffies(iic_read_delay)) - jiffies)); + + cnt = (dev->buf_len + 1) >> 1; + for (i = 0; i < cnt; i++) { + data[i] = ioread16(dev->base + SMTRDR + (i * sizeof(u16))); + dev_dbg(dev->dev, "read data[%x] 0x%04x\n", i, data[i]); + } + + smbus_read_data(data, dev->buf, dev->buf_len); + + dev->last_read_time = jiffies; + + return 0; +} + +static int highlander_i2c_write(struct highlander_i2c_dev *dev) +{ + int i, cnt; + u16 data[16]; + + smbus_write_data(dev->buf, data, dev->buf_len); + + cnt = (dev->buf_len + 1) >> 1; + for (i = 0; i < cnt; i++) { + iowrite16(data[i], dev->base + SMTRDR + (i * sizeof(u16))); + dev_dbg(dev->dev, "write data[%x] 0x%04x\n", i, data[i]); + } + + if (highlander_i2c_wait_for_bbsy(dev)) + return -EAGAIN; + + highlander_i2c_start(dev); + + return highlander_i2c_wait_xfer_done(dev); +} + +static int highlander_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + struct highlander_i2c_dev *dev = i2c_get_adapdata(adap); + u16 tmp; + + init_completion(&dev->cmd_complete); + + dev_dbg(dev->dev, "addr %04x, command %02x, read_write %d, size %d\n", + addr, command, read_write, size); + + /* + * Set up the buffer and transfer size + */ + switch (size) { + case I2C_SMBUS_BYTE_DATA: + dev->buf = &data->byte; + dev->buf_len = 1; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + dev->buf = &data->block[1]; + dev->buf_len = data->block[0]; + break; + default: + dev_err(dev->dev, "unsupported command %d\n", size); + return -EINVAL; + } + + /* + * Encode the mode setting + */ + tmp = ioread16(dev->base + SMMR); + tmp &= ~(SMMR_MODE0 | SMMR_MODE1); + + switch (dev->buf_len) { + case 1: + /* default */ + break; + case 8: + tmp |= SMMR_MODE0; + break; + case 16: + tmp |= SMMR_MODE1; + break; + case 32: + tmp |= (SMMR_MODE0 | SMMR_MODE1); + break; + default: + dev_err(dev->dev, "unsupported xfer size %d\n", dev->buf_len); + return -EINVAL; + } + + iowrite16(tmp, dev->base + SMMR); + + /* Ensure we're in a sane state */ + highlander_i2c_done(dev); + + /* Set slave address */ + iowrite16((addr << 1) | read_write, dev->base + SMSMADR); + + highlander_i2c_command(dev, command, dev->buf_len); + + if (read_write == I2C_SMBUS_READ) + return highlander_i2c_read(dev); + else + return highlander_i2c_write(dev); +} + +static u32 highlander_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm highlander_i2c_algo = { + .smbus_xfer = highlander_i2c_smbus_xfer, + .functionality = highlander_i2c_func, +}; + +static int __devinit highlander_i2c_probe(struct platform_device *pdev) +{ + struct highlander_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "no mem resource\n"); + return -ENODEV; + } + + dev = kzalloc(sizeof(struct highlander_i2c_dev), GFP_KERNEL); + if (unlikely(!dev)) + return -ENOMEM; + + dev->base = ioremap_nocache(res->start, resource_size(res)); + if (unlikely(!dev->base)) { + ret = -ENXIO; + goto err; + } + + dev->dev = &pdev->dev; + platform_set_drvdata(pdev, dev); + + dev->irq = platform_get_irq(pdev, 0); + if (iic_force_poll) + dev->irq = 0; + + if (dev->irq) { + ret = request_irq(dev->irq, highlander_i2c_irq, IRQF_DISABLED, + pdev->name, dev); + if (unlikely(ret)) + goto err_unmap; + + highlander_i2c_irq_enable(dev); + } else { + dev_notice(&pdev->dev, "no IRQ, using polling mode\n"); + highlander_i2c_irq_disable(dev); + } + + dev->last_read_time = jiffies; /* initial read jiffies */ + + highlander_i2c_setup(dev); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "HL FPGA I2C adapter", sizeof(adap->name)); + adap->algo = &highlander_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->nr = pdev->id; + + /* + * Reset the adapter + */ + ret = highlander_i2c_reset(dev); + if (unlikely(ret)) { + dev_err(&pdev->dev, "controller didn't come up\n"); + goto err_free_irq; + } + + ret = i2c_add_numbered_adapter(adap); + if (unlikely(ret)) { + dev_err(&pdev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + if (dev->irq) + free_irq(dev->irq, dev); +err_unmap: + iounmap(dev->base); +err: + kfree(dev); + + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int __devexit highlander_i2c_remove(struct platform_device *pdev) +{ + struct highlander_i2c_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adapter); + + if (dev->irq) + free_irq(dev->irq, dev); + + iounmap(dev->base); + kfree(dev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver highlander_i2c_driver = { + .driver = { + .name = "i2c-highlander", + .owner = THIS_MODULE, + }, + + .probe = highlander_i2c_probe, + .remove = __devexit_p(highlander_i2c_remove), +}; + +static int __init highlander_i2c_init(void) +{ + return platform_driver_register(&highlander_i2c_driver); +} + +static void __exit highlander_i2c_exit(void) +{ + platform_driver_unregister(&highlander_i2c_driver); +} + +module_init(highlander_i2c_init); +module_exit(highlander_i2c_exit); + +MODULE_AUTHOR("Paul Mundt"); +MODULE_DESCRIPTION("Renesas Highlander FPGA I2C/SMBus adapter"); +MODULE_LICENSE("GPL v2"); + +module_param(iic_force_poll, bool, 0); +module_param(iic_force_normal, bool, 0); +module_param(iic_timeout, int, 0); +module_param(iic_read_delay, int, 0); + +MODULE_PARM_DESC(iic_force_poll, "Force polling mode"); +MODULE_PARM_DESC(iic_force_normal, + "Force normal mode (100 kHz), default is fast mode (400 kHz)"); +MODULE_PARM_DESC(iic_timeout, "Set timeout value in msecs (default 1000 ms)"); +MODULE_PARM_DESC(iic_read_delay, + "Delay between data read cycles (default 0 ms)"); diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c new file mode 100644 index 00000000..9ff1695d --- /dev/null +++ b/drivers/i2c/busses/i2c-hydra.c @@ -0,0 +1,178 @@ +/* + i2c Support for the Apple `Hydra' Mac I/O + + Copyright (c) 1999-2004 Geert Uytterhoeven <geert@linux-m68k.org> + + Based on i2c Support for Via Technologies 82C586B South Bridge + Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi> + + 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/pci.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/init.h> +#include <linux/io.h> +#include <asm/hydra.h> + + +#define HYDRA_CPD_PD0 0x00000001 /* CachePD lines */ +#define HYDRA_CPD_PD1 0x00000002 +#define HYDRA_CPD_PD2 0x00000004 +#define HYDRA_CPD_PD3 0x00000008 + +#define HYDRA_SCLK HYDRA_CPD_PD0 +#define HYDRA_SDAT HYDRA_CPD_PD1 +#define HYDRA_SCLK_OE 0x00000010 +#define HYDRA_SDAT_OE 0x00000020 + +static inline void pdregw(void *data, u32 val) +{ + struct Hydra *hydra = (struct Hydra *)data; + writel(val, &hydra->CachePD); +} + +static inline u32 pdregr(void *data) +{ + struct Hydra *hydra = (struct Hydra *)data; + return readl(&hydra->CachePD); +} + +static void hydra_bit_setscl(void *data, int state) +{ + u32 val = pdregr(data); + if (state) + val &= ~HYDRA_SCLK_OE; + else { + val &= ~HYDRA_SCLK; + val |= HYDRA_SCLK_OE; + } + pdregw(data, val); +} + +static void hydra_bit_setsda(void *data, int state) +{ + u32 val = pdregr(data); + if (state) + val &= ~HYDRA_SDAT_OE; + else { + val &= ~HYDRA_SDAT; + val |= HYDRA_SDAT_OE; + } + pdregw(data, val); +} + +static int hydra_bit_getscl(void *data) +{ + return (pdregr(data) & HYDRA_SCLK) != 0; +} + +static int hydra_bit_getsda(void *data) +{ + return (pdregr(data) & HYDRA_SDAT) != 0; +} + +/* ------------------------------------------------------------------------ */ + +static struct i2c_algo_bit_data hydra_bit_data = { + .setsda = hydra_bit_setsda, + .setscl = hydra_bit_setscl, + .getsda = hydra_bit_getsda, + .getscl = hydra_bit_getscl, + .udelay = 5, + .timeout = HZ +}; + +static struct i2c_adapter hydra_adap = { + .owner = THIS_MODULE, + .name = "Hydra i2c", + .algo_data = &hydra_bit_data, +}; + +static const struct pci_device_id hydra_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_HYDRA) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, hydra_ids); + +static int __devinit hydra_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + unsigned long base = pci_resource_start(dev, 0); + int res; + + if (!request_mem_region(base+offsetof(struct Hydra, CachePD), 4, + hydra_adap.name)) + return -EBUSY; + + hydra_bit_data.data = pci_ioremap_bar(dev, 0); + if (hydra_bit_data.data == NULL) { + release_mem_region(base+offsetof(struct Hydra, CachePD), 4); + return -ENODEV; + } + + pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */ + hydra_adap.dev.parent = &dev->dev; + res = i2c_bit_add_bus(&hydra_adap); + if (res < 0) { + iounmap(hydra_bit_data.data); + release_mem_region(base+offsetof(struct Hydra, CachePD), 4); + return res; + } + return 0; +} + +static void __devexit hydra_remove(struct pci_dev *dev) +{ + pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */ + i2c_del_adapter(&hydra_adap); + iounmap(hydra_bit_data.data); + release_mem_region(pci_resource_start(dev, 0)+ + offsetof(struct Hydra, CachePD), 4); +} + + +static struct pci_driver hydra_driver = { + .name = "hydra_smbus", + .id_table = hydra_ids, + .probe = hydra_probe, + .remove = __devexit_p(hydra_remove), +}; + +static int __init i2c_hydra_init(void) +{ + return pci_register_driver(&hydra_driver); +} + + +static void __exit i2c_hydra_exit(void) +{ + pci_unregister_driver(&hydra_driver); +} + + + +MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>"); +MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O"); +MODULE_LICENSE("GPL"); + +module_init(i2c_hydra_init); +module_exit(i2c_hydra_exit); + diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c new file mode 100644 index 00000000..ab26840d --- /dev/null +++ b/drivers/i2c/busses/i2c-i801.c @@ -0,0 +1,946 @@ +/* + Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, + Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker + <mdsxyz123@yahoo.com> + Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org> + Copyright (C) 2010 Intel Corporation, + David Woodhouse <dwmw2@infradead.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. +*/ + +/* + Supports the following Intel I/O Controller Hubs (ICH): + + I/O Block I2C + region SMBus Block proc. block + Chip name PCI ID size PEC buffer call read + ---------------------------------------------------------------------- + 82801AA (ICH) 0x2413 16 no no no no + 82801AB (ICH0) 0x2423 16 no no no no + 82801BA (ICH2) 0x2443 16 no no no no + 82801CA (ICH3) 0x2483 32 soft no no no + 82801DB (ICH4) 0x24c3 32 hard yes no no + 82801E (ICH5) 0x24d3 32 hard yes yes yes + 6300ESB 0x25a4 32 hard yes yes yes + 82801F (ICH6) 0x266a 32 hard yes yes yes + 6310ESB/6320ESB 0x269b 32 hard yes yes yes + 82801G (ICH7) 0x27da 32 hard yes yes yes + 82801H (ICH8) 0x283e 32 hard yes yes yes + 82801I (ICH9) 0x2930 32 hard yes yes yes + EP80579 (Tolapai) 0x5032 32 hard yes yes yes + ICH10 0x3a30 32 hard yes yes yes + ICH10 0x3a60 32 hard yes yes yes + 5/3400 Series (PCH) 0x3b30 32 hard yes yes yes + 6 Series (PCH) 0x1c22 32 hard yes yes yes + Patsburg (PCH) 0x1d22 32 hard yes yes yes + Patsburg (PCH) IDF 0x1d70 32 hard yes yes yes + Patsburg (PCH) IDF 0x1d71 32 hard yes yes yes + Patsburg (PCH) IDF 0x1d72 32 hard yes yes yes + DH89xxCC (PCH) 0x2330 32 hard yes yes yes + Panther Point (PCH) 0x1e22 32 hard yes yes yes + + Features supported by this driver: + Software PEC no + Hardware PEC yes + Block buffer yes + Block process call transaction no + I2C block read transaction yes (doesn't use the block buffer) + Slave mode no + + See the file Documentation/i2c/busses/i2c-i801 for details. +*/ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/acpi.h> +#include <linux/io.h> +#include <linux/dmi.h> +#include <linux/slab.h> + +/* I801 SMBus address offsets */ +#define SMBHSTSTS(p) (0 + (p)->smba) +#define SMBHSTCNT(p) (2 + (p)->smba) +#define SMBHSTCMD(p) (3 + (p)->smba) +#define SMBHSTADD(p) (4 + (p)->smba) +#define SMBHSTDAT0(p) (5 + (p)->smba) +#define SMBHSTDAT1(p) (6 + (p)->smba) +#define SMBBLKDAT(p) (7 + (p)->smba) +#define SMBPEC(p) (8 + (p)->smba) /* ICH3 and later */ +#define SMBAUXSTS(p) (12 + (p)->smba) /* ICH4 and later */ +#define SMBAUXCTL(p) (13 + (p)->smba) /* ICH4 and later */ + +/* PCI Address Constants */ +#define SMBBAR 4 +#define SMBHSTCFG 0x040 + +/* Host configuration bits for SMBHSTCFG */ +#define SMBHSTCFG_HST_EN 1 +#define SMBHSTCFG_SMB_SMI_EN 2 +#define SMBHSTCFG_I2C_EN 4 + +/* Auxiliary control register bits, ICH4+ only */ +#define SMBAUXCTL_CRC 1 +#define SMBAUXCTL_E32B 2 + +/* kill bit for SMBHSTCNT */ +#define SMBHSTCNT_KILL 2 + +/* Other settings */ +#define MAX_TIMEOUT 100 +#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ + +/* I801 command constants */ +#define I801_QUICK 0x00 +#define I801_BYTE 0x04 +#define I801_BYTE_DATA 0x08 +#define I801_WORD_DATA 0x0C +#define I801_PROC_CALL 0x10 /* unimplemented */ +#define I801_BLOCK_DATA 0x14 +#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */ +#define I801_BLOCK_LAST 0x34 +#define I801_I2C_BLOCK_LAST 0x38 /* ICH5 and later */ +#define I801_START 0x40 +#define I801_PEC_EN 0x80 /* ICH3 and later */ + +/* I801 Hosts Status register bits */ +#define SMBHSTSTS_BYTE_DONE 0x80 +#define SMBHSTSTS_INUSE_STS 0x40 +#define SMBHSTSTS_SMBALERT_STS 0x20 +#define SMBHSTSTS_FAILED 0x10 +#define SMBHSTSTS_BUS_ERR 0x08 +#define SMBHSTSTS_DEV_ERR 0x04 +#define SMBHSTSTS_INTR 0x02 +#define SMBHSTSTS_HOST_BUSY 0x01 + +#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | \ + SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | \ + SMBHSTSTS_INTR) + +/* Older devices have their ID defined in <linux/pci_ids.h> */ +#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22 +/* Patsburg also has three 'Integrated Device Function' SMBus controllers */ +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0 0x1d70 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71 +#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72 +#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22 +#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 +#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 + +struct i801_priv { + struct i2c_adapter adapter; + unsigned long smba; + unsigned char original_hstcfg; + struct pci_dev *pci_dev; + unsigned int features; +}; + +static struct pci_driver i801_driver; + +#define FEATURE_SMBUS_PEC (1 << 0) +#define FEATURE_BLOCK_BUFFER (1 << 1) +#define FEATURE_BLOCK_PROC (1 << 2) +#define FEATURE_I2C_BLOCK_READ (1 << 3) +/* Not really a feature, but it's convenient to handle it as such */ +#define FEATURE_IDF (1 << 15) + +static const char *i801_feature_names[] = { + "SMBus PEC", + "Block buffer", + "Block process call", + "I2C block read", +}; + +static unsigned int disable_features; +module_param(disable_features, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(disable_features, "Disable selected driver features"); + +/* Make sure the SMBus host is ready to start transmitting. + Return 0 if it is, -EBUSY if it is not. */ +static int i801_check_pre(struct i801_priv *priv) +{ + int status; + + status = inb_p(SMBHSTSTS(priv)); + if (status & SMBHSTSTS_HOST_BUSY) { + dev_err(&priv->pci_dev->dev, "SMBus is busy, can't use it!\n"); + return -EBUSY; + } + + status &= STATUS_FLAGS; + if (status) { + dev_dbg(&priv->pci_dev->dev, "Clearing status flags (%02x)\n", + status); + outb_p(status, SMBHSTSTS(priv)); + status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS; + if (status) { + dev_err(&priv->pci_dev->dev, + "Failed clearing status flags (%02x)\n", + status); + return -EBUSY; + } + } + + return 0; +} + +/* Convert the status register to an error code, and clear it. */ +static int i801_check_post(struct i801_priv *priv, int status, int timeout) +{ + int result = 0; + + /* If the SMBus is still busy, we give up */ + if (timeout) { + dev_err(&priv->pci_dev->dev, "Transaction timeout\n"); + /* try to stop the current command */ + dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n"); + outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL, + SMBHSTCNT(priv)); + msleep(1); + outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL), + SMBHSTCNT(priv)); + + /* Check if it worked */ + status = inb_p(SMBHSTSTS(priv)); + if ((status & SMBHSTSTS_HOST_BUSY) || + !(status & SMBHSTSTS_FAILED)) + dev_err(&priv->pci_dev->dev, + "Failed terminating the transaction\n"); + outb_p(STATUS_FLAGS, SMBHSTSTS(priv)); + return -ETIMEDOUT; + } + + if (status & SMBHSTSTS_FAILED) { + result = -EIO; + dev_err(&priv->pci_dev->dev, "Transaction failed\n"); + } + if (status & SMBHSTSTS_DEV_ERR) { + result = -ENXIO; + dev_dbg(&priv->pci_dev->dev, "No response\n"); + } + if (status & SMBHSTSTS_BUS_ERR) { + result = -EAGAIN; + dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n"); + } + + if (result) { + /* Clear error flags */ + outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv)); + status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS; + if (status) { + dev_warn(&priv->pci_dev->dev, "Failed clearing status " + "flags at end of transaction (%02x)\n", + status); + } + } + + return result; +} + +static int i801_transaction(struct i801_priv *priv, int xact) +{ + int status; + int result; + int timeout = 0; + + result = i801_check_pre(priv); + if (result < 0) + return result; + + /* the current contents of SMBHSTCNT can be overwritten, since PEC, + * INTREN, SMBSCMD are passed in xact */ + outb_p(xact | I801_START, SMBHSTCNT(priv)); + + /* We will always wait for a fraction of a second! */ + do { + msleep(1); + status = inb_p(SMBHSTSTS(priv)); + } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT)); + + result = i801_check_post(priv, status, timeout > MAX_TIMEOUT); + if (result < 0) + return result; + + outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); + return 0; +} + +/* wait for INTR bit as advised by Intel */ +static void i801_wait_hwpec(struct i801_priv *priv) +{ + int timeout = 0; + int status; + + do { + msleep(1); + status = inb_p(SMBHSTSTS(priv)); + } while ((!(status & SMBHSTSTS_INTR)) + && (timeout++ < MAX_TIMEOUT)); + + if (timeout > MAX_TIMEOUT) + dev_dbg(&priv->pci_dev->dev, "PEC Timeout!\n"); + + outb_p(status, SMBHSTSTS(priv)); +} + +static int i801_block_transaction_by_block(struct i801_priv *priv, + union i2c_smbus_data *data, + char read_write, int hwpec) +{ + int i, len; + int status; + + inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */ + + /* Use 32-byte buffer to process this transaction */ + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + outb_p(len, SMBHSTDAT0(priv)); + for (i = 0; i < len; i++) + outb_p(data->block[i+1], SMBBLKDAT(priv)); + } + + status = i801_transaction(priv, I801_BLOCK_DATA | ENABLE_INT9 | + I801_PEC_EN * hwpec); + if (status) + return status; + + if (read_write == I2C_SMBUS_READ) { + len = inb_p(SMBHSTDAT0(priv)); + if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + + data->block[0] = len; + for (i = 0; i < len; i++) + data->block[i + 1] = inb_p(SMBBLKDAT(priv)); + } + return 0; +} + +static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, + union i2c_smbus_data *data, + char read_write, int command, + int hwpec) +{ + int i, len; + int smbcmd; + int status; + int result; + int timeout; + + result = i801_check_pre(priv); + if (result < 0) + return result; + + len = data->block[0]; + + if (read_write == I2C_SMBUS_WRITE) { + outb_p(len, SMBHSTDAT0(priv)); + outb_p(data->block[1], SMBBLKDAT(priv)); + } + + for (i = 1; i <= len; i++) { + if (i == len && read_write == I2C_SMBUS_READ) { + if (command == I2C_SMBUS_I2C_BLOCK_DATA) + smbcmd = I801_I2C_BLOCK_LAST; + else + smbcmd = I801_BLOCK_LAST; + } else { + if (command == I2C_SMBUS_I2C_BLOCK_DATA + && read_write == I2C_SMBUS_READ) + smbcmd = I801_I2C_BLOCK_DATA; + else + smbcmd = I801_BLOCK_DATA; + } + outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT(priv)); + + if (i == 1) + outb_p(inb(SMBHSTCNT(priv)) | I801_START, + SMBHSTCNT(priv)); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + msleep(1); + status = inb_p(SMBHSTSTS(priv)); + } while ((!(status & SMBHSTSTS_BYTE_DONE)) + && (timeout++ < MAX_TIMEOUT)); + + result = i801_check_post(priv, status, timeout > MAX_TIMEOUT); + if (result < 0) + return result; + + if (i == 1 && read_write == I2C_SMBUS_READ + && command != I2C_SMBUS_I2C_BLOCK_DATA) { + len = inb_p(SMBHSTDAT0(priv)); + if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { + dev_err(&priv->pci_dev->dev, + "Illegal SMBus block read size %d\n", + len); + /* Recover */ + while (inb_p(SMBHSTSTS(priv)) & + SMBHSTSTS_HOST_BUSY) + outb_p(SMBHSTSTS_BYTE_DONE, + SMBHSTSTS(priv)); + outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); + return -EPROTO; + } + data->block[0] = len; + } + + /* Retrieve/store value in SMBBLKDAT */ + if (read_write == I2C_SMBUS_READ) + data->block[i] = inb_p(SMBBLKDAT(priv)); + if (read_write == I2C_SMBUS_WRITE && i+1 <= len) + outb_p(data->block[i+1], SMBBLKDAT(priv)); + + /* signals SMBBLKDAT ready */ + outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS(priv)); + } + + return 0; +} + +static int i801_set_block_buffer_mode(struct i801_priv *priv) +{ + outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv)); + if ((inb_p(SMBAUXCTL(priv)) & SMBAUXCTL_E32B) == 0) + return -EIO; + return 0; +} + +/* Block transaction function */ +static int i801_block_transaction(struct i801_priv *priv, + union i2c_smbus_data *data, char read_write, + int command, int hwpec) +{ + int result = 0; + unsigned char hostc; + + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + if (read_write == I2C_SMBUS_WRITE) { + /* set I2C_EN bit in configuration register */ + pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc); + pci_write_config_byte(priv->pci_dev, SMBHSTCFG, + hostc | SMBHSTCFG_I2C_EN); + } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) { + dev_err(&priv->pci_dev->dev, + "I2C block read is unsupported!\n"); + return -EOPNOTSUPP; + } + } + + if (read_write == I2C_SMBUS_WRITE + || command == I2C_SMBUS_I2C_BLOCK_DATA) { + if (data->block[0] < 1) + data->block[0] = 1; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) + data->block[0] = I2C_SMBUS_BLOCK_MAX; + } else { + data->block[0] = 32; /* max for SMBus block reads */ + } + + /* Experience has shown that the block buffer can only be used for + SMBus (not I2C) block transactions, even though the datasheet + doesn't mention this limitation. */ + if ((priv->features & FEATURE_BLOCK_BUFFER) + && command != I2C_SMBUS_I2C_BLOCK_DATA + && i801_set_block_buffer_mode(priv) == 0) + result = i801_block_transaction_by_block(priv, data, + read_write, hwpec); + else + result = i801_block_transaction_byte_by_byte(priv, data, + read_write, + command, hwpec); + + if (result == 0 && hwpec) + i801_wait_hwpec(priv); + + if (command == I2C_SMBUS_I2C_BLOCK_DATA + && read_write == I2C_SMBUS_WRITE) { + /* restore saved configuration register value */ + pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc); + } + return result; +} + +/* Return negative errno on error. */ +static s32 i801_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int hwpec; + int block = 0; + int ret, xact = 0; + struct i801_priv *priv = i2c_get_adapdata(adap); + + hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC) + && size != I2C_SMBUS_QUICK + && size != I2C_SMBUS_I2C_BLOCK_DATA; + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD(priv)); + xact = I801_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD(priv)); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD(priv)); + xact = I801_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD(priv)); + outb_p(command, SMBHSTCMD(priv)); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0(priv)); + xact = I801_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD(priv)); + outb_p(command, SMBHSTCMD(priv)); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0(priv)); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv)); + } + xact = I801_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD(priv)); + outb_p(command, SMBHSTCMD(priv)); + block = 1; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + /* NB: page 240 of ICH5 datasheet shows that the R/#W + * bit should be cleared here, even when reading */ + outb_p((addr & 0x7f) << 1, SMBHSTADD(priv)); + if (read_write == I2C_SMBUS_READ) { + /* NB: page 240 of ICH5 datasheet also shows + * that DATA1 is the cmd field when reading */ + outb_p(command, SMBHSTDAT1(priv)); + } else + outb_p(command, SMBHSTCMD(priv)); + block = 1; + break; + default: + dev_err(&priv->pci_dev->dev, "Unsupported transaction %d\n", + size); + return -EOPNOTSUPP; + } + + if (hwpec) /* enable/disable hardware PEC */ + outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv)); + else + outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC), + SMBAUXCTL(priv)); + + if (block) + ret = i801_block_transaction(priv, data, read_write, size, + hwpec); + else + ret = i801_transaction(priv, xact | ENABLE_INT9); + + /* Some BIOSes don't like it when PEC is enabled at reboot or resume + time, so we forcibly disable it after every transaction. Turn off + E32B for the same reason. */ + if (hwpec || block) + outb_p(inb_p(SMBAUXCTL(priv)) & + ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); + + if (block) + return ret; + if (ret) + return ret; + if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) + return 0; + + switch (xact & 0x7f) { + case I801_BYTE: /* Result put in SMBHSTDAT0 */ + case I801_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0(priv)); + break; + case I801_WORD_DATA: + data->word = inb_p(SMBHSTDAT0(priv)) + + (inb_p(SMBHSTDAT1(priv)) << 8); + break; + } + return 0; +} + + +static u32 i801_func(struct i2c_adapter *adapter) +{ + struct i801_priv *priv = i2c_get_adapdata(adapter); + + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | + ((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) | + ((priv->features & FEATURE_I2C_BLOCK_READ) ? + I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0); +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = i801_access, + .functionality = i801_func, +}; + +static const struct pci_device_id i801_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_4) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_16) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_17) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_17) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_5) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_6) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EP80579_1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_4) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_5) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, i801_ids); + +#if defined CONFIG_X86 && defined CONFIG_DMI +static unsigned char apanel_addr; + +/* Scan the system ROM for the signature "FJKEYINF" */ +static __init const void __iomem *bios_signature(const void __iomem *bios) +{ + ssize_t offset; + const unsigned char signature[] = "FJKEYINF"; + + for (offset = 0; offset < 0x10000; offset += 0x10) { + if (check_signature(bios + offset, signature, + sizeof(signature)-1)) + return bios + offset; + } + return NULL; +} + +static void __init input_apanel_init(void) +{ + void __iomem *bios; + const void __iomem *p; + + bios = ioremap(0xF0000, 0x10000); /* Can't fail */ + p = bios_signature(bios); + if (p) { + /* just use the first address */ + apanel_addr = readb(p + 8 + 3) >> 1; + } + iounmap(bios); +} + +struct dmi_onboard_device_info { + const char *name; + u8 type; + unsigned short i2c_addr; + const char *i2c_type; +}; + +static struct dmi_onboard_device_info __devinitdata dmi_devices[] = { + { "Syleus", DMI_DEV_TYPE_OTHER, 0x73, "fscsyl" }, + { "Hermes", DMI_DEV_TYPE_OTHER, 0x73, "fscher" }, + { "Hades", DMI_DEV_TYPE_OTHER, 0x73, "fschds" }, +}; + +static void __devinit dmi_check_onboard_device(u8 type, const char *name, + struct i2c_adapter *adap) +{ + int i; + struct i2c_board_info info; + + for (i = 0; i < ARRAY_SIZE(dmi_devices); i++) { + /* & ~0x80, ignore enabled/disabled bit */ + if ((type & ~0x80) != dmi_devices[i].type) + continue; + if (strcasecmp(name, dmi_devices[i].name)) + continue; + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = dmi_devices[i].i2c_addr; + strlcpy(info.type, dmi_devices[i].i2c_type, I2C_NAME_SIZE); + i2c_new_device(adap, &info); + break; + } +} + +/* We use our own function to check for onboard devices instead of + dmi_find_device() as some buggy BIOS's have the devices we are interested + in marked as disabled */ +static void __devinit dmi_check_onboard_devices(const struct dmi_header *dm, + void *adap) +{ + int i, count; + + if (dm->type != 10) + return; + + count = (dm->length - sizeof(struct dmi_header)) / 2; + for (i = 0; i < count; i++) { + const u8 *d = (char *)(dm + 1) + (i * 2); + const char *name = ((char *) dm) + dm->length; + u8 type = d[0]; + u8 s = d[1]; + + if (!s) + continue; + s--; + while (s > 0 && name[0]) { + name += strlen(name) + 1; + s--; + } + if (name[0] == 0) /* Bogus string reference */ + continue; + + dmi_check_onboard_device(type, name, adap); + } +} + +/* Register optional slaves */ +static void __devinit i801_probe_optional_slaves(struct i801_priv *priv) +{ + /* Only register slaves on main SMBus channel */ + if (priv->features & FEATURE_IDF) + return; + + if (apanel_addr) { + struct i2c_board_info info; + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = apanel_addr; + strlcpy(info.type, "fujitsu_apanel", I2C_NAME_SIZE); + i2c_new_device(&priv->adapter, &info); + } + + if (dmi_name_in_vendors("FUJITSU")) + dmi_walk(dmi_check_onboard_devices, &priv->adapter); +} +#else +static void __init input_apanel_init(void) {} +static void __devinit i801_probe_optional_slaves(struct i801_priv *priv) {} +#endif /* CONFIG_X86 && CONFIG_DMI */ + +static int __devinit i801_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + unsigned char temp; + int err, i; + struct i801_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_adapdata(&priv->adapter, priv); + priv->adapter.owner = THIS_MODULE; + priv->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + priv->adapter.algo = &smbus_algorithm; + + priv->pci_dev = dev; + switch (dev->device) { + case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0: + case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1: + case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2: + priv->features |= FEATURE_IDF; + /* fall through */ + default: + priv->features |= FEATURE_I2C_BLOCK_READ; + /* fall through */ + case PCI_DEVICE_ID_INTEL_82801DB_3: + priv->features |= FEATURE_SMBUS_PEC; + priv->features |= FEATURE_BLOCK_BUFFER; + /* fall through */ + case PCI_DEVICE_ID_INTEL_82801CA_3: + case PCI_DEVICE_ID_INTEL_82801BA_2: + case PCI_DEVICE_ID_INTEL_82801AB_3: + case PCI_DEVICE_ID_INTEL_82801AA_3: + break; + } + + /* Disable features on user request */ + for (i = 0; i < ARRAY_SIZE(i801_feature_names); i++) { + if (priv->features & disable_features & (1 << i)) + dev_notice(&dev->dev, "%s disabled by user\n", + i801_feature_names[i]); + } + priv->features &= ~disable_features; + + err = pci_enable_device(dev); + if (err) { + dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n", + err); + goto exit; + } + + /* Determine the address of the SMBus area */ + priv->smba = pci_resource_start(dev, SMBBAR); + if (!priv->smba) { + dev_err(&dev->dev, "SMBus base address uninitialized, " + "upgrade BIOS\n"); + err = -ENODEV; + goto exit; + } + + err = acpi_check_resource_conflict(&dev->resource[SMBBAR]); + if (err) { + err = -ENODEV; + goto exit; + } + + err = pci_request_region(dev, SMBBAR, i801_driver.name); + if (err) { + dev_err(&dev->dev, "Failed to request SMBus region " + "0x%lx-0x%Lx\n", priv->smba, + (unsigned long long)pci_resource_end(dev, SMBBAR)); + goto exit; + } + + pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &temp); + priv->original_hstcfg = temp; + temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ + if (!(temp & SMBHSTCFG_HST_EN)) { + dev_info(&dev->dev, "Enabling SMBus device\n"); + temp |= SMBHSTCFG_HST_EN; + } + pci_write_config_byte(priv->pci_dev, SMBHSTCFG, temp); + + if (temp & SMBHSTCFG_SMB_SMI_EN) + dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n"); + else + dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n"); + + /* Clear special mode bits */ + if (priv->features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER)) + outb_p(inb_p(SMBAUXCTL(priv)) & + ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); + + /* set up the sysfs linkage to our parent device */ + priv->adapter.dev.parent = &dev->dev; + + /* Retry up to 3 times on lost arbitration */ + priv->adapter.retries = 3; + + snprintf(priv->adapter.name, sizeof(priv->adapter.name), + "SMBus I801 adapter at %04lx", priv->smba); + err = i2c_add_adapter(&priv->adapter); + if (err) { + dev_err(&dev->dev, "Failed to add SMBus adapter\n"); + goto exit_release; + } + + i801_probe_optional_slaves(priv); + + pci_set_drvdata(dev, priv); + return 0; + +exit_release: + pci_release_region(dev, SMBBAR); +exit: + kfree(priv); + return err; +} + +static void __devexit i801_remove(struct pci_dev *dev) +{ + struct i801_priv *priv = pci_get_drvdata(dev); + + i2c_del_adapter(&priv->adapter); + pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); + pci_release_region(dev, SMBBAR); + pci_set_drvdata(dev, NULL); + kfree(priv); + /* + * do not call pci_disable_device(dev) since it can cause hard hangs on + * some systems during power-off (eg. Fujitsu-Siemens Lifebook E8010) + */ +} + +#ifdef CONFIG_PM +static int i801_suspend(struct pci_dev *dev, pm_message_t mesg) +{ + struct i801_priv *priv = pci_get_drvdata(dev); + + pci_save_state(dev); + pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); + pci_set_power_state(dev, pci_choose_state(dev, mesg)); + return 0; +} + +static int i801_resume(struct pci_dev *dev) +{ + pci_set_power_state(dev, PCI_D0); + pci_restore_state(dev); + return pci_enable_device(dev); +} +#else +#define i801_suspend NULL +#define i801_resume NULL +#endif + +static struct pci_driver i801_driver = { + .name = "i801_smbus", + .id_table = i801_ids, + .probe = i801_probe, + .remove = __devexit_p(i801_remove), + .suspend = i801_suspend, + .resume = i801_resume, +}; + +static int __init i2c_i801_init(void) +{ + if (dmi_name_in_vendors("FUJITSU")) + input_apanel_init(); + return pci_register_driver(&i801_driver); +} + +static void __exit i2c_i801_exit(void) +{ + pci_unregister_driver(&i801_driver); +} + +MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>, " + "Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("I801 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_i801_init); +module_exit(i2c_i801_exit); diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c new file mode 100644 index 00000000..3c110fbc --- /dev/null +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -0,0 +1,829 @@ +/* + * drivers/i2c/busses/i2c-ibm_iic.c + * + * Support for the IIC peripheral on IBM PPC 4xx + * + * Copyright (c) 2003, 2004 Zultys Technologies. + * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> + * + * Copyright (c) 2008 PIKA Technologies + * Sean MacLennan <smaclennan@pikatech.com> + * + * Based on original work by + * Ian DaSilva <idasilva@mvista.com> + * Armin Kuster <akuster@mvista.com> + * Matt Porter <mporter@mvista.com> + * + * Copyright 2000-2003 MontaVista Software Inc. + * + * Original driver version was highly leveraged from i2c-elektor.c + * + * Copyright 1995-97 Simon G. Vogl + * 1998-99 Hans Berglund + * + * With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> + * and even Frodo Looijaard <frodol@dds.nl> + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <asm/irq.h> +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/of_platform.h> +#include <linux/of_i2c.h> + +#include "i2c-ibm_iic.h" + +#define DRIVER_VERSION "2.2" + +MODULE_DESCRIPTION("IBM IIC driver v" DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +static int iic_force_poll; +module_param(iic_force_poll, bool, 0); +MODULE_PARM_DESC(iic_force_poll, "Force polling mode"); + +static int iic_force_fast; +module_param(iic_force_fast, bool, 0); +MODULE_PARM_DESC(iic_force_fast, "Force fast mode (400 kHz)"); + +#define DBG_LEVEL 0 + +#ifdef DBG +#undef DBG +#endif + +#ifdef DBG2 +#undef DBG2 +#endif + +#if DBG_LEVEL > 0 +# define DBG(f,x...) printk(KERN_DEBUG "ibm-iic" f, ##x) +#else +# define DBG(f,x...) ((void)0) +#endif +#if DBG_LEVEL > 1 +# define DBG2(f,x...) DBG(f, ##x) +#else +# define DBG2(f,x...) ((void)0) +#endif +#if DBG_LEVEL > 2 +static void dump_iic_regs(const char* header, struct ibm_iic_private* dev) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + printk(KERN_DEBUG "ibm-iic%d: %s\n", dev->idx, header); + printk(KERN_DEBUG + " cntl = 0x%02x, mdcntl = 0x%02x\n" + " sts = 0x%02x, extsts = 0x%02x\n" + " clkdiv = 0x%02x, xfrcnt = 0x%02x\n" + " xtcntlss = 0x%02x, directcntl = 0x%02x\n", + in_8(&iic->cntl), in_8(&iic->mdcntl), in_8(&iic->sts), + in_8(&iic->extsts), in_8(&iic->clkdiv), in_8(&iic->xfrcnt), + in_8(&iic->xtcntlss), in_8(&iic->directcntl)); +} +# define DUMP_REGS(h,dev) dump_iic_regs((h),(dev)) +#else +# define DUMP_REGS(h,dev) ((void)0) +#endif + +/* Bus timings (in ns) for bit-banging */ +static struct i2c_timings { + unsigned int hd_sta; + unsigned int su_sto; + unsigned int low; + unsigned int high; + unsigned int buf; +} timings [] = { +/* Standard mode (100 KHz) */ +{ + .hd_sta = 4000, + .su_sto = 4000, + .low = 4700, + .high = 4000, + .buf = 4700, +}, +/* Fast mode (400 KHz) */ +{ + .hd_sta = 600, + .su_sto = 600, + .low = 1300, + .high = 600, + .buf = 1300, +}}; + +/* Enable/disable interrupt generation */ +static inline void iic_interrupt_mode(struct ibm_iic_private* dev, int enable) +{ + out_8(&dev->vaddr->intmsk, enable ? INTRMSK_EIMTC : 0); +} + +/* + * Initialize IIC interface. + */ +static void iic_dev_init(struct ibm_iic_private* dev) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + + DBG("%d: init\n", dev->idx); + + /* Clear master address */ + out_8(&iic->lmadr, 0); + out_8(&iic->hmadr, 0); + + /* Clear slave address */ + out_8(&iic->lsadr, 0); + out_8(&iic->hsadr, 0); + + /* Clear status & extended status */ + out_8(&iic->sts, STS_SCMP | STS_IRQA); + out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD | EXTSTS_LA + | EXTSTS_ICT | EXTSTS_XFRA); + + /* Set clock divider */ + out_8(&iic->clkdiv, dev->clckdiv); + + /* Clear transfer count */ + out_8(&iic->xfrcnt, 0); + + /* Clear extended control and status */ + out_8(&iic->xtcntlss, XTCNTLSS_SRC | XTCNTLSS_SRS | XTCNTLSS_SWC + | XTCNTLSS_SWS); + + /* Clear control register */ + out_8(&iic->cntl, 0); + + /* Enable interrupts if possible */ + iic_interrupt_mode(dev, dev->irq >= 0); + + /* Set mode control */ + out_8(&iic->mdcntl, MDCNTL_FMDB | MDCNTL_EINT | MDCNTL_EUBS + | (dev->fast_mode ? MDCNTL_FSM : 0)); + + DUMP_REGS("iic_init", dev); +} + +/* + * Reset IIC interface + */ +static void iic_dev_reset(struct ibm_iic_private* dev) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + int i; + u8 dc; + + DBG("%d: soft reset\n", dev->idx); + DUMP_REGS("reset", dev); + + /* Place chip in the reset state */ + out_8(&iic->xtcntlss, XTCNTLSS_SRST); + + /* Check if bus is free */ + dc = in_8(&iic->directcntl); + if (!DIRCTNL_FREE(dc)){ + DBG("%d: trying to regain bus control\n", dev->idx); + + /* Try to set bus free state */ + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + + /* Wait until we regain bus control */ + for (i = 0; i < 100; ++i){ + dc = in_8(&iic->directcntl); + if (DIRCTNL_FREE(dc)) + break; + + /* Toggle SCL line */ + dc ^= DIRCNTL_SCC; + out_8(&iic->directcntl, dc); + udelay(10); + dc ^= DIRCNTL_SCC; + out_8(&iic->directcntl, dc); + + /* be nice */ + cond_resched(); + } + } + + /* Remove reset */ + out_8(&iic->xtcntlss, 0); + + /* Reinitialize interface */ + iic_dev_init(dev); +} + +/* + * Do 0-length transaction using bit-banging through IIC_DIRECTCNTL register. + */ + +/* Wait for SCL and/or SDA to be high */ +static int iic_dc_wait(volatile struct iic_regs __iomem *iic, u8 mask) +{ + unsigned long x = jiffies + HZ / 28 + 2; + while ((in_8(&iic->directcntl) & mask) != mask){ + if (unlikely(time_after(jiffies, x))) + return -1; + cond_resched(); + } + return 0; +} + +static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + const struct i2c_timings* t = &timings[dev->fast_mode ? 1 : 0]; + u8 mask, v, sda; + int i, res; + + /* Only 7-bit addresses are supported */ + if (unlikely(p->flags & I2C_M_TEN)){ + DBG("%d: smbus_quick - 10 bit addresses are not supported\n", + dev->idx); + return -EINVAL; + } + + DBG("%d: smbus_quick(0x%02x)\n", dev->idx, p->addr); + + /* Reset IIC interface */ + out_8(&iic->xtcntlss, XTCNTLSS_SRST); + + /* Wait for bus to become free */ + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + if (unlikely(iic_dc_wait(iic, DIRCNTL_MSDA | DIRCNTL_MSC))) + goto err; + ndelay(t->buf); + + /* START */ + out_8(&iic->directcntl, DIRCNTL_SCC); + sda = 0; + ndelay(t->hd_sta); + + /* Send address */ + v = (u8)((p->addr << 1) | ((p->flags & I2C_M_RD) ? 1 : 0)); + for (i = 0, mask = 0x80; i < 8; ++i, mask >>= 1){ + out_8(&iic->directcntl, sda); + ndelay(t->low / 2); + sda = (v & mask) ? DIRCNTL_SDAC : 0; + out_8(&iic->directcntl, sda); + ndelay(t->low / 2); + + out_8(&iic->directcntl, DIRCNTL_SCC | sda); + if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC))) + goto err; + ndelay(t->high); + } + + /* ACK */ + out_8(&iic->directcntl, sda); + ndelay(t->low / 2); + out_8(&iic->directcntl, DIRCNTL_SDAC); + ndelay(t->low / 2); + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC))) + goto err; + res = (in_8(&iic->directcntl) & DIRCNTL_MSDA) ? -EREMOTEIO : 1; + ndelay(t->high); + + /* STOP */ + out_8(&iic->directcntl, 0); + ndelay(t->low); + out_8(&iic->directcntl, DIRCNTL_SCC); + if (unlikely(iic_dc_wait(iic, DIRCNTL_MSC))) + goto err; + ndelay(t->su_sto); + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + + ndelay(t->buf); + + DBG("%d: smbus_quick -> %s\n", dev->idx, res ? "NACK" : "ACK"); +out: + /* Remove reset */ + out_8(&iic->xtcntlss, 0); + + /* Reinitialize interface */ + iic_dev_init(dev); + + return res; +err: + DBG("%d: smbus_quick - bus is stuck\n", dev->idx); + res = -EREMOTEIO; + goto out; +} + +/* + * IIC interrupt handler + */ +static irqreturn_t iic_handler(int irq, void *dev_id) +{ + struct ibm_iic_private* dev = (struct ibm_iic_private*)dev_id; + volatile struct iic_regs __iomem *iic = dev->vaddr; + + DBG2("%d: irq handler, STS = 0x%02x, EXTSTS = 0x%02x\n", + dev->idx, in_8(&iic->sts), in_8(&iic->extsts)); + + /* Acknowledge IRQ and wakeup iic_wait_for_tc */ + out_8(&iic->sts, STS_IRQA | STS_SCMP); + wake_up_interruptible(&dev->wq); + + return IRQ_HANDLED; +} + +/* + * Get master transfer result and clear errors if any. + * Returns the number of actually transferred bytes or error (<0) + */ +static int iic_xfer_result(struct ibm_iic_private* dev) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + + if (unlikely(in_8(&iic->sts) & STS_ERR)){ + DBG("%d: xfer error, EXTSTS = 0x%02x\n", dev->idx, + in_8(&iic->extsts)); + + /* Clear errors and possible pending IRQs */ + out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD | + EXTSTS_LA | EXTSTS_ICT | EXTSTS_XFRA); + + /* Flush master data buffer */ + out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB); + + /* Is bus free? + * If error happened during combined xfer + * IIC interface is usually stuck in some strange + * state, the only way out - soft reset. + */ + if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ + DBG("%d: bus is stuck, resetting\n", dev->idx); + iic_dev_reset(dev); + } + return -EREMOTEIO; + } + else + return in_8(&iic->xfrcnt) & XFRCNT_MTC_MASK; +} + +/* + * Try to abort active transfer. + */ +static void iic_abort_xfer(struct ibm_iic_private* dev) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + unsigned long x; + + DBG("%d: iic_abort_xfer\n", dev->idx); + + out_8(&iic->cntl, CNTL_HMT); + + /* + * Wait for the abort command to complete. + * It's not worth to be optimized, just poll (timeout >= 1 tick) + */ + x = jiffies + 2; + while ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ + if (time_after(jiffies, x)){ + DBG("%d: abort timeout, resetting...\n", dev->idx); + iic_dev_reset(dev); + return; + } + schedule(); + } + + /* Just to clear errors */ + iic_xfer_result(dev); +} + +/* + * Wait for master transfer to complete. + * It puts current process to sleep until we get interrupt or timeout expires. + * Returns the number of transferred bytes or error (<0) + */ +static int iic_wait_for_tc(struct ibm_iic_private* dev){ + + volatile struct iic_regs __iomem *iic = dev->vaddr; + int ret = 0; + + if (dev->irq >= 0){ + /* Interrupt mode */ + ret = wait_event_interruptible_timeout(dev->wq, + !(in_8(&iic->sts) & STS_PT), dev->adap.timeout); + + if (unlikely(ret < 0)) + DBG("%d: wait interrupted\n", dev->idx); + else if (unlikely(in_8(&iic->sts) & STS_PT)){ + DBG("%d: wait timeout\n", dev->idx); + ret = -ETIMEDOUT; + } + } + else { + /* Polling mode */ + unsigned long x = jiffies + dev->adap.timeout; + + while (in_8(&iic->sts) & STS_PT){ + if (unlikely(time_after(jiffies, x))){ + DBG("%d: poll timeout\n", dev->idx); + ret = -ETIMEDOUT; + break; + } + + if (unlikely(signal_pending(current))){ + DBG("%d: poll interrupted\n", dev->idx); + ret = -ERESTARTSYS; + break; + } + schedule(); + } + } + + if (unlikely(ret < 0)) + iic_abort_xfer(dev); + else + ret = iic_xfer_result(dev); + + DBG2("%d: iic_wait_for_tc -> %d\n", dev->idx, ret); + + return ret; +} + +/* + * Low level master transfer routine + */ +static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm, + int combined_xfer) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + char* buf = pm->buf; + int i, j, loops, ret = 0; + int len = pm->len; + + u8 cntl = (in_8(&iic->cntl) & CNTL_AMD) | CNTL_PT; + if (pm->flags & I2C_M_RD) + cntl |= CNTL_RW; + + loops = (len + 3) / 4; + for (i = 0; i < loops; ++i, len -= 4){ + int count = len > 4 ? 4 : len; + u8 cmd = cntl | ((count - 1) << CNTL_TCT_SHIFT); + + if (!(cntl & CNTL_RW)) + for (j = 0; j < count; ++j) + out_8((void __iomem *)&iic->mdbuf, *buf++); + + if (i < loops - 1) + cmd |= CNTL_CHT; + else if (combined_xfer) + cmd |= CNTL_RPST; + + DBG2("%d: xfer_bytes, %d, CNTL = 0x%02x\n", dev->idx, count, cmd); + + /* Start transfer */ + out_8(&iic->cntl, cmd); + + /* Wait for completion */ + ret = iic_wait_for_tc(dev); + + if (unlikely(ret < 0)) + break; + else if (unlikely(ret != count)){ + DBG("%d: xfer_bytes, requested %d, transferred %d\n", + dev->idx, count, ret); + + /* If it's not a last part of xfer, abort it */ + if (combined_xfer || (i < loops - 1)) + iic_abort_xfer(dev); + + ret = -EREMOTEIO; + break; + } + + if (cntl & CNTL_RW) + for (j = 0; j < count; ++j) + *buf++ = in_8((void __iomem *)&iic->mdbuf); + } + + return ret > 0 ? 0 : ret; +} + +/* + * Set target slave address for master transfer + */ +static inline void iic_address(struct ibm_iic_private* dev, struct i2c_msg* msg) +{ + volatile struct iic_regs __iomem *iic = dev->vaddr; + u16 addr = msg->addr; + + DBG2("%d: iic_address, 0x%03x (%d-bit)\n", dev->idx, + addr, msg->flags & I2C_M_TEN ? 10 : 7); + + if (msg->flags & I2C_M_TEN){ + out_8(&iic->cntl, CNTL_AMD); + out_8(&iic->lmadr, addr); + out_8(&iic->hmadr, 0xf0 | ((addr >> 7) & 0x06)); + } + else { + out_8(&iic->cntl, 0); + out_8(&iic->lmadr, addr << 1); + } +} + +static inline int iic_invalid_address(const struct i2c_msg* p) +{ + return (p->addr > 0x3ff) || (!(p->flags & I2C_M_TEN) && (p->addr > 0x7f)); +} + +static inline int iic_address_neq(const struct i2c_msg* p1, + const struct i2c_msg* p2) +{ + return (p1->addr != p2->addr) + || ((p1->flags & I2C_M_TEN) != (p2->flags & I2C_M_TEN)); +} + +/* + * Generic master transfer entrypoint. + * Returns the number of processed messages or error (<0) + */ +static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct ibm_iic_private* dev = (struct ibm_iic_private*)(i2c_get_adapdata(adap)); + volatile struct iic_regs __iomem *iic = dev->vaddr; + int i, ret = 0; + + DBG2("%d: iic_xfer, %d msg(s)\n", dev->idx, num); + + if (!num) + return 0; + + /* Check the sanity of the passed messages. + * Uhh, generic i2c layer is more suitable place for such code... + */ + if (unlikely(iic_invalid_address(&msgs[0]))){ + DBG("%d: invalid address 0x%03x (%d-bit)\n", dev->idx, + msgs[0].addr, msgs[0].flags & I2C_M_TEN ? 10 : 7); + return -EINVAL; + } + for (i = 0; i < num; ++i){ + if (unlikely(msgs[i].len <= 0)){ + if (num == 1 && !msgs[0].len){ + /* Special case for I2C_SMBUS_QUICK emulation. + * IBM IIC doesn't support 0-length transactions + * so we have to emulate them using bit-banging. + */ + return iic_smbus_quick(dev, &msgs[0]); + } + DBG("%d: invalid len %d in msg[%d]\n", dev->idx, + msgs[i].len, i); + return -EINVAL; + } + if (unlikely(iic_address_neq(&msgs[0], &msgs[i]))){ + DBG("%d: invalid addr in msg[%d]\n", dev->idx, i); + return -EINVAL; + } + } + + /* Check bus state */ + if (unlikely((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE)){ + DBG("%d: iic_xfer, bus is not free\n", dev->idx); + + /* Usually it means something serious has happened. + * We *cannot* have unfinished previous transfer + * so it doesn't make any sense to try to stop it. + * Probably we were not able to recover from the + * previous error. + * The only *reasonable* thing I can think of here + * is soft reset. --ebs + */ + iic_dev_reset(dev); + + if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ + DBG("%d: iic_xfer, bus is still not free\n", dev->idx); + return -EREMOTEIO; + } + } + else { + /* Flush master data buffer (just in case) */ + out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB); + } + + /* Load slave address */ + iic_address(dev, &msgs[0]); + + /* Do real transfer */ + for (i = 0; i < num && !ret; ++i) + ret = iic_xfer_bytes(dev, &msgs[i], i < num - 1); + + return ret < 0 ? ret : num; +} + +static u32 iic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm iic_algo = { + .master_xfer = iic_xfer, + .functionality = iic_func +}; + +/* + * Calculates IICx_CLCKDIV value for a specific OPB clock frequency + */ +static inline u8 iic_clckdiv(unsigned int opb) +{ + /* Compatibility kludge, should go away after all cards + * are fixed to fill correct value for opbfreq. + * Previous driver version used hardcoded divider value 4, + * it corresponds to OPB frequency from the range (40, 50] MHz + */ + if (!opb){ + printk(KERN_WARNING "ibm-iic: using compatibility value for OPB freq," + " fix your board specific setup\n"); + opb = 50000000; + } + + /* Convert to MHz */ + opb /= 1000000; + + if (opb < 20 || opb > 150){ + printk(KERN_WARNING "ibm-iic: invalid OPB clock frequency %u MHz\n", + opb); + opb = opb < 20 ? 20 : 150; + } + return (u8)((opb + 9) / 10 - 1); +} + +static int __devinit iic_request_irq(struct platform_device *ofdev, + struct ibm_iic_private *dev) +{ + struct device_node *np = ofdev->dev.of_node; + int irq; + + if (iic_force_poll) + return 0; + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + dev_err(&ofdev->dev, "irq_of_parse_and_map failed\n"); + return 0; + } + + /* Disable interrupts until we finish initialization, assumes + * level-sensitive IRQ setup... + */ + iic_interrupt_mode(dev, 0); + if (request_irq(irq, iic_handler, 0, "IBM IIC", dev)) { + dev_err(&ofdev->dev, "request_irq %d failed\n", irq); + /* Fallback to the polling mode */ + return 0; + } + + return irq; +} + +/* + * Register single IIC interface + */ +static int __devinit iic_probe(struct platform_device *ofdev) +{ + struct device_node *np = ofdev->dev.of_node; + struct ibm_iic_private *dev; + struct i2c_adapter *adap; + const u32 *freq; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + dev_err(&ofdev->dev, "failed to allocate device data\n"); + return -ENOMEM; + } + + dev_set_drvdata(&ofdev->dev, dev); + + dev->vaddr = of_iomap(np, 0); + if (dev->vaddr == NULL) { + dev_err(&ofdev->dev, "failed to iomap device\n"); + ret = -ENXIO; + goto error_cleanup; + } + + init_waitqueue_head(&dev->wq); + + dev->irq = iic_request_irq(ofdev, dev); + if (!dev->irq) + dev_warn(&ofdev->dev, "using polling mode\n"); + + /* Board specific settings */ + if (iic_force_fast || of_get_property(np, "fast-mode", NULL)) + dev->fast_mode = 1; + + freq = of_get_property(np, "clock-frequency", NULL); + if (freq == NULL) { + freq = of_get_property(np->parent, "clock-frequency", NULL); + if (freq == NULL) { + dev_err(&ofdev->dev, "Unable to get bus frequency\n"); + ret = -EINVAL; + goto error_cleanup; + } + } + + dev->clckdiv = iic_clckdiv(*freq); + dev_dbg(&ofdev->dev, "clckdiv = %d\n", dev->clckdiv); + + /* Initialize IIC interface */ + iic_dev_init(dev); + + /* Register it with i2c layer */ + adap = &dev->adap; + adap->dev.parent = &ofdev->dev; + adap->dev.of_node = of_node_get(np); + strlcpy(adap->name, "IBM IIC", sizeof(adap->name)); + i2c_set_adapdata(adap, dev); + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->algo = &iic_algo; + adap->timeout = HZ; + + ret = i2c_add_adapter(adap); + if (ret < 0) { + dev_err(&ofdev->dev, "failed to register i2c adapter\n"); + goto error_cleanup; + } + + dev_info(&ofdev->dev, "using %s mode\n", + dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)"); + + /* Now register all the child nodes */ + of_i2c_register_devices(adap); + + return 0; + +error_cleanup: + if (dev->irq) { + iic_interrupt_mode(dev, 0); + free_irq(dev->irq, dev); + } + + if (dev->vaddr) + iounmap(dev->vaddr); + + dev_set_drvdata(&ofdev->dev, NULL); + kfree(dev); + return ret; +} + +/* + * Cleanup initialized IIC interface + */ +static int __devexit iic_remove(struct platform_device *ofdev) +{ + struct ibm_iic_private *dev = dev_get_drvdata(&ofdev->dev); + + dev_set_drvdata(&ofdev->dev, NULL); + + i2c_del_adapter(&dev->adap); + + if (dev->irq) { + iic_interrupt_mode(dev, 0); + free_irq(dev->irq, dev); + } + + iounmap(dev->vaddr); + kfree(dev); + + return 0; +} + +static const struct of_device_id ibm_iic_match[] = { + { .compatible = "ibm,iic", }, + {} +}; + +static struct platform_driver ibm_iic_driver = { + .driver = { + .name = "ibm-iic", + .owner = THIS_MODULE, + .of_match_table = ibm_iic_match, + }, + .probe = iic_probe, + .remove = __devexit_p(iic_remove), +}; + +static int __init iic_init(void) +{ + return platform_driver_register(&ibm_iic_driver); +} + +static void __exit iic_exit(void) +{ + platform_driver_unregister(&ibm_iic_driver); +} + +module_init(iic_init); +module_exit(iic_exit); diff --git a/drivers/i2c/busses/i2c-ibm_iic.h b/drivers/i2c/busses/i2c-ibm_iic.h new file mode 100644 index 00000000..fdaa4829 --- /dev/null +++ b/drivers/i2c/busses/i2c-ibm_iic.h @@ -0,0 +1,123 @@ +/* + * drivers/i2c/busses/i2c-ibm_iic.h + * + * Support for the IIC peripheral on IBM PPC 4xx + * + * Copyright (c) 2003 Zultys Technologies. + * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> + * + * Based on original work by + * Ian DaSilva <idasilva@mvista.com> + * Armin Kuster <akuster@mvista.com> + * Matt Porter <mporter@mvista.com> + * + * Copyright 2000-2003 MontaVista Software Inc. + * + * 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. + * + */ +#ifndef __I2C_IBM_IIC_H_ +#define __I2C_IBM_IIC_H_ + +#include <linux/i2c.h> + +struct iic_regs { + u16 mdbuf; + u16 sbbuf; + u8 lmadr; + u8 hmadr; + u8 cntl; + u8 mdcntl; + u8 sts; + u8 extsts; + u8 lsadr; + u8 hsadr; + u8 clkdiv; + u8 intmsk; + u8 xfrcnt; + u8 xtcntlss; + u8 directcntl; +}; + +struct ibm_iic_private { + struct i2c_adapter adap; + volatile struct iic_regs __iomem *vaddr; + wait_queue_head_t wq; + int idx; + int irq; + int fast_mode; + u8 clckdiv; +}; + +/* IICx_CNTL register */ +#define CNTL_HMT 0x80 +#define CNTL_AMD 0x40 +#define CNTL_TCT_MASK 0x30 +#define CNTL_TCT_SHIFT 4 +#define CNTL_RPST 0x08 +#define CNTL_CHT 0x04 +#define CNTL_RW 0x02 +#define CNTL_PT 0x01 + +/* IICx_MDCNTL register */ +#define MDCNTL_FSDB 0x80 +#define MDCNTL_FMDB 0x40 +#define MDCNTL_EGC 0x20 +#define MDCNTL_FSM 0x10 +#define MDCNTL_ESM 0x08 +#define MDCNTL_EINT 0x04 +#define MDCNTL_EUBS 0x02 +#define MDCNTL_HSCL 0x01 + +/* IICx_STS register */ +#define STS_SSS 0x80 +#define STS_SLPR 0x40 +#define STS_MDBS 0x20 +#define STS_MDBF 0x10 +#define STS_SCMP 0x08 +#define STS_ERR 0x04 +#define STS_IRQA 0x02 +#define STS_PT 0x01 + +/* IICx_EXTSTS register */ +#define EXTSTS_IRQP 0x80 +#define EXTSTS_BCS_MASK 0x70 +#define EXTSTS_BCS_FREE 0x40 +#define EXTSTS_IRQD 0x08 +#define EXTSTS_LA 0x04 +#define EXTSTS_ICT 0x02 +#define EXTSTS_XFRA 0x01 + +/* IICx_INTRMSK register */ +#define INTRMSK_EIRC 0x80 +#define INTRMSK_EIRS 0x40 +#define INTRMSK_EIWC 0x20 +#define INTRMSK_EIWS 0x10 +#define INTRMSK_EIHE 0x08 +#define INTRMSK_EIIC 0x04 +#define INTRMSK_EITA 0x02 +#define INTRMSK_EIMTC 0x01 + +/* IICx_XFRCNT register */ +#define XFRCNT_MTC_MASK 0x07 + +/* IICx_XTCNTLSS register */ +#define XTCNTLSS_SRC 0x80 +#define XTCNTLSS_SRS 0x40 +#define XTCNTLSS_SWC 0x20 +#define XTCNTLSS_SWS 0x10 +#define XTCNTLSS_SRST 0x01 + +/* IICx_DIRECTCNTL register */ +#define DIRCNTL_SDAC 0x08 +#define DIRCNTL_SCC 0x04 +#define DIRCNTL_MSDA 0x02 +#define DIRCNTL_MSC 0x01 + +/* Check if we really control the I2C bus and bus is free */ +#define DIRCTNL_FREE(v) (((v) & 0x0f) == 0x0f) + +#endif /* __I2C_IBM_IIC_H_ */ diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c new file mode 100644 index 00000000..364c4dea --- /dev/null +++ b/drivers/i2c/busses/i2c-imx.c @@ -0,0 +1,700 @@ +/* + * Copyright (C) 2002 Motorola GSG-China + * + * 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. + * + * Author: + * Darius Augulis, Teltonika Inc. + * + * Desc.: + * Implementation of I2C Adapter/Algorithm Driver + * for I2C Bus integrated in Freescale i.MX/MXC processors + * + * Derived from Motorola GSG China I2C example driver + * + * Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de + * Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de + * Copyright (C) 2007 RightHand Technologies, Inc. + * Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt> + * + */ + +/** Includes ******************************************************************* +*******************************************************************************/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/sched.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/slab.h> + +#include <mach/irqs.h> +#include <mach/hardware.h> +#include <mach/i2c.h> + +/** Defines ******************************************************************** +*******************************************************************************/ + +/* This will be the driver name the kernel reports */ +#define DRIVER_NAME "imx-i2c" + +/* Default value */ +#define IMX_I2C_BIT_RATE 100000 /* 100kHz */ + +/* IMX I2C registers */ +#define IMX_I2C_IADR 0x00 /* i2c slave address */ +#define IMX_I2C_IFDR 0x04 /* i2c frequency divider */ +#define IMX_I2C_I2CR 0x08 /* i2c control */ +#define IMX_I2C_I2SR 0x0C /* i2c status */ +#define IMX_I2C_I2DR 0x10 /* i2c transfer data */ + +/* Bits of IMX I2C registers */ +#define I2SR_RXAK 0x01 +#define I2SR_IIF 0x02 +#define I2SR_SRW 0x04 +#define I2SR_IAL 0x10 +#define I2SR_IBB 0x20 +#define I2SR_IAAS 0x40 +#define I2SR_ICF 0x80 +#define I2CR_RSTA 0x04 +#define I2CR_TXAK 0x08 +#define I2CR_MTX 0x10 +#define I2CR_MSTA 0x20 +#define I2CR_IIEN 0x40 +#define I2CR_IEN 0x80 + + +/** Variables ****************************************************************** +*******************************************************************************/ + +/* + * sorted list of clock divider, register value pairs + * taken from table 26-5, p.26-9, Freescale i.MX + * Integrated Portable System Processor Reference Manual + * Document Number: MC9328MXLRM, Rev. 5.1, 06/2007 + * + * Duplicated divider values removed from list + */ + +static u16 i2c_clk_div[50][2] = { + { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, + { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, + { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, + { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, + { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, + { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, + { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, + { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, + { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, + { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, + { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, + { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, + { 3072, 0x1E }, { 3840, 0x1F } +}; + +struct imx_i2c_struct { + struct i2c_adapter adapter; + struct resource *res; + struct clk *clk; + void __iomem *base; + int irq; + wait_queue_head_t queue; + unsigned long i2csr; + unsigned int disable_delay; + int stopped; + unsigned int ifdr; /* IMX_I2C_IFDR */ + unsigned int cur_clk; +}; + +/** Functions for IMX I2C adapter driver *************************************** +*******************************************************************************/ + +static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) +{ + unsigned long orig_jiffies = jiffies; + unsigned int temp; + unsigned long dwRetryMaxCnt=5000; + unsigned long dwRetryCnt=0; + + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + + while (1) { + temp = readb(i2c_imx->base + IMX_I2C_I2SR); + if (for_busy && (temp & I2SR_IBB)) + break; + if (!for_busy && !(temp & I2SR_IBB)) + break; + if (fatal_signal_pending(current)) { + dev_dbg(&i2c_imx->adapter.dev, + "<%s> I2C Interrupted\n", __func__); + return -EINTR; + } + if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { + dev_dbg(&i2c_imx->adapter.dev, + "<%s> I2C bus is busy\n", __func__); + return -ETIMEDOUT; + } + + if(in_atomic() || in_interrupt() ) { + if(++dwRetryCnt>dwRetryMaxCnt) { + printk(KERN_ERR"%s retry over %d times \n",__FUNCTION__,dwRetryMaxCnt); + break; + } + udelay(100); + } + else { + schedule(); + } + } + + return 0; +} + +static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx) +{ + if(in_atomic() || in_interrupt()) { + unsigned long dwRetryMaxCnt=1000; + unsigned long dwRetryCnt=0; + + do { + i2c_imx->i2csr |= readb(i2c_imx->base + IMX_I2C_I2SR); + if(i2c_imx->i2csr & I2SR_IIF) { + break; + } + + if(dwRetryCnt++>dwRetryMaxCnt) { + printk(KERN_ERR"%s retry over %d times !!\n",__FUNCTION__,dwRetryMaxCnt); + break; + } + udelay(100); + }while(1) ; + + } + else { + wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10); + } + + if (unlikely(!(i2c_imx->i2csr & I2SR_IIF))) { + dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__); + return -ETIMEDOUT; + } + dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__); + i2c_imx->i2csr = 0; + return 0; +} + +static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx) +{ + if (readb(i2c_imx->base + IMX_I2C_I2SR) & I2SR_RXAK) { + dev_dbg(&i2c_imx->adapter.dev, "<%s> No ACK\n", __func__); + return -EIO; /* No ACK */ + } + + dev_dbg(&i2c_imx->adapter.dev, "<%s> ACK received\n", __func__); + return 0; +} + +static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, + unsigned int rate) +{ + unsigned int i2c_clk_rate; + unsigned int div; + int i; + + /* Divider value calculation */ + i2c_clk_rate = clk_get_rate(i2c_imx->clk); + if (i2c_imx->cur_clk == i2c_clk_rate) + return; + else + i2c_imx->cur_clk = i2c_clk_rate; + div = (i2c_clk_rate + rate - 1) / rate; + if (div < i2c_clk_div[0][0]) + i = 0; + else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) + i = ARRAY_SIZE(i2c_clk_div) - 1; + else + for (i = 0; i2c_clk_div[i][0] < div; i++) + ; + + /* Store divider value */ + i2c_imx->ifdr = i2c_clk_div[i][1]; + + /* + * There dummy delay is calculated. + * It should be about one I2C clock period long. + * This delay is used in I2C bus disable function + * to fix chip hardware bug. + */ + i2c_imx->disable_delay = (500000U * i2c_clk_div[i][0] + + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2); + + /* dev_dbg() can't be used, because adapter is not yet registered */ +#ifdef CONFIG_I2C_DEBUG_BUS + printk(KERN_DEBUG "I2C: <%s> I2C_CLK=%d, REQ DIV=%d\n", + __func__, i2c_clk_rate, div); + printk(KERN_DEBUG "I2C: <%s> IFDR[IC]=0x%x, REAL DIV=%d\n", + __func__, i2c_clk_div[i][1], i2c_clk_div[i][0]); +#endif +} + +static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) +{ + unsigned int temp = 0; + struct imxi2c_platform_data *pdata; + int result; + + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + + /* Currently on Arik/Rigel, the I2C clk is from IPG_PERCLK which is + * sourced from IPG_CLK. In low bus freq mode, IPG_CLK is at 12MHz + * and IPG_PERCLK is down to 4MHz. + * Update I2C divider before set i2c clock. + */ + pdata = i2c_imx->adapter.dev.parent->platform_data; + if (pdata && pdata->bitrate) + i2c_imx_set_clk(i2c_imx, pdata->bitrate); + else + i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE); + + clk_enable(i2c_imx->clk); + writeb(i2c_imx->ifdr, i2c_imx->base + IMX_I2C_IFDR); + /* Enable I2C controller */ + writeb(0, i2c_imx->base + IMX_I2C_I2SR); + writeb(I2CR_IEN, i2c_imx->base + IMX_I2C_I2CR); + + /* Wait controller to be stable */ + udelay(50); + + /* Start I2C transaction */ + temp = readb(i2c_imx->base + IMX_I2C_I2CR); + temp |= I2CR_MSTA; + writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + result = i2c_imx_bus_busy(i2c_imx, 1); + if (result) + return result; + i2c_imx->stopped = 0; + + temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; + writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + return result; +} + +static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) +{ + unsigned int temp = 0; + + if (!i2c_imx->stopped) { + /* Stop I2C transaction */ + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + temp = readb(i2c_imx->base + IMX_I2C_I2CR); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + } + if (cpu_is_mx1()) { + /* + * This delay caused by an i.MXL hardware bug. + * If no (or too short) delay, no "STOP" bit will be generated. + */ + udelay(i2c_imx->disable_delay); + } + + if (!i2c_imx->stopped) { + i2c_imx_bus_busy(i2c_imx, 0); + i2c_imx->stopped = 1; + } + + /* Disable I2C controller */ + writeb(0, i2c_imx->base + IMX_I2C_I2CR); + clk_disable(i2c_imx->clk); +} + +static irqreturn_t i2c_imx_isr(int irq, void *dev_id) +{ + struct imx_i2c_struct *i2c_imx = dev_id; + unsigned int temp; + + temp = readb(i2c_imx->base + IMX_I2C_I2SR); + if (temp & I2SR_IIF) { + /* save status register */ + i2c_imx->i2csr = temp; + temp &= ~I2SR_IIF; + writeb(temp, i2c_imx->base + IMX_I2C_I2SR); + wake_up(&i2c_imx->queue); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) +{ + int i, result; + + dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n", + __func__, msgs->addr << 1); + + /* write slave address */ + writeb(msgs->addr << 1, i2c_imx->base + IMX_I2C_I2DR); + result = i2c_imx_trx_complete(i2c_imx); + if (result) + return result; + result = i2c_imx_acked(i2c_imx); + if (result) + return result; + dev_dbg(&i2c_imx->adapter.dev, "<%s> write data\n", __func__); + + /* write data */ + for (i = 0; i < msgs->len; i++) { + dev_dbg(&i2c_imx->adapter.dev, + "<%s> write byte: B%d=0x%X\n", + __func__, i, msgs->buf[i]); + writeb(msgs->buf[i], i2c_imx->base + IMX_I2C_I2DR); + result = i2c_imx_trx_complete(i2c_imx); + if (result) + return result; + result = i2c_imx_acked(i2c_imx); + if (result) + return result; + } + return 0; +} + +static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) +{ + int i, result; + unsigned int temp; + + dev_dbg(&i2c_imx->adapter.dev, + "<%s> write slave address: addr=0x%x\n", + __func__, (msgs->addr << 1) | 0x01); + + /* write slave address */ + writeb((msgs->addr << 1) | 0x01, i2c_imx->base + IMX_I2C_I2DR); + result = i2c_imx_trx_complete(i2c_imx); + if (result) + return result; + result = i2c_imx_acked(i2c_imx); + if (result) + return result; + + dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__); + + /* setup bus to read data */ + temp = readb(i2c_imx->base + IMX_I2C_I2CR); + temp &= ~I2CR_MTX; + if (msgs->len - 1) + temp &= ~I2CR_TXAK; + writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + readb(i2c_imx->base + IMX_I2C_I2DR); /* dummy read */ + + dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__); + + /* read data */ + for (i = 0; i < msgs->len; i++) { + result = i2c_imx_trx_complete(i2c_imx); + if (result) + return result; + if (i == (msgs->len - 1)) { + /* It must generate STOP before read I2DR to prevent + controller from generating another clock cycle */ + dev_dbg(&i2c_imx->adapter.dev, + "<%s> clear MSTA\n", __func__); + temp = readb(i2c_imx->base + IMX_I2C_I2CR); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + i2c_imx_bus_busy(i2c_imx, 0); + i2c_imx->stopped = 1; + } else if (i == (msgs->len - 2)) { + dev_dbg(&i2c_imx->adapter.dev, + "<%s> set TXAK\n", __func__); + temp = readb(i2c_imx->base + IMX_I2C_I2CR); + temp |= I2CR_TXAK; + writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + } + msgs->buf[i] = readb(i2c_imx->base + IMX_I2C_I2DR); + dev_dbg(&i2c_imx->adapter.dev, + "<%s> read byte: B%d=0x%X\n", + __func__, i, msgs->buf[i]); + } + return 0; +} + +static int i2c_imx_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + unsigned int i, temp; + int result; + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); + + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + + /* Start I2C transfer */ + result = i2c_imx_start(i2c_imx); + if (result) + goto fail0; + + /* read/write data */ + for (i = 0; i < num; i++) { + if (i) { + dev_dbg(&i2c_imx->adapter.dev, + "<%s> repeated start\n", __func__); + temp = readb(i2c_imx->base + IMX_I2C_I2CR); + temp |= I2CR_RSTA; + writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + result = i2c_imx_bus_busy(i2c_imx, 1); + if (result) + goto fail0; + } + dev_dbg(&i2c_imx->adapter.dev, + "<%s> transfer message: %d\n", __func__, i); + /* write/read data */ +#ifdef CONFIG_I2C_DEBUG_BUS + temp = readb(i2c_imx->base + IMX_I2C_I2CR); + dev_dbg(&i2c_imx->adapter.dev, "<%s> CONTROL: IEN=%d, IIEN=%d, " + "MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n", __func__, + (temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0), + (temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0), + (temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0)); + temp = readb(i2c_imx->base + IMX_I2C_I2SR); + dev_dbg(&i2c_imx->adapter.dev, + "<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, " + "IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n", __func__, + (temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0), + (temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0), + (temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0), + (temp & I2SR_RXAK ? 1 : 0)); +#endif + if (msgs[i].flags & I2C_M_RD) + result = i2c_imx_read(i2c_imx, &msgs[i]); + else + result = i2c_imx_write(i2c_imx, &msgs[i]); + if (result) + goto fail0; + } + +fail0: + /* Stop I2C transfer */ + i2c_imx_stop(i2c_imx); + + dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__, + (result < 0) ? "error" : "success msg", + (result < 0) ? result : num); + return (result < 0) ? result : num; +} + +static u32 i2c_imx_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm i2c_imx_algo = { + .master_xfer = i2c_imx_xfer, + .functionality = i2c_imx_func, +}; + +static int __init i2c_imx_probe(struct platform_device *pdev) +{ + struct imx_i2c_struct *i2c_imx; + struct resource *res; + struct imxi2c_platform_data *pdata; + void __iomem *base; + resource_size_t res_size; + int irq; + int ret; + + dev_dbg(&pdev->dev, "<%s>\n", __func__); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get device resources\n"); + return -ENOENT; + } + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "can't get irq number\n"); + return -ENOENT; + } + + pdata = pdev->dev.platform_data; + + if (pdata && pdata->init) { + ret = pdata->init(&pdev->dev); + if (ret) + return ret; + } + + res_size = resource_size(res); + + if (!request_mem_region(res->start, res_size, DRIVER_NAME)) { + ret = -EBUSY; + goto fail0; + } + + base = ioremap(res->start, res_size); + if (!base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -EIO; + goto fail1; + } + + i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL); + if (!i2c_imx) { + dev_err(&pdev->dev, "can't allocate interface\n"); + ret = -ENOMEM; + goto fail2; + } + + /* Setup i2c_imx driver structure */ + strcpy(i2c_imx->adapter.name, pdev->name); + i2c_imx->adapter.owner = THIS_MODULE; + i2c_imx->adapter.algo = &i2c_imx_algo; + i2c_imx->adapter.dev.parent = &pdev->dev; + i2c_imx->adapter.nr = pdev->id; + i2c_imx->irq = irq; + i2c_imx->base = base; + i2c_imx->res = res; + + /* Get I2C clock */ + i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk"); + if (IS_ERR(i2c_imx->clk)) { + ret = PTR_ERR(i2c_imx->clk); + dev_err(&pdev->dev, "can't get I2C clock\n"); + goto fail3; + } + + /* Request IRQ */ + ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx); + if (ret) { + dev_err(&pdev->dev, "can't claim irq %d\n", i2c_imx->irq); + goto fail4; + } + + /* Init queue */ + init_waitqueue_head(&i2c_imx->queue); + + /* Set up adapter data */ + i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); + + /* Set up clock divider */ + if (pdata && pdata->bitrate) + i2c_imx_set_clk(i2c_imx, pdata->bitrate); + else + i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE); + + /* Set up chip registers to defaults */ + writeb(0, i2c_imx->base + IMX_I2C_I2CR); + writeb(0, i2c_imx->base + IMX_I2C_I2SR); + + /* Add I2C adapter */ + ret = i2c_add_numbered_adapter(&i2c_imx->adapter); + if (ret < 0) { + dev_err(&pdev->dev, "registration failed\n"); + goto fail5; + } + + /* Set up platform driver data */ + platform_set_drvdata(pdev, i2c_imx); + + dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq); + dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n", + i2c_imx->res->start, i2c_imx->res->end); + dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n", + res_size, i2c_imx->res->start); + dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n", + i2c_imx->adapter.name); + dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); + + return 0; /* Return OK */ + +fail5: + free_irq(i2c_imx->irq, i2c_imx); +fail4: + clk_put(i2c_imx->clk); +fail3: + kfree(i2c_imx); +fail2: + iounmap(base); +fail1: + release_mem_region(res->start, resource_size(res)); +fail0: + if (pdata && pdata->exit) + pdata->exit(&pdev->dev); + return ret; /* Return error number */ +} + +static int __exit i2c_imx_remove(struct platform_device *pdev) +{ + struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev); + struct imxi2c_platform_data *pdata = pdev->dev.platform_data; + + /* remove adapter */ + dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); + i2c_del_adapter(&i2c_imx->adapter); + platform_set_drvdata(pdev, NULL); + + /* free interrupt */ + free_irq(i2c_imx->irq, i2c_imx); + + /* setup chip registers to defaults */ + writeb(0, i2c_imx->base + IMX_I2C_IADR); + writeb(0, i2c_imx->base + IMX_I2C_IFDR); + writeb(0, i2c_imx->base + IMX_I2C_I2CR); + writeb(0, i2c_imx->base + IMX_I2C_I2SR); + + /* Shut down hardware */ + if (pdata && pdata->exit) + pdata->exit(&pdev->dev); + + clk_put(i2c_imx->clk); + + iounmap(i2c_imx->base); + release_mem_region(i2c_imx->res->start, resource_size(i2c_imx->res)); + kfree(i2c_imx); + return 0; +} + +static struct platform_driver i2c_imx_driver = { + .remove = __exit_p(i2c_imx_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + } +}; + +static int __init i2c_adap_imx_init(void) +{ + return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe); +} +subsys_initcall(i2c_adap_imx_init); + +static void __exit i2c_adap_imx_exit(void) +{ + platform_driver_unregister(&i2c_imx_driver); +} +module_exit(i2c_adap_imx_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Darius Augulis"); +MODULE_DESCRIPTION("I2C adapter driver for IMX I2C bus"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/i2c/busses/i2c-intel-mid.c b/drivers/i2c/busses/i2c-intel-mid.c new file mode 100644 index 00000000..e828ac85 --- /dev/null +++ b/drivers/i2c/busses/i2c-intel-mid.c @@ -0,0 +1,1135 @@ +/* + * Support for Moorestown/Medfield I2C chip + * + * Copyright (c) 2009 Intel Corporation. + * Copyright (c) 2009 Synopsys. Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, version + * 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/io.h> + +#define DRIVER_NAME "i2c-intel-mid" +#define VERSION "Version 0.5ac2" +#define PLATFORM "Moorestown/Medfield" + +/* Tables use: 0 Moorestown, 1 Medfield */ +#define NUM_PLATFORMS 2 +enum platform_enum { + MOORESTOWN = 0, + MEDFIELD = 1, +}; + +enum mid_i2c_status { + STATUS_IDLE = 0, + STATUS_READ_START, + STATUS_READ_IN_PROGRESS, + STATUS_READ_SUCCESS, + STATUS_WRITE_START, + STATUS_WRITE_SUCCESS, + STATUS_XFER_ABORT, + STATUS_STANDBY +}; + +/** + * struct intel_mid_i2c_private - per device I²C context + * @adap: core i2c layer adapter information + * @dev: device reference for power management + * @base: register base + * @speed: speed mode for this port + * @complete: completion object for transaction wait + * @abort: reason for last abort + * @rx_buf: pointer into working receive buffer + * @rx_buf_len: receive buffer length + * @status: adapter state machine + * @msg: the message we are currently processing + * @platform: the MID device type we are part of + * @lock: transaction serialization + * + * We allocate one of these per device we discover, it holds the core + * i2c layer objects and the data we need to track privately. + */ +struct intel_mid_i2c_private { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; + int speed; + struct completion complete; + int abort; + u8 *rx_buf; + int rx_buf_len; + enum mid_i2c_status status; + struct i2c_msg *msg; + enum platform_enum platform; + struct mutex lock; +}; + +#define NUM_SPEEDS 3 + +#define ACTIVE 0 +#define STANDBY 1 + + +/* Control register */ +#define IC_CON 0x00 +#define SLV_DIS (1 << 6) /* Disable slave mode */ +#define RESTART (1 << 5) /* Send a Restart condition */ +#define ADDR_10BIT (1 << 4) /* 10-bit addressing */ +#define STANDARD_MODE (1 << 1) /* standard mode */ +#define FAST_MODE (2 << 1) /* fast mode */ +#define HIGH_MODE (3 << 1) /* high speed mode */ +#define MASTER_EN (1 << 0) /* Master mode */ + +/* Target address register */ +#define IC_TAR 0x04 +#define IC_TAR_10BIT_ADDR (1 << 12) /* 10-bit addressing */ +#define IC_TAR_SPECIAL (1 << 11) /* Perform special I2C cmd */ +#define IC_TAR_GC_OR_START (1 << 10) /* 0: Gerneral Call Address */ + /* 1: START BYTE */ +/* Slave Address Register */ +#define IC_SAR 0x08 /* Not used in Master mode */ + +/* High Speed Master Mode Code Address Register */ +#define IC_HS_MADDR 0x0c + +/* Rx/Tx Data Buffer and Command Register */ +#define IC_DATA_CMD 0x10 +#define IC_RD (1 << 8) /* 1: Read 0: Write */ + +/* Standard Speed Clock SCL High Count Register */ +#define IC_SS_SCL_HCNT 0x14 + +/* Standard Speed Clock SCL Low Count Register */ +#define IC_SS_SCL_LCNT 0x18 + +/* Fast Speed Clock SCL High Count Register */ +#define IC_FS_SCL_HCNT 0x1c + +/* Fast Spedd Clock SCL Low Count Register */ +#define IC_FS_SCL_LCNT 0x20 + +/* High Speed Clock SCL High Count Register */ +#define IC_HS_SCL_HCNT 0x24 + +/* High Speed Clock SCL Low Count Register */ +#define IC_HS_SCL_LCNT 0x28 + +/* Interrupt Status Register */ +#define IC_INTR_STAT 0x2c /* Read only */ +#define R_GEN_CALL (1 << 11) +#define R_START_DET (1 << 10) +#define R_STOP_DET (1 << 9) +#define R_ACTIVITY (1 << 8) +#define R_RX_DONE (1 << 7) +#define R_TX_ABRT (1 << 6) +#define R_RD_REQ (1 << 5) +#define R_TX_EMPTY (1 << 4) +#define R_TX_OVER (1 << 3) +#define R_RX_FULL (1 << 2) +#define R_RX_OVER (1 << 1) +#define R_RX_UNDER (1 << 0) + +/* Interrupt Mask Register */ +#define IC_INTR_MASK 0x30 /* Read and Write */ +#define M_GEN_CALL (1 << 11) +#define M_START_DET (1 << 10) +#define M_STOP_DET (1 << 9) +#define M_ACTIVITY (1 << 8) +#define M_RX_DONE (1 << 7) +#define M_TX_ABRT (1 << 6) +#define M_RD_REQ (1 << 5) +#define M_TX_EMPTY (1 << 4) +#define M_TX_OVER (1 << 3) +#define M_RX_FULL (1 << 2) +#define M_RX_OVER (1 << 1) +#define M_RX_UNDER (1 << 0) + +/* Raw Interrupt Status Register */ +#define IC_RAW_INTR_STAT 0x34 /* Read Only */ +#define GEN_CALL (1 << 11) /* General call */ +#define START_DET (1 << 10) /* (RE)START occurred */ +#define STOP_DET (1 << 9) /* STOP occurred */ +#define ACTIVITY (1 << 8) /* Bus busy */ +#define RX_DONE (1 << 7) /* Not used in Master mode */ +#define TX_ABRT (1 << 6) /* Transmit Abort */ +#define RD_REQ (1 << 5) /* Not used in Master mode */ +#define TX_EMPTY (1 << 4) /* TX FIFO <= threshold */ +#define TX_OVER (1 << 3) /* TX FIFO overflow */ +#define RX_FULL (1 << 2) /* RX FIFO >= threshold */ +#define RX_OVER (1 << 1) /* RX FIFO overflow */ +#define RX_UNDER (1 << 0) /* RX FIFO empty */ + +/* Receive FIFO Threshold Register */ +#define IC_RX_TL 0x38 + +/* Transmit FIFO Treshold Register */ +#define IC_TX_TL 0x3c + +/* Clear Combined and Individual Interrupt Register */ +#define IC_CLR_INTR 0x40 +#define CLR_INTR (1 << 0) + +/* Clear RX_UNDER Interrupt Register */ +#define IC_CLR_RX_UNDER 0x44 +#define CLR_RX_UNDER (1 << 0) + +/* Clear RX_OVER Interrupt Register */ +#define IC_CLR_RX_OVER 0x48 +#define CLR_RX_OVER (1 << 0) + +/* Clear TX_OVER Interrupt Register */ +#define IC_CLR_TX_OVER 0x4c +#define CLR_TX_OVER (1 << 0) + +#define IC_CLR_RD_REQ 0x50 + +/* Clear TX_ABRT Interrupt Register */ +#define IC_CLR_TX_ABRT 0x54 +#define CLR_TX_ABRT (1 << 0) +#define IC_CLR_RX_DONE 0x58 + +/* Clear ACTIVITY Interrupt Register */ +#define IC_CLR_ACTIVITY 0x5c +#define CLR_ACTIVITY (1 << 0) + +/* Clear STOP_DET Interrupt Register */ +#define IC_CLR_STOP_DET 0x60 +#define CLR_STOP_DET (1 << 0) + +/* Clear START_DET Interrupt Register */ +#define IC_CLR_START_DET 0x64 +#define CLR_START_DET (1 << 0) + +/* Clear GEN_CALL Interrupt Register */ +#define IC_CLR_GEN_CALL 0x68 +#define CLR_GEN_CALL (1 << 0) + +/* Enable Register */ +#define IC_ENABLE 0x6c +#define ENABLE (1 << 0) + +/* Status Register */ +#define IC_STATUS 0x70 /* Read Only */ +#define STAT_SLV_ACTIVITY (1 << 6) /* Slave not in idle */ +#define STAT_MST_ACTIVITY (1 << 5) /* Master not in idle */ +#define STAT_RFF (1 << 4) /* RX FIFO Full */ +#define STAT_RFNE (1 << 3) /* RX FIFO Not Empty */ +#define STAT_TFE (1 << 2) /* TX FIFO Empty */ +#define STAT_TFNF (1 << 1) /* TX FIFO Not Full */ +#define STAT_ACTIVITY (1 << 0) /* Activity Status */ + +/* Transmit FIFO Level Register */ +#define IC_TXFLR 0x74 /* Read Only */ +#define TXFLR (1 << 0) /* TX FIFO level */ + +/* Receive FIFO Level Register */ +#define IC_RXFLR 0x78 /* Read Only */ +#define RXFLR (1 << 0) /* RX FIFO level */ + +/* Transmit Abort Source Register */ +#define IC_TX_ABRT_SOURCE 0x80 +#define ABRT_SLVRD_INTX (1 << 15) +#define ABRT_SLV_ARBLOST (1 << 14) +#define ABRT_SLVFLUSH_TXFIFO (1 << 13) +#define ARB_LOST (1 << 12) +#define ABRT_MASTER_DIS (1 << 11) +#define ABRT_10B_RD_NORSTRT (1 << 10) +#define ABRT_SBYTE_NORSTRT (1 << 9) +#define ABRT_HS_NORSTRT (1 << 8) +#define ABRT_SBYTE_ACKDET (1 << 7) +#define ABRT_HS_ACKDET (1 << 6) +#define ABRT_GCALL_READ (1 << 5) +#define ABRT_GCALL_NOACK (1 << 4) +#define ABRT_TXDATA_NOACK (1 << 3) +#define ABRT_10ADDR2_NOACK (1 << 2) +#define ABRT_10ADDR1_NOACK (1 << 1) +#define ABRT_7B_ADDR_NOACK (1 << 0) + +/* Enable Status Register */ +#define IC_ENABLE_STATUS 0x9c +#define IC_EN (1 << 0) /* I2C in an enabled state */ + +/* Component Parameter Register 1*/ +#define IC_COMP_PARAM_1 0xf4 +#define APB_DATA_WIDTH (0x3 << 0) + +/* added by xiaolin --begin */ +#define SS_MIN_SCL_HIGH 4000 +#define SS_MIN_SCL_LOW 4700 +#define FS_MIN_SCL_HIGH 600 +#define FS_MIN_SCL_LOW 1300 +#define HS_MIN_SCL_HIGH_100PF 60 +#define HS_MIN_SCL_LOW_100PF 120 + +#define STANDARD 0 +#define FAST 1 +#define HIGH 2 + +#define NUM_SPEEDS 3 + +static int speed_mode[6] = { + FAST, + FAST, + FAST, + STANDARD, + FAST, + FAST +}; + +static int ctl_num = 6; +module_param_array(speed_mode, int, &ctl_num, S_IRUGO); +MODULE_PARM_DESC(speed_mode, "Set the speed of the i2c interface (0-2)"); + +/** + * intel_mid_i2c_disable - Disable I2C controller + * @adap: struct pointer to i2c_adapter + * + * Return Value: + * 0 success + * -EBUSY if device is busy + * -ETIMEDOUT if i2c cannot be disabled within the given time + * + * I2C bus state should be checked prior to disabling the hardware. If bus is + * not in idle state, an errno is returned. Write "0" to IC_ENABLE to disable + * I2C controller. + */ +static int intel_mid_i2c_disable(struct i2c_adapter *adap) +{ + struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); + int err = 0; + int count = 0; + int ret1, ret2; + static const u16 delay[NUM_SPEEDS] = {100, 25, 3}; + + /* Set IC_ENABLE to 0 */ + writel(0, i2c->base + IC_ENABLE); + + /* Check if device is busy */ + dev_dbg(&adap->dev, "mrst i2c disable\n"); + while ((ret1 = readl(i2c->base + IC_ENABLE_STATUS) & 0x1) + || (ret2 = readl(i2c->base + IC_STATUS) & 0x1)) { + udelay(delay[i2c->speed]); + writel(0, i2c->base + IC_ENABLE); + dev_dbg(&adap->dev, "i2c is busy, count is %d speed %d\n", + count, i2c->speed); + if (count++ > 10) { + err = -ETIMEDOUT; + break; + } + } + + /* Clear all interrupts */ + readl(i2c->base + IC_CLR_INTR); + readl(i2c->base + IC_CLR_STOP_DET); + readl(i2c->base + IC_CLR_START_DET); + readl(i2c->base + IC_CLR_ACTIVITY); + readl(i2c->base + IC_CLR_TX_ABRT); + readl(i2c->base + IC_CLR_RX_OVER); + readl(i2c->base + IC_CLR_RX_UNDER); + readl(i2c->base + IC_CLR_TX_OVER); + readl(i2c->base + IC_CLR_RX_DONE); + readl(i2c->base + IC_CLR_GEN_CALL); + + /* Disable all interupts */ + writel(0x0000, i2c->base + IC_INTR_MASK); + + return err; +} + +/** + * intel_mid_i2c_hwinit - Initialize the I2C hardware registers + * @dev: pci device struct pointer + * + * This function will be called in intel_mid_i2c_probe() before device + * registration. + * + * Return Values: + * 0 success + * -EBUSY i2c cannot be disabled + * -ETIMEDOUT i2c cannot be disabled + * -EFAULT If APB data width is not 32-bit wide + * + * I2C should be disabled prior to other register operation. If failed, an + * errno is returned. Mask and Clear all interrpts, this should be done at + * first. Set common registers which will not be modified during normal + * transfers, including: control register, FIFO threshold and clock freq. + * Check APB data width at last. + */ +static int intel_mid_i2c_hwinit(struct intel_mid_i2c_private *i2c) +{ + int err; + + static const u16 hcnt[NUM_PLATFORMS][NUM_SPEEDS] = { + { 0x75, 0x15, 0x07 }, + { 0x04c, 0x10, 0x06 } + }; + static const u16 lcnt[NUM_PLATFORMS][NUM_SPEEDS] = { + { 0x7C, 0x21, 0x0E }, + { 0x053, 0x19, 0x0F } + }; + + /* Disable i2c first */ + err = intel_mid_i2c_disable(&i2c->adap); + if (err) + return err; + + /* + * Setup clock frequency and speed mode + * Enable restart condition, + * enable master FSM, disable slave FSM, + * use target address when initiating transfer + */ + + writel((i2c->speed + 1) << 1 | SLV_DIS | RESTART | MASTER_EN, + i2c->base + IC_CON); + writel(hcnt[i2c->platform][i2c->speed], + i2c->base + (IC_SS_SCL_HCNT + (i2c->speed << 3))); + writel(lcnt[i2c->platform][i2c->speed], + i2c->base + (IC_SS_SCL_LCNT + (i2c->speed << 3))); + + /* Set tranmit & receive FIFO threshold to zero */ + writel(0x0, i2c->base + IC_RX_TL); + writel(0x0, i2c->base + IC_TX_TL); + + return 0; +} + +/** + * intel_mid_i2c_func - Return the supported three I2C operations. + * @adapter: i2c_adapter struct pointer + */ +static u32 intel_mid_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; +} + +/** + * intel_mid_i2c_address_neq - To check if the addresses for different i2c messages + * are equal. + * @p1: first i2c_msg + * @p2: second i2c_msg + * + * Return Values: + * 0 if addresses are equal + * 1 if not equal + * + * Within a single transfer, the I2C client may need to send its address more + * than once. So a check if the addresses match is needed. + */ +static inline bool intel_mid_i2c_address_neq(const struct i2c_msg *p1, + const struct i2c_msg *p2) +{ + if (p1->addr != p2->addr) + return 1; + if ((p1->flags ^ p2->flags) & I2C_M_TEN) + return 1; + return 0; +} + +/** + * intel_mid_i2c_abort - To handle transfer abortions and print error messages. + * @adap: i2c_adapter struct pointer + * + * By reading register IC_TX_ABRT_SOURCE, various transfer errors can be + * distingushed. At present, no circumstances have been found out that + * multiple errors would be occurred simutaneously, so we simply use the + * register value directly. + * + * At last the error bits are cleared. (Note clear ABRT_SBYTE_NORSTRT bit need + * a few extra steps) + */ +static void intel_mid_i2c_abort(struct intel_mid_i2c_private *i2c) +{ + /* Read about source register */ + int abort = i2c->abort; + struct i2c_adapter *adap = &i2c->adap; + + /* Single transfer error check: + * According to databook, TX/RX FIFOs would be flushed when + * the abort interrupt occurred. + */ + if (abort & ABRT_MASTER_DIS) + dev_err(&adap->dev, + "initiate master operation with master mode disabled.\n"); + if (abort & ABRT_10B_RD_NORSTRT) + dev_err(&adap->dev, + "RESTART disabled and master sent READ cmd in 10-bit addressing.\n"); + + if (abort & ABRT_SBYTE_NORSTRT) { + dev_err(&adap->dev, + "RESTART disabled and user is trying to send START byte.\n"); + writel(~ABRT_SBYTE_NORSTRT, i2c->base + IC_TX_ABRT_SOURCE); + writel(RESTART, i2c->base + IC_CON); + writel(~IC_TAR_SPECIAL, i2c->base + IC_TAR); + } + + if (abort & ABRT_SBYTE_ACKDET) + dev_err(&adap->dev, + "START byte was not acknowledged.\n"); + if (abort & ABRT_TXDATA_NOACK) + dev_dbg(&adap->dev, + "No acknowledgement received from slave.\n"); + if (abort & ABRT_10ADDR2_NOACK) + dev_dbg(&adap->dev, + "The 2nd address byte of the 10-bit address was not acknowledged.\n"); + if (abort & ABRT_10ADDR1_NOACK) + dev_dbg(&adap->dev, + "The 1st address byte of 10-bit address was not acknowledged.\n"); + if (abort & ABRT_7B_ADDR_NOACK) + dev_dbg(&adap->dev, + "I2C slave device not acknowledged.\n"); + + /* Clear TX_ABRT bit */ + readl(i2c->base + IC_CLR_TX_ABRT); + i2c->status = STATUS_XFER_ABORT; +} + +/** + * xfer_read - Internal function to implement master read transfer. + * @adap: i2c_adapter struct pointer + * @buf: buffer in i2c_msg + * @length: number of bytes to be read + * + * Return Values: + * 0 if the read transfer succeeds + * -ETIMEDOUT if cannot read the "raw" interrupt register + * -EINVAL if a transfer abort occurred + * + * For every byte, a "READ" command will be loaded into IC_DATA_CMD prior to + * data transfer. The actual "read" operation will be performed if an RX_FULL + * interrupt occurred. + * + * Note there may be two interrupt signals captured, one should read + * IC_RAW_INTR_STAT to separate between errors and actual data. + */ +static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length) +{ + struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); + int i = length; + int err; + + if (length >= 256) { + dev_err(&adap->dev, + "I2C FIFO cannot support larger than 256 bytes\n"); + return -EMSGSIZE; + } + + INIT_COMPLETION(i2c->complete); + + readl(i2c->base + IC_CLR_INTR); + writel(0x0044, i2c->base + IC_INTR_MASK); + + i2c->status = STATUS_READ_START; + + while (i--) + writel(IC_RD, i2c->base + IC_DATA_CMD); + + i2c->status = STATUS_READ_START; + err = wait_for_completion_interruptible_timeout(&i2c->complete, HZ); + if (!err) { + dev_err(&adap->dev, "Timeout for ACK from I2C slave device\n"); + intel_mid_i2c_hwinit(i2c); + return -ETIMEDOUT; + } + if (i2c->status == STATUS_READ_SUCCESS) + return 0; + else + return -EIO; +} + +/** + * xfer_write - Internal function to implement master write transfer. + * @adap: i2c_adapter struct pointer + * @buf: buffer in i2c_msg + * @length: number of bytes to be read + * + * Return Values: + * 0 if the read transfer succeeds + * -ETIMEDOUT if we cannot read the "raw" interrupt register + * -EINVAL if a transfer abort occurred + * + * For every byte, a "WRITE" command will be loaded into IC_DATA_CMD prior to + * data transfer. The actual "write" operation will be performed when the + * RX_FULL interrupt signal occurs. + * + * Note there may be two interrupt signals captured, one should read + * IC_RAW_INTR_STAT to separate between errors and actual data. + */ +static int xfer_write(struct i2c_adapter *adap, + unsigned char *buf, int length) +{ + struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); + int i, err; + + if (length >= 256) { + dev_err(&adap->dev, + "I2C FIFO cannot support larger than 256 bytes\n"); + return -EMSGSIZE; + } + + INIT_COMPLETION(i2c->complete); + + readl(i2c->base + IC_CLR_INTR); + writel(0x0050, i2c->base + IC_INTR_MASK); + + i2c->status = STATUS_WRITE_START; + for (i = 0; i < length; i++) + writel((u16)(*(buf + i)), i2c->base + IC_DATA_CMD); + + i2c->status = STATUS_WRITE_START; + err = wait_for_completion_interruptible_timeout(&i2c->complete, HZ); + if (!err) { + dev_err(&adap->dev, "Timeout for ACK from I2C slave device\n"); + intel_mid_i2c_hwinit(i2c); + return -ETIMEDOUT; + } else { + if (i2c->status == STATUS_WRITE_SUCCESS) + return 0; + else + return -EIO; + } +} + +static int intel_mid_i2c_setup(struct i2c_adapter *adap, struct i2c_msg *pmsg) +{ + struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); + int err; + u32 reg; + u32 bit_mask; + u32 mode; + + /* Disable device first */ + err = intel_mid_i2c_disable(adap); + if (err) { + dev_err(&adap->dev, + "Cannot disable i2c controller, timeout\n"); + return err; + } + + mode = (1 + i2c->speed) << 1; + /* set the speed mode */ + reg = readl(i2c->base + IC_CON); + if ((reg & 0x06) != mode) { + dev_dbg(&adap->dev, "set mode %d\n", i2c->speed); + writel((reg & ~0x6) | mode, i2c->base + IC_CON); + } + + reg = readl(i2c->base + IC_CON); + /* use 7-bit addressing */ + if (pmsg->flags & I2C_M_TEN) { + if ((reg & ADDR_10BIT) != ADDR_10BIT) { + dev_dbg(&adap->dev, "set i2c 10 bit address mode\n"); + writel(reg | ADDR_10BIT, i2c->base + IC_CON); + } + } else { + if ((reg & ADDR_10BIT) != 0x0) { + dev_dbg(&adap->dev, "set i2c 7 bit address mode\n"); + writel(reg & ~ADDR_10BIT, i2c->base + IC_CON); + } + } + /* enable restart conditions */ + reg = readl(i2c->base + IC_CON); + if ((reg & RESTART) != RESTART) { + dev_dbg(&adap->dev, "enable restart conditions\n"); + writel(reg | RESTART, i2c->base + IC_CON); + } + + /* enable master FSM */ + reg = readl(i2c->base + IC_CON); + dev_dbg(&adap->dev, "ic_con reg is 0x%x\n", reg); + writel(reg | MASTER_EN, i2c->base + IC_CON); + if ((reg & SLV_DIS) != SLV_DIS) { + dev_dbg(&adap->dev, "enable master FSM\n"); + writel(reg | SLV_DIS, i2c->base + IC_CON); + dev_dbg(&adap->dev, "ic_con reg is 0x%x\n", reg); + } + + /* use target address when initiating transfer */ + reg = readl(i2c->base + IC_TAR); + bit_mask = IC_TAR_SPECIAL | IC_TAR_GC_OR_START; + + if ((reg & bit_mask) != 0x0) { + dev_dbg(&adap->dev, + "WR: use target address when intiating transfer, i2c_tx_target\n"); + writel(reg & ~bit_mask, i2c->base + IC_TAR); + } + + /* set target address to the I2C slave address */ + dev_dbg(&adap->dev, + "set target address to the I2C slave address, addr is %x\n", + pmsg->addr); + writel(pmsg->addr | (pmsg->flags & I2C_M_TEN ? IC_TAR_10BIT_ADDR : 0), + i2c->base + IC_TAR); + + /* Enable I2C controller */ + writel(ENABLE, i2c->base + IC_ENABLE); + + return 0; +} + +/** + * intel_mid_i2c_xfer - Main master transfer routine. + * @adap: i2c_adapter struct pointer + * @pmsg: i2c_msg struct pointer + * @num: number of i2c_msg + * + * Return Values: + * + number of messages transferred + * -ETIMEDOUT If cannot disable I2C controller or read IC_STATUS + * -EINVAL If the address in i2c_msg is invalid + * + * This function will be registered in i2c-core and exposed to external + * I2C clients. + * 1. Disable I2C controller + * 2. Unmask three interrupts: RX_FULL, TX_EMPTY, TX_ABRT + * 3. Check if address in i2c_msg is valid + * 4. Enable I2C controller + * 5. Perform real transfer (call xfer_read or xfer_write) + * 6. Wait until the current transfer is finished (check bus state) + * 7. Mask and clear all interrupts + */ +static int intel_mid_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *pmsg, + int num) +{ + struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); + int i, err = 0; + + /* if number of messages equal 0*/ + if (num == 0) + return 0; + + pm_runtime_get(i2c->dev); + + mutex_lock(&i2c->lock); + dev_dbg(&adap->dev, "intel_mid_i2c_xfer, process %d msg(s)\n", num); + dev_dbg(&adap->dev, "slave address is %x\n", pmsg->addr); + + + if (i2c->status != STATUS_IDLE) { + dev_err(&adap->dev, "Adapter %d in transfer/standby\n", + adap->nr); + mutex_unlock(&i2c->lock); + pm_runtime_put(i2c->dev); + return -1; + } + + + for (i = 1; i < num; i++) { + /* Message address equal? */ + if (unlikely(intel_mid_i2c_address_neq(&pmsg[0], &pmsg[i]))) { + dev_err(&adap->dev, "Invalid address in msg[%d]\n", i); + mutex_unlock(&i2c->lock); + pm_runtime_put(i2c->dev); + return -EINVAL; + } + } + + if (intel_mid_i2c_setup(adap, pmsg)) { + mutex_unlock(&i2c->lock); + pm_runtime_put(i2c->dev); + return -EINVAL; + } + + for (i = 0; i < num; i++) { + i2c->msg = pmsg; + i2c->status = STATUS_IDLE; + /* Read or Write */ + if (pmsg->flags & I2C_M_RD) { + dev_dbg(&adap->dev, "I2C_M_RD\n"); + err = xfer_read(adap, pmsg->buf, pmsg->len); + } else { + dev_dbg(&adap->dev, "I2C_M_WR\n"); + err = xfer_write(adap, pmsg->buf, pmsg->len); + } + if (err < 0) + break; + dev_dbg(&adap->dev, "msg[%d] transfer complete\n", i); + pmsg++; /* next message */ + } + + /* Mask interrupts */ + writel(0x0000, i2c->base + IC_INTR_MASK); + /* Clear all interrupts */ + readl(i2c->base + IC_CLR_INTR); + + i2c->status = STATUS_IDLE; + mutex_unlock(&i2c->lock); + pm_runtime_put(i2c->dev); + + return err; +} + +static int intel_mid_i2c_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct intel_mid_i2c_private *i2c = pci_get_drvdata(pdev); + struct i2c_adapter *adap = to_i2c_adapter(dev); + int err; + + if (i2c->status != STATUS_IDLE) + return -1; + + intel_mid_i2c_disable(adap); + + err = pci_save_state(pdev); + if (err) { + dev_err(dev, "pci_save_state failed\n"); + return err; + } + + err = pci_set_power_state(pdev, PCI_D3hot); + if (err) { + dev_err(dev, "pci_set_power_state failed\n"); + return err; + } + i2c->status = STATUS_STANDBY; + + return 0; +} + +static int intel_mid_i2c_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct intel_mid_i2c_private *i2c = pci_get_drvdata(pdev); + int err; + + if (i2c->status != STATUS_STANDBY) + return 0; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + err = pci_enable_device(pdev); + if (err) { + dev_err(dev, "pci_enable_device failed\n"); + return err; + } + + i2c->status = STATUS_IDLE; + + intel_mid_i2c_hwinit(i2c); + return err; +} + +static void i2c_isr_read(struct intel_mid_i2c_private *i2c) +{ + struct i2c_msg *msg = i2c->msg; + int rx_num; + u32 len; + u8 *buf; + + if (!(msg->flags & I2C_M_RD)) + return; + + if (i2c->status != STATUS_READ_IN_PROGRESS) { + len = msg->len; + buf = msg->buf; + } else { + len = i2c->rx_buf_len; + buf = i2c->rx_buf; + } + + rx_num = readl(i2c->base + IC_RXFLR); + + for (; len > 0 && rx_num > 0; len--, rx_num--) + *buf++ = readl(i2c->base + IC_DATA_CMD); + + if (len > 0) { + i2c->status = STATUS_READ_IN_PROGRESS; + i2c->rx_buf_len = len; + i2c->rx_buf = buf; + } else + i2c->status = STATUS_READ_SUCCESS; + + return; +} + +static irqreturn_t intel_mid_i2c_isr(int this_irq, void *dev) +{ + struct intel_mid_i2c_private *i2c = dev; + u32 stat = readl(i2c->base + IC_INTR_STAT); + + if (!stat) + return IRQ_NONE; + + dev_dbg(&i2c->adap.dev, "%s, stat = 0x%x\n", __func__, stat); + stat &= 0x54; + + if (i2c->status != STATUS_WRITE_START && + i2c->status != STATUS_READ_START && + i2c->status != STATUS_READ_IN_PROGRESS) + goto err; + + if (stat & TX_ABRT) + i2c->abort = readl(i2c->base + IC_TX_ABRT_SOURCE); + + readl(i2c->base + IC_CLR_INTR); + + if (stat & TX_ABRT) { + intel_mid_i2c_abort(i2c); + goto exit; + } + + if (stat & RX_FULL) { + i2c_isr_read(i2c); + goto exit; + } + + if (stat & TX_EMPTY) { + if (readl(i2c->base + IC_STATUS) & 0x4) + i2c->status = STATUS_WRITE_SUCCESS; + } + +exit: + if (i2c->status == STATUS_READ_SUCCESS || + i2c->status == STATUS_WRITE_SUCCESS || + i2c->status == STATUS_XFER_ABORT) { + /* Clear all interrupts */ + readl(i2c->base + IC_CLR_INTR); + /* Mask interrupts */ + writel(0, i2c->base + IC_INTR_MASK); + complete(&i2c->complete); + } +err: + return IRQ_HANDLED; +} + +static struct i2c_algorithm intel_mid_i2c_algorithm = { + .master_xfer = intel_mid_i2c_xfer, + .functionality = intel_mid_i2c_func, +}; + + +static const struct dev_pm_ops intel_mid_i2c_pm_ops = { + .runtime_suspend = intel_mid_i2c_runtime_suspend, + .runtime_resume = intel_mid_i2c_runtime_resume, +}; + +/** + * intel_mid_i2c_probe - I2C controller initialization routine + * @dev: pci device + * @id: device id + * + * Return Values: + * 0 success + * -ENODEV If cannot allocate pci resource + * -ENOMEM If the register base remapping failed, or + * if kzalloc failed + * + * Initialization steps: + * 1. Request for PCI resource + * 2. Remap the start address of PCI resource to register base + * 3. Request for device memory region + * 4. Fill in the struct members of intel_mid_i2c_private + * 5. Call intel_mid_i2c_hwinit() for hardware initialization + * 6. Register I2C adapter in i2c-core + */ +static int __devinit intel_mid_i2c_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct intel_mid_i2c_private *mrst; + unsigned long start, len; + int err, busnum; + void __iomem *base = NULL; + + dev_dbg(&dev->dev, "Get into probe function for I2C\n"); + err = pci_enable_device(dev); + if (err) { + dev_err(&dev->dev, "Failed to enable I2C PCI device (%d)\n", + err); + goto exit; + } + + /* Determine the address of the I2C area */ + start = pci_resource_start(dev, 0); + len = pci_resource_len(dev, 0); + if (!start || len == 0) { + dev_err(&dev->dev, "base address not set\n"); + err = -ENODEV; + goto exit; + } + dev_dbg(&dev->dev, "%s i2c resource start 0x%lx, len=%ld\n", + PLATFORM, start, len); + + err = pci_request_region(dev, 0, DRIVER_NAME); + if (err) { + dev_err(&dev->dev, "failed to request I2C region " + "0x%lx-0x%lx\n", start, + (unsigned long)pci_resource_end(dev, 0)); + goto exit; + } + + base = ioremap_nocache(start, len); + if (!base) { + dev_err(&dev->dev, "I/O memory remapping failed\n"); + err = -ENOMEM; + goto fail0; + } + + /* Allocate the per-device data structure, intel_mid_i2c_private */ + mrst = kzalloc(sizeof(struct intel_mid_i2c_private), GFP_KERNEL); + if (mrst == NULL) { + dev_err(&dev->dev, "can't allocate interface\n"); + err = -ENOMEM; + goto fail1; + } + + /* Initialize struct members */ + snprintf(mrst->adap.name, sizeof(mrst->adap.name), + "Intel MID I2C at %lx", start); + mrst->adap.owner = THIS_MODULE; + mrst->adap.algo = &intel_mid_i2c_algorithm; + mrst->adap.dev.parent = &dev->dev; + mrst->dev = &dev->dev; + mrst->base = base; + mrst->speed = STANDARD; + mrst->abort = 0; + mrst->rx_buf_len = 0; + mrst->status = STATUS_IDLE; + + pci_set_drvdata(dev, mrst); + i2c_set_adapdata(&mrst->adap, mrst); + + mrst->adap.nr = busnum = id->driver_data; + if (dev->device <= 0x0804) + mrst->platform = MOORESTOWN; + else + mrst->platform = MEDFIELD; + + dev_dbg(&dev->dev, "I2C%d\n", busnum); + + if (ctl_num > busnum) { + if (speed_mode[busnum] < 0 || speed_mode[busnum] >= NUM_SPEEDS) + dev_warn(&dev->dev, "invalid speed %d ignored.\n", + speed_mode[busnum]); + else + mrst->speed = speed_mode[busnum]; + } + + /* Initialize i2c controller */ + err = intel_mid_i2c_hwinit(mrst); + if (err < 0) { + dev_err(&dev->dev, "I2C interface initialization failed\n"); + goto fail2; + } + + mutex_init(&mrst->lock); + init_completion(&mrst->complete); + + /* Clear all interrupts */ + readl(mrst->base + IC_CLR_INTR); + writel(0x0000, mrst->base + IC_INTR_MASK); + + err = request_irq(dev->irq, intel_mid_i2c_isr, IRQF_SHARED, + mrst->adap.name, mrst); + if (err) { + dev_err(&dev->dev, "Failed to request IRQ for I2C controller: " + "%s", mrst->adap.name); + goto fail2; + } + + /* Adapter registration */ + err = i2c_add_numbered_adapter(&mrst->adap); + if (err) { + dev_err(&dev->dev, "Adapter %s registration failed\n", + mrst->adap.name); + goto fail3; + } + + dev_dbg(&dev->dev, "%s I2C bus %d driver bind success.\n", + (mrst->platform == MOORESTOWN) ? "Moorestown" : "Medfield", + busnum); + + pm_runtime_enable(&dev->dev); + return 0; + +fail3: + free_irq(dev->irq, mrst); +fail2: + pci_set_drvdata(dev, NULL); + kfree(mrst); +fail1: + iounmap(base); +fail0: + pci_release_region(dev, 0); +exit: + return err; +} + +static void __devexit intel_mid_i2c_remove(struct pci_dev *dev) +{ + struct intel_mid_i2c_private *mrst = pci_get_drvdata(dev); + intel_mid_i2c_disable(&mrst->adap); + if (i2c_del_adapter(&mrst->adap)) + dev_err(&dev->dev, "Failed to delete i2c adapter"); + + free_irq(dev->irq, mrst); + pci_set_drvdata(dev, NULL); + iounmap(mrst->base); + kfree(mrst); + pci_release_region(dev, 0); +} + +static struct pci_device_id intel_mid_i2c_ids[] = { + /* Moorestown */ + { PCI_VDEVICE(INTEL, 0x0802), 0 }, + { PCI_VDEVICE(INTEL, 0x0803), 1 }, + { PCI_VDEVICE(INTEL, 0x0804), 2 }, + /* Medfield */ + { PCI_VDEVICE(INTEL, 0x0817), 3,}, + { PCI_VDEVICE(INTEL, 0x0818), 4 }, + { PCI_VDEVICE(INTEL, 0x0819), 5 }, + { PCI_VDEVICE(INTEL, 0x082C), 0 }, + { PCI_VDEVICE(INTEL, 0x082D), 1 }, + { PCI_VDEVICE(INTEL, 0x082E), 2 }, + { 0,} +}; +MODULE_DEVICE_TABLE(pci, intel_mid_i2c_ids); + +static struct pci_driver intel_mid_i2c_driver = { + .name = DRIVER_NAME, + .id_table = intel_mid_i2c_ids, + .probe = intel_mid_i2c_probe, + .remove = __devexit_p(intel_mid_i2c_remove), +}; + +static int __init intel_mid_i2c_init(void) +{ + return pci_register_driver(&intel_mid_i2c_driver); +} + +static void __exit intel_mid_i2c_exit(void) +{ + pci_unregister_driver(&intel_mid_i2c_driver); +} + +module_init(intel_mid_i2c_init); +module_exit(intel_mid_i2c_exit); + +MODULE_AUTHOR("Ba Zheng <zheng.ba@intel.com>"); +MODULE_DESCRIPTION("I2C driver for Moorestown Platform"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(VERSION); diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c new file mode 100644 index 00000000..f09c9319 --- /dev/null +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -0,0 +1,545 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx & IXP46x */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd + * <Peter dot Milne at D hyphen TACQ dot com> + * + * With acknowledgements to i2c-algo-ibm_ocp.c by + * Ian DaSilva, MontaVista Software, Inc. idasilva@mvista.com + * + * And i2c-algo-pcf.c, which was created by Simon G. Vogl and Hans Berglund: + * + * Copyright (C) 1995-1997 Simon G. Vogl, 1998-2000 Hans Berglund + * + * And which acknowledged Kyösti Mälkki <kmalkki@cc.hut.fi>, + * Frodo Looijaard <frodol@dds.nl>, Martin Bailey<mbailey@littlefeet-inc.com> + * + * Major cleanup by Deepak Saxena <dsaxena@plexity.net>, 01/2005: + * + * - Use driver model to pass per-chip info instead of hardcoding and #ifdefs + * - Use ioremap/__raw_readl/__raw_writel instead of direct dereference + * - Make it work with IXP46x chips + * - Cleanup function names, coding style, etc + * + * - writing to slave address causes latchup on iop331. + * fix: driver refuses to address self. + * + * 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. + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/io.h> + +#include "i2c-iop3xx.h" + +/* global unit counter */ +static int i2c_id; + +static inline unsigned char +iic_cook_addr(struct i2c_msg *msg) +{ + unsigned char addr; + + addr = (msg->addr << 1); + + if (msg->flags & I2C_M_RD) + addr |= 1; + + return addr; +} + +static void +iop3xx_i2c_reset(struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + /* Follows devman 9.3 */ + __raw_writel(IOP3XX_ICR_UNIT_RESET, iop3xx_adap->ioaddr + CR_OFFSET); + __raw_writel(IOP3XX_ISR_CLEARBITS, iop3xx_adap->ioaddr + SR_OFFSET); + __raw_writel(0, iop3xx_adap->ioaddr + CR_OFFSET); +} + +static void +iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + u32 cr = IOP3XX_ICR_GCD | IOP3XX_ICR_SCLEN | IOP3XX_ICR_UE; + + /* + * Every time unit enable is asserted, GPOD needs to be cleared + * on IOP3XX to avoid data corruption on the bus. + */ +#if defined(CONFIG_ARCH_IOP32X) || defined(CONFIG_ARCH_IOP33X) + if (iop3xx_adap->id == 0) { + gpio_line_set(IOP3XX_GPIO_LINE(7), GPIO_LOW); + gpio_line_set(IOP3XX_GPIO_LINE(6), GPIO_LOW); + } else { + gpio_line_set(IOP3XX_GPIO_LINE(5), GPIO_LOW); + gpio_line_set(IOP3XX_GPIO_LINE(4), GPIO_LOW); + } +#endif + /* NB SR bits not same position as CR IE bits :-( */ + iop3xx_adap->SR_enabled = + IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD | + IOP3XX_ISR_RXFULL | IOP3XX_ISR_TXEMPTY; + + cr |= IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE | + IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE; + + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); +} + +static void +iop3xx_i2c_transaction_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); + + cr &= ~(IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE | + IOP3XX_ICR_MSTOP | IOP3XX_ICR_SCLEN); + + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); +} + +/* + * NB: the handler has to clear the source of the interrupt! + * Then it passes the SR flags of interest to BH via adap data + */ +static irqreturn_t +iop3xx_i2c_irq_handler(int this_irq, void *dev_id) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id; + u32 sr = __raw_readl(iop3xx_adap->ioaddr + SR_OFFSET); + + if ((sr &= iop3xx_adap->SR_enabled)) { + __raw_writel(sr, iop3xx_adap->ioaddr + SR_OFFSET); + iop3xx_adap->SR_received |= sr; + wake_up_interruptible(&iop3xx_adap->waitq); + } + return IRQ_HANDLED; +} + +/* check all error conditions, clear them , report most important */ +static int +iop3xx_i2c_error(u32 sr) +{ + int rc = 0; + + if ((sr & IOP3XX_ISR_BERRD)) { + if ( !rc ) rc = -I2C_ERR_BERR; + } + if ((sr & IOP3XX_ISR_ALD)) { + if ( !rc ) rc = -I2C_ERR_ALD; + } + return rc; +} + +static inline u32 +iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap) +{ + unsigned long flags; + u32 sr; + + spin_lock_irqsave(&iop3xx_adap->lock, flags); + sr = iop3xx_adap->SR_received; + iop3xx_adap->SR_received = 0; + spin_unlock_irqrestore(&iop3xx_adap->lock, flags); + + return sr; +} + +/* + * sleep until interrupted, then recover and analyse the SR + * saved by handler + */ +typedef int (* compare_func)(unsigned test, unsigned mask); +/* returns 1 on correct comparison */ + +static int +iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, + unsigned flags, unsigned* status, + compare_func compare) +{ + unsigned sr = 0; + int interrupted; + int done; + int rc = 0; + + do { + interrupted = wait_event_interruptible_timeout ( + iop3xx_adap->waitq, + (done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )), + 1 * HZ; + ); + if ((rc = iop3xx_i2c_error(sr)) < 0) { + *status = sr; + return rc; + } else if (!interrupted) { + *status = sr; + return -ETIMEDOUT; + } + } while(!done); + + *status = sr; + + return 0; +} + +/* + * Concrete compare_funcs + */ +static int +all_bits_clear(unsigned test, unsigned mask) +{ + return (test & mask) == 0; +} + +static int +any_bits_set(unsigned test, unsigned mask) +{ + return (test & mask) != 0; +} + +static int +iop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) +{ + return iop3xx_i2c_wait_event( + iop3xx_adap, + IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD, + status, any_bits_set); +} + +static int +iop3xx_i2c_wait_rx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) +{ + return iop3xx_i2c_wait_event( + iop3xx_adap, + IOP3XX_ISR_RXFULL | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD, + status, any_bits_set); +} + +static int +iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) +{ + return iop3xx_i2c_wait_event( + iop3xx_adap, IOP3XX_ISR_UNITBUSY, status, all_bits_clear); +} + +static int +iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap, + struct i2c_msg* msg) +{ + unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); + int status; + int rc; + + /* avoid writing to my slave address (hangs on 80331), + * forbidden in Intel developer manual + */ + if (msg->addr == MYSAR) { + return -EBUSY; + } + + __raw_writel(iic_cook_addr(msg), iop3xx_adap->ioaddr + DBR_OFFSET); + + cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK); + cr |= IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE; + + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); + rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status); + + return rc; +} + +static int +iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte, + int stop) +{ + unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); + int status; + int rc = 0; + + __raw_writel(byte, iop3xx_adap->ioaddr + DBR_OFFSET); + cr &= ~IOP3XX_ICR_MSTART; + if (stop) { + cr |= IOP3XX_ICR_MSTOP; + } else { + cr &= ~IOP3XX_ICR_MSTOP; + } + cr |= IOP3XX_ICR_TBYTE; + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); + rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status); + + return rc; +} + +static int +iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte, + int stop) +{ + unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); + int status; + int rc = 0; + + cr &= ~IOP3XX_ICR_MSTART; + + if (stop) { + cr |= IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK; + } else { + cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK); + } + cr |= IOP3XX_ICR_TBYTE; + __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); + + rc = iop3xx_i2c_wait_rx_done(iop3xx_adap, &status); + + *byte = __raw_readl(iop3xx_adap->ioaddr + DBR_OFFSET); + + return rc; +} + +static int +iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int ii; + int rc = 0; + + for (ii = 0; rc == 0 && ii != count; ++ii) + rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1); + return rc; +} + +static int +iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int ii; + int rc = 0; + + for (ii = 0; rc == 0 && ii != count; ++ii) + rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1); + + return rc; +} + +/* + * Description: This function implements combined transactions. Combined + * transactions consist of combinations of reading and writing blocks of data. + * FROM THE SAME ADDRESS + * Each transfer (i.e. a read or a write) is separated by a repeated start + * condition. + */ +static int +iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int rc; + + rc = iop3xx_i2c_send_target_addr(iop3xx_adap, pmsg); + if (rc < 0) { + return rc; + } + + if ((pmsg->flags&I2C_M_RD)) { + return iop3xx_i2c_readbytes(i2c_adap, pmsg->buf, pmsg->len); + } else { + return iop3xx_i2c_writebytes(i2c_adap, pmsg->buf, pmsg->len); + } +} + +/* + * master_xfer() - main read/write entry + */ +static int +iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; + int im = 0; + int ret = 0; + int status; + + iop3xx_i2c_wait_idle(iop3xx_adap, &status); + iop3xx_i2c_reset(iop3xx_adap); + iop3xx_i2c_enable(iop3xx_adap); + + for (im = 0; ret == 0 && im != num; im++) { + ret = iop3xx_i2c_handle_msg(i2c_adap, &msgs[im]); + } + + iop3xx_i2c_transaction_cleanup(iop3xx_adap); + + if(ret) + return ret; + + return im; +} + +static u32 +iop3xx_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm iop3xx_i2c_algo = { + .master_xfer = iop3xx_i2c_master_xfer, + .functionality = iop3xx_i2c_func, +}; + +static int +iop3xx_i2c_remove(struct platform_device *pdev) +{ + struct i2c_adapter *padapter = platform_get_drvdata(pdev); + struct i2c_algo_iop3xx_data *adapter_data = + (struct i2c_algo_iop3xx_data *)padapter->algo_data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + unsigned long cr = __raw_readl(adapter_data->ioaddr + CR_OFFSET); + + /* + * Disable the actual HW unit + */ + cr &= ~(IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE | + IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE); + __raw_writel(cr, adapter_data->ioaddr + CR_OFFSET); + + iounmap(adapter_data->ioaddr); + release_mem_region(res->start, IOP3XX_I2C_IO_SIZE); + kfree(adapter_data); + kfree(padapter); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int +iop3xx_i2c_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret, irq; + struct i2c_adapter *new_adapter; + struct i2c_algo_iop3xx_data *adapter_data; + + new_adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (!new_adapter) { + ret = -ENOMEM; + goto out; + } + + adapter_data = kzalloc(sizeof(struct i2c_algo_iop3xx_data), GFP_KERNEL); + if (!adapter_data) { + ret = -ENOMEM; + goto free_adapter; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto free_both; + } + + if (!request_mem_region(res->start, IOP3XX_I2C_IO_SIZE, pdev->name)) { + ret = -EBUSY; + goto free_both; + } + + /* set the adapter enumeration # */ + adapter_data->id = i2c_id++; + + adapter_data->ioaddr = ioremap(res->start, IOP3XX_I2C_IO_SIZE); + if (!adapter_data->ioaddr) { + ret = -ENOMEM; + goto release_region; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = -ENXIO; + goto unmap; + } + ret = request_irq(irq, iop3xx_i2c_irq_handler, 0, + pdev->name, adapter_data); + + if (ret) { + ret = -EIO; + goto unmap; + } + + memcpy(new_adapter->name, pdev->name, strlen(pdev->name)); + new_adapter->owner = THIS_MODULE; + new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + new_adapter->dev.parent = &pdev->dev; + new_adapter->nr = pdev->id; + + /* + * Default values...should these come in from board code? + */ + new_adapter->timeout = HZ; + new_adapter->algo = &iop3xx_i2c_algo; + + init_waitqueue_head(&adapter_data->waitq); + spin_lock_init(&adapter_data->lock); + + iop3xx_i2c_reset(adapter_data); + iop3xx_i2c_enable(adapter_data); + + platform_set_drvdata(pdev, new_adapter); + new_adapter->algo_data = adapter_data; + + i2c_add_numbered_adapter(new_adapter); + + return 0; + +unmap: + iounmap(adapter_data->ioaddr); + +release_region: + release_mem_region(res->start, IOP3XX_I2C_IO_SIZE); + +free_both: + kfree(adapter_data); + +free_adapter: + kfree(new_adapter); + +out: + return ret; +} + + +static struct platform_driver iop3xx_i2c_driver = { + .probe = iop3xx_i2c_probe, + .remove = iop3xx_i2c_remove, + .driver = { + .owner = THIS_MODULE, + .name = "IOP3xx-I2C", + }, +}; + +static int __init +i2c_iop3xx_init (void) +{ + return platform_driver_register(&iop3xx_i2c_driver); +} + +static void __exit +i2c_iop3xx_exit (void) +{ + platform_driver_unregister(&iop3xx_i2c_driver); + return; +} + +module_init (i2c_iop3xx_init); +module_exit (i2c_iop3xx_exit); + +MODULE_AUTHOR("D-TACQ Solutions Ltd <www.d-tacq.com>"); +MODULE_DESCRIPTION("IOP3xx iic algorithm and driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:IOP3xx-I2C"); diff --git a/drivers/i2c/busses/i2c-iop3xx.h b/drivers/i2c/busses/i2c-iop3xx.h new file mode 100644 index 00000000..097e2709 --- /dev/null +++ b/drivers/i2c/busses/i2c-iop3xx.h @@ -0,0 +1,107 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-iop3xx.h algorithm driver definitions private to i2c-iop3xx.c */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd + * <Peter dot Milne at D hyphen TACQ dot 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. + + 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. */ +/* ------------------------------------------------------------------------- */ + + +#ifndef I2C_IOP3XX_H +#define I2C_IOP3XX_H 1 + +/* + * iop321 hardware bit definitions + */ +#define IOP3XX_ICR_FAST_MODE 0x8000 /* 1=400kBps, 0=100kBps */ +#define IOP3XX_ICR_UNIT_RESET 0x4000 /* 1=RESET */ +#define IOP3XX_ICR_SAD_IE 0x2000 /* 1=Slave Detect Interrupt Enable */ +#define IOP3XX_ICR_ALD_IE 0x1000 /* 1=Arb Loss Detect Interrupt Enable */ +#define IOP3XX_ICR_SSD_IE 0x0800 /* 1=Slave STOP Detect Interrupt Enable */ +#define IOP3XX_ICR_BERR_IE 0x0400 /* 1=Bus Error Interrupt Enable */ +#define IOP3XX_ICR_RXFULL_IE 0x0200 /* 1=Receive Full Interrupt Enable */ +#define IOP3XX_ICR_TXEMPTY_IE 0x0100 /* 1=Transmit Empty Interrupt Enable */ +#define IOP3XX_ICR_GCD 0x0080 /* 1=General Call Disable */ +/* + * IOP3XX_ICR_GCD: 1 disables response as slave. "This bit must be set + * when sending a master mode general call message from the I2C unit" + */ +#define IOP3XX_ICR_UE 0x0040 /* 1=Unit Enable */ +/* + * "NOTE: To avoid I2C bus integrity problems, + * the user needs to ensure that the GPIO Output Data Register - + * GPOD bits associated with an I2C port are cleared prior to setting + * the enable bit for that I2C serial port. + * The user prepares to enable I2C port 0 and + * I2C port 1 by clearing GPOD bits 7:6 and GPOD bits 5:4, respectively. + */ +#define IOP3XX_ICR_SCLEN 0x0020 /* 1=SCL enable for master mode */ +#define IOP3XX_ICR_MABORT 0x0010 /* 1=Send a STOP with no data + * NB TBYTE must be clear */ +#define IOP3XX_ICR_TBYTE 0x0008 /* 1=Send/Receive a byte. i2c clears */ +#define IOP3XX_ICR_NACK 0x0004 /* 1=reply with NACK */ +#define IOP3XX_ICR_MSTOP 0x0002 /* 1=send a STOP after next data byte */ +#define IOP3XX_ICR_MSTART 0x0001 /* 1=initiate a START */ + + +#define IOP3XX_ISR_BERRD 0x0400 /* 1=BUS ERROR Detected */ +#define IOP3XX_ISR_SAD 0x0200 /* 1=Slave ADdress Detected */ +#define IOP3XX_ISR_GCAD 0x0100 /* 1=General Call Address Detected */ +#define IOP3XX_ISR_RXFULL 0x0080 /* 1=Receive Full */ +#define IOP3XX_ISR_TXEMPTY 0x0040 /* 1=Transmit Empty */ +#define IOP3XX_ISR_ALD 0x0020 /* 1=Arbitration Loss Detected */ +#define IOP3XX_ISR_SSD 0x0010 /* 1=Slave STOP Detected */ +#define IOP3XX_ISR_BBUSY 0x0008 /* 1=Bus BUSY */ +#define IOP3XX_ISR_UNITBUSY 0x0004 /* 1=Unit Busy */ +#define IOP3XX_ISR_NACK 0x0002 /* 1=Unit Rx or Tx a NACK */ +#define IOP3XX_ISR_RXREAD 0x0001 /* 1=READ 0=WRITE (R/W bit of slave addr */ + +#define IOP3XX_ISR_CLEARBITS 0x07f0 + +#define IOP3XX_ISAR_SAMASK 0x007f + +#define IOP3XX_IDBR_MASK 0x00ff + +#define IOP3XX_IBMR_SCL 0x0002 +#define IOP3XX_IBMR_SDA 0x0001 + +#define IOP3XX_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */ +#define IOP3XX_GPOD_I2C1 0x0030 /* clear these bits to enable ch1 */ + +#define MYSAR 0 /* default slave address */ + +#define I2C_ERR 321 +#define I2C_ERR_BERR (I2C_ERR+0) +#define I2C_ERR_ALD (I2C_ERR+1) + + +#define CR_OFFSET 0 +#define SR_OFFSET 0x4 +#define SAR_OFFSET 0x8 +#define DBR_OFFSET 0xc +#define CCR_OFFSET 0x10 +#define BMR_OFFSET 0x14 + +#define IOP3XX_I2C_IO_SIZE 0x18 + +struct i2c_algo_iop3xx_data { + void __iomem *ioaddr; + wait_queue_head_t waitq; + spinlock_t lock; + u32 SR_enabled, SR_received; + int id; +}; + +#endif /* I2C_IOP3XX_H */ diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c new file mode 100644 index 00000000..0682f8f2 --- /dev/null +++ b/drivers/i2c/busses/i2c-isch.c @@ -0,0 +1,325 @@ +/* + i2c-isch.c - Linux kernel driver for Intel SCH chipset SMBus + - Based on i2c-piix4.c + Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and + Philip Edelbrock <phil@netroedge.com> + - Intel SCH support + Copyright (c) 2007 - 2008 Jacob Jun Pan <jacob.jun.pan@intel.com> + + 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. + + 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. +*/ + +/* + Supports: + Intel SCH chipsets (AF82US15W, AF82US15L, AF82UL11L) + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/acpi.h> + +/* SCH SMBus address offsets */ +#define SMBHSTCNT (0 + sch_smba) +#define SMBHSTSTS (1 + sch_smba) +#define SMBHSTADD (4 + sch_smba) /* TSA */ +#define SMBHSTCMD (5 + sch_smba) +#define SMBHSTDAT0 (6 + sch_smba) +#define SMBHSTDAT1 (7 + sch_smba) +#define SMBBLKDAT (0x20 + sch_smba) + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* I2C constants */ +#define SCH_QUICK 0x00 +#define SCH_BYTE 0x01 +#define SCH_BYTE_DATA 0x02 +#define SCH_WORD_DATA 0x03 +#define SCH_BLOCK_DATA 0x05 + +static unsigned short sch_smba; +static struct i2c_adapter sch_adapter; + +/* + * Start the i2c transaction -- the i2c_access will prepare the transaction + * and this function will execute it. + * return 0 for success and others for failure. + */ +static int sch_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&sch_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT), + inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0), + inb(SMBHSTDAT1)); + + /* Make sure the SMBus host is ready to start transmitting */ + temp = inb(SMBHSTSTS) & 0x0f; + if (temp) { + /* Can not be busy since we checked it in sch_access */ + if (temp & 0x01) { + dev_dbg(&sch_adapter.dev, "Completion (%02x). " + "Clear...\n", temp); + } + if (temp & 0x06) { + dev_dbg(&sch_adapter.dev, "SMBus error (%02x). " + "Resetting...\n", temp); + } + outb(temp, SMBHSTSTS); + temp = inb(SMBHSTSTS) & 0x0f; + if (temp) { + dev_err(&sch_adapter.dev, + "SMBus is not ready: (%02x)\n", temp); + return -EAGAIN; + } + } + + /* start the transaction by setting bit 4 */ + outb(inb(SMBHSTCNT) | 0x10, SMBHSTCNT); + + do { + msleep(1); + temp = inb(SMBHSTSTS) & 0x0f; + } while ((temp & 0x08) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_err(&sch_adapter.dev, "SMBus Timeout!\n"); + result = -ETIMEDOUT; + } + if (temp & 0x04) { + result = -EIO; + dev_dbg(&sch_adapter.dev, "Bus collision! SMBus may be " + "locked until next hard reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } else if (temp & 0x02) { + result = -EIO; + dev_err(&sch_adapter.dev, "Error: no response!\n"); + } else if (temp & 0x01) { + dev_dbg(&sch_adapter.dev, "Post complete!\n"); + outb(temp, SMBHSTSTS); + temp = inb(SMBHSTSTS) & 0x07; + if (temp & 0x06) { + /* Completion clear failed */ + dev_dbg(&sch_adapter.dev, "Failed reset at end of " + "transaction (%02x), Bus error!\n", temp); + } + } else { + result = -ENXIO; + dev_dbg(&sch_adapter.dev, "No such address.\n"); + } + dev_dbg(&sch_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT), + inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0), + inb(SMBHSTDAT1)); + return result; +} + +/* + * This is the main access entry for i2c-sch access + * adap is i2c_adapter pointer, addr is the i2c device bus address, read_write + * (0 for read and 1 for write), size is i2c transaction type and data is the + * union of transaction for data to be transferred or data read from bus. + * return 0 for success and others for failure. + */ +static s32 sch_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + int i, len, temp, rc; + + /* Make sure the SMBus host is not busy */ + temp = inb(SMBHSTSTS) & 0x0f; + if (temp & 0x08) { + dev_dbg(&sch_adapter.dev, "SMBus busy (%02x)\n", temp); + return -EAGAIN; + } + dev_dbg(&sch_adapter.dev, "access size: %d %s\n", size, + (read_write)?"READ":"WRITE"); + switch (size) { + case I2C_SMBUS_QUICK: + outb((addr << 1) | read_write, SMBHSTADD); + size = SCH_QUICK; + break; + case I2C_SMBUS_BYTE: + outb((addr << 1) | read_write, SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb(command, SMBHSTCMD); + size = SCH_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb((addr << 1) | read_write, SMBHSTADD); + outb(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb(data->byte, SMBHSTDAT0); + size = SCH_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb((addr << 1) | read_write, SMBHSTADD); + outb(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb(data->word & 0xff, SMBHSTDAT0); + outb((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = SCH_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb((addr << 1) | read_write, SMBHSTADD); + outb(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + outb(len, SMBHSTDAT0); + for (i = 1; i <= len; i++) + outb(data->block[i], SMBBLKDAT+i-1); + } + size = SCH_BLOCK_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + dev_dbg(&sch_adapter.dev, "write size %d to 0x%04x\n", size, SMBHSTCNT); + outb((inb(SMBHSTCNT) & 0xb0) | (size & 0x7), SMBHSTCNT); + + rc = sch_transaction(); + if (rc) /* Error in transaction */ + return rc; + + if ((read_write == I2C_SMBUS_WRITE) || (size == SCH_QUICK)) + return 0; + + switch (size) { + case SCH_BYTE: + case SCH_BYTE_DATA: + data->byte = inb(SMBHSTDAT0); + break; + case SCH_WORD_DATA: + data->word = inb(SMBHSTDAT0) + (inb(SMBHSTDAT1) << 8); + break; + case SCH_BLOCK_DATA: + data->block[0] = inb(SMBHSTDAT0); + if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb(SMBBLKDAT+i-1); + break; + } + return 0; +} + +static u32 sch_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = sch_access, + .functionality = sch_func, +}; + +static struct i2c_adapter sch_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, +}; + +static int __devinit smbus_sch_probe(struct platform_device *dev) +{ + struct resource *res; + int retval; + + res = platform_get_resource(dev, IORESOURCE_IO, 0); + if (!res) + return -EBUSY; + + if (!request_region(res->start, resource_size(res), dev->name)) { + dev_err(&dev->dev, "SMBus region 0x%x already in use!\n", + sch_smba); + return -EBUSY; + } + + sch_smba = res->start; + + dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba); + + /* set up the sysfs linkage to our parent device */ + sch_adapter.dev.parent = &dev->dev; + + snprintf(sch_adapter.name, sizeof(sch_adapter.name), + "SMBus SCH adapter at %04x", sch_smba); + + retval = i2c_add_adapter(&sch_adapter); + if (retval) { + dev_err(&dev->dev, "Couldn't register adapter!\n"); + release_region(res->start, resource_size(res)); + sch_smba = 0; + } + + return retval; +} + +static int __devexit smbus_sch_remove(struct platform_device *pdev) +{ + struct resource *res; + if (sch_smba) { + i2c_del_adapter(&sch_adapter); + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start, resource_size(res)); + sch_smba = 0; + } + + return 0; +} + +static struct platform_driver smbus_sch_driver = { + .driver = { + .name = "isch_smbus", + .owner = THIS_MODULE, + }, + .probe = smbus_sch_probe, + .remove = __devexit_p(smbus_sch_remove), +}; + +static int __init i2c_sch_init(void) +{ + return platform_driver_register(&smbus_sch_driver); +} + +static void __exit i2c_sch_exit(void) +{ + platform_driver_unregister(&smbus_sch_driver); +} + +MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>"); +MODULE_DESCRIPTION("Intel SCH SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_sch_init); +module_exit(i2c_sch_exit); +MODULE_ALIAS("platform:isch_smbus"); diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c new file mode 100644 index 00000000..5d8aed5e --- /dev/null +++ b/drivers/i2c/busses/i2c-ixp2000.c @@ -0,0 +1,168 @@ +/* + * drivers/i2c/busses/i2c-ixp2000.c + * + * I2C adapter for IXP2000 systems using GPIOs for I2C bus + * + * Author: Deepak Saxena <dsaxena@plexity.net> + * Based on IXDP2400 code by: Naeem M. Afzal <naeem.m.afzal@intel.com> + * Made generic by: Jeff Daly <jeffrey.daly@intel.com> + * + * Copyright (c) 2003-2004 MontaVista Software Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * From Jeff Daly: + * + * I2C adapter driver for Intel IXDP2xxx platforms. This should work for any + * IXP2000 platform if it uses the HW GPIO in the same manner. Basically, + * SDA and SCL GPIOs have external pullups. Setting the respective GPIO to + * an input will make the signal a '1' via the pullup. Setting them to + * outputs will pull them down. + * + * The GPIOs are open drain signals and are used as configuration strap inputs + * during power-up so there's generally a buffer on the board that needs to be + * 'enabled' to drive the GPIOs. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/slab.h> + +#include <mach/hardware.h> /* Pick up IXP2000-specific bits */ +#include <mach/gpio.h> + +static inline int ixp2000_scl_pin(void *data) +{ + return ((struct ixp2000_i2c_pins*)data)->scl_pin; +} + +static inline int ixp2000_sda_pin(void *data) +{ + return ((struct ixp2000_i2c_pins*)data)->sda_pin; +} + + +static void ixp2000_bit_setscl(void *data, int val) +{ + int i = 5000; + + if (val) { + gpio_line_config(ixp2000_scl_pin(data), GPIO_IN); + while(!gpio_line_get(ixp2000_scl_pin(data)) && i--); + } else { + gpio_line_config(ixp2000_scl_pin(data), GPIO_OUT); + } +} + +static void ixp2000_bit_setsda(void *data, int val) +{ + if (val) { + gpio_line_config(ixp2000_sda_pin(data), GPIO_IN); + } else { + gpio_line_config(ixp2000_sda_pin(data), GPIO_OUT); + } +} + +static int ixp2000_bit_getscl(void *data) +{ + return gpio_line_get(ixp2000_scl_pin(data)); +} + +static int ixp2000_bit_getsda(void *data) +{ + return gpio_line_get(ixp2000_sda_pin(data)); +} + +struct ixp2000_i2c_data { + struct ixp2000_i2c_pins *gpio_pins; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo_data; +}; + +static int ixp2000_i2c_remove(struct platform_device *plat_dev) +{ + struct ixp2000_i2c_data *drv_data = platform_get_drvdata(plat_dev); + + platform_set_drvdata(plat_dev, NULL); + + i2c_del_adapter(&drv_data->adapter); + + kfree(drv_data); + + return 0; +} + +static int ixp2000_i2c_probe(struct platform_device *plat_dev) +{ + int err; + struct ixp2000_i2c_pins *gpio = plat_dev->dev.platform_data; + struct ixp2000_i2c_data *drv_data = + kzalloc(sizeof(struct ixp2000_i2c_data), GFP_KERNEL); + + if (!drv_data) + return -ENOMEM; + drv_data->gpio_pins = gpio; + + drv_data->algo_data.data = gpio; + drv_data->algo_data.setsda = ixp2000_bit_setsda; + drv_data->algo_data.setscl = ixp2000_bit_setscl; + drv_data->algo_data.getsda = ixp2000_bit_getsda; + drv_data->algo_data.getscl = ixp2000_bit_getscl; + drv_data->algo_data.udelay = 6; + drv_data->algo_data.timeout = HZ; + + strlcpy(drv_data->adapter.name, plat_dev->dev.driver->name, + sizeof(drv_data->adapter.name)); + drv_data->adapter.algo_data = &drv_data->algo_data, + + drv_data->adapter.dev.parent = &plat_dev->dev; + + gpio_line_config(gpio->sda_pin, GPIO_IN); + gpio_line_config(gpio->scl_pin, GPIO_IN); + gpio_line_set(gpio->scl_pin, 0); + gpio_line_set(gpio->sda_pin, 0); + + if ((err = i2c_bit_add_bus(&drv_data->adapter)) != 0) { + dev_err(&plat_dev->dev, "Could not install, error %d\n", err); + kfree(drv_data); + return err; + } + + platform_set_drvdata(plat_dev, drv_data); + + return 0; +} + +static struct platform_driver ixp2000_i2c_driver = { + .probe = ixp2000_i2c_probe, + .remove = ixp2000_i2c_remove, + .driver = { + .name = "IXP2000-I2C", + .owner = THIS_MODULE, + }, +}; + +static int __init ixp2000_i2c_init(void) +{ + return platform_driver_register(&ixp2000_i2c_driver); +} + +static void __exit ixp2000_i2c_exit(void) +{ + platform_driver_unregister(&ixp2000_i2c_driver); +} + +module_init(ixp2000_i2c_init); +module_exit(ixp2000_i2c_exit); + +MODULE_AUTHOR ("Deepak Saxena <dsaxena@plexity.net>"); +MODULE_DESCRIPTION("IXP2000 GPIO-based I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:IXP2000-I2C"); + diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c new file mode 100644 index 00000000..107397a6 --- /dev/null +++ b/drivers/i2c/busses/i2c-mpc.c @@ -0,0 +1,734 @@ +/* + * (C) Copyright 2003-2004 + * Humboldt Solutions Ltd, adrian@humboldt.co.uk. + + * This is a combined i2c adapter and algorithm driver for the + * MPC107/Tsi107 PowerPC northbridge and processors that include + * the same I2C unit (8240, 8245, 85xx). + * + * Release 0.8 + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/of_platform.h> +#include <linux/of_i2c.h> +#include <linux/slab.h> + +#include <linux/io.h> +#include <linux/fsl_devices.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <asm/mpc52xx.h> +#include <sysdev/fsl_soc.h> + +#define DRV_NAME "mpc-i2c" + +#define MPC_I2C_CLOCK_LEGACY 0 +#define MPC_I2C_CLOCK_PRESERVE (~0U) + +#define MPC_I2C_FDR 0x04 +#define MPC_I2C_CR 0x08 +#define MPC_I2C_SR 0x0c +#define MPC_I2C_DR 0x10 +#define MPC_I2C_DFSRR 0x14 + +#define CCR_MEN 0x80 +#define CCR_MIEN 0x40 +#define CCR_MSTA 0x20 +#define CCR_MTX 0x10 +#define CCR_TXAK 0x08 +#define CCR_RSTA 0x04 + +#define CSR_MCF 0x80 +#define CSR_MAAS 0x40 +#define CSR_MBB 0x20 +#define CSR_MAL 0x10 +#define CSR_SRW 0x04 +#define CSR_MIF 0x02 +#define CSR_RXAK 0x01 + +struct mpc_i2c { + struct device *dev; + void __iomem *base; + u32 interrupt; + wait_queue_head_t queue; + struct i2c_adapter adap; + int irq; + u32 real_clk; +}; + +struct mpc_i2c_divider { + u16 divider; + u16 fdr; /* including dfsrr */ +}; + +struct mpc_i2c_data { + void (*setup)(struct device_node *node, struct mpc_i2c *i2c, + u32 clock, u32 prescaler); + u32 prescaler; +}; + +static inline void writeccr(struct mpc_i2c *i2c, u32 x) +{ + writeb(x, i2c->base + MPC_I2C_CR); +} + +static irqreturn_t mpc_i2c_isr(int irq, void *dev_id) +{ + struct mpc_i2c *i2c = dev_id; + if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) { + /* Read again to allow register to stabilise */ + i2c->interrupt = readb(i2c->base + MPC_I2C_SR); + writeb(0, i2c->base + MPC_I2C_SR); + wake_up(&i2c->queue); + } + return IRQ_HANDLED; +} + +/* Sometimes 9th clock pulse isn't generated, and slave doesn't release + * the bus, because it wants to send ACK. + * Following sequence of enabling/disabling and sending start/stop generates + * the 9 pulses, so it's all OK. + */ +static void mpc_i2c_fixup(struct mpc_i2c *i2c) +{ + int k; + u32 delay_val = 1000000 / i2c->real_clk + 1; + + if (delay_val < 2) + delay_val = 2; + + for (k = 9; k; k--) { + writeccr(i2c, 0); + writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN); + udelay(delay_val); + writeccr(i2c, CCR_MEN); + udelay(delay_val << 1); + } +} + +static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) +{ + unsigned long orig_jiffies = jiffies; + u32 x; + int result = 0; + + if (!i2c->irq) { + while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) { + schedule(); + if (time_after(jiffies, orig_jiffies + timeout)) { + dev_dbg(i2c->dev, "timeout\n"); + writeccr(i2c, 0); + result = -EIO; + break; + } + } + x = readb(i2c->base + MPC_I2C_SR); + writeb(0, i2c->base + MPC_I2C_SR); + } else { + /* Interrupt mode */ + result = wait_event_timeout(i2c->queue, + (i2c->interrupt & CSR_MIF), timeout); + + if (unlikely(!(i2c->interrupt & CSR_MIF))) { + dev_dbg(i2c->dev, "wait timeout\n"); + writeccr(i2c, 0); + result = -ETIMEDOUT; + } + + x = i2c->interrupt; + i2c->interrupt = 0; + } + + if (result < 0) + return result; + + if (!(x & CSR_MCF)) { + dev_dbg(i2c->dev, "unfinished\n"); + return -EIO; + } + + if (x & CSR_MAL) { + dev_dbg(i2c->dev, "MAL\n"); + return -EIO; + } + + if (writing && (x & CSR_RXAK)) { + dev_dbg(i2c->dev, "No RXAK\n"); + /* generate stop */ + writeccr(i2c, CCR_MEN); + return -EIO; + } + return 0; +} + +#if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x) +static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] __devinitconst = { + {20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23}, + {28, 0x24}, {30, 0x01}, {32, 0x25}, {34, 0x02}, + {36, 0x26}, {40, 0x27}, {44, 0x04}, {48, 0x28}, + {52, 0x63}, {56, 0x29}, {60, 0x41}, {64, 0x2a}, + {68, 0x07}, {72, 0x2b}, {80, 0x2c}, {88, 0x09}, + {96, 0x2d}, {104, 0x0a}, {112, 0x2e}, {120, 0x81}, + {128, 0x2f}, {136, 0x47}, {144, 0x0c}, {160, 0x30}, + {176, 0x49}, {192, 0x31}, {208, 0x4a}, {224, 0x32}, + {240, 0x0f}, {256, 0x33}, {272, 0x87}, {288, 0x10}, + {320, 0x34}, {352, 0x89}, {384, 0x35}, {416, 0x8a}, + {448, 0x36}, {480, 0x13}, {512, 0x37}, {576, 0x14}, + {640, 0x38}, {768, 0x39}, {896, 0x3a}, {960, 0x17}, + {1024, 0x3b}, {1152, 0x18}, {1280, 0x3c}, {1536, 0x3d}, + {1792, 0x3e}, {1920, 0x1b}, {2048, 0x3f}, {2304, 0x1c}, + {2560, 0x1d}, {3072, 0x1e}, {3584, 0x7e}, {3840, 0x1f}, + {4096, 0x7f}, {4608, 0x5c}, {5120, 0x5d}, {6144, 0x5e}, + {7168, 0xbe}, {7680, 0x5f}, {8192, 0xbf}, {9216, 0x9c}, + {10240, 0x9d}, {12288, 0x9e}, {15360, 0x9f} +}; + +static int __devinit mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, + int prescaler, u32 *real_clk) +{ + const struct mpc_i2c_divider *div = NULL; + unsigned int pvr = mfspr(SPRN_PVR); + u32 divider; + int i; + + if (clock == MPC_I2C_CLOCK_LEGACY) { + /* see below - default fdr = 0x3f -> div = 2048 */ + *real_clk = mpc5xxx_get_bus_frequency(node) / 2048; + return -EINVAL; + } + + /* Determine divider value */ + divider = mpc5xxx_get_bus_frequency(node) / clock; + + /* + * We want to choose an FDR/DFSR that generates an I2C bus speed that + * is equal to or lower than the requested speed. + */ + for (i = 0; i < ARRAY_SIZE(mpc_i2c_dividers_52xx); i++) { + div = &mpc_i2c_dividers_52xx[i]; + /* Old MPC5200 rev A CPUs do not support the high bits */ + if (div->fdr & 0xc0 && pvr == 0x80822011) + continue; + if (div->divider >= divider) + break; + } + + *real_clk = mpc5xxx_get_bus_frequency(node) / div->divider; + return (int)div->fdr; +} + +static void __devinit mpc_i2c_setup_52xx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) +{ + int ret, fdr; + + if (clock == MPC_I2C_CLOCK_PRESERVE) { + dev_dbg(i2c->dev, "using fdr %d\n", + readb(i2c->base + MPC_I2C_FDR)); + return; + } + + ret = mpc_i2c_get_fdr_52xx(node, clock, prescaler, &i2c->real_clk); + fdr = (ret >= 0) ? ret : 0x3f; /* backward compatibility */ + + writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR); + + if (ret >= 0) + dev_info(i2c->dev, "clock %u Hz (fdr=%d)\n", i2c->real_clk, + fdr); +} +#else /* !(CONFIG_PPC_MPC52xx || CONFIG_PPC_MPC512x) */ +static void __devinit mpc_i2c_setup_52xx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) +{ +} +#endif /* CONFIG_PPC_MPC52xx || CONFIG_PPC_MPC512x */ + +#ifdef CONFIG_PPC_MPC512x +static void __devinit mpc_i2c_setup_512x(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) +{ + struct device_node *node_ctrl; + void __iomem *ctrl; + const u32 *pval; + u32 idx; + + /* Enable I2C interrupts for mpc5121 */ + node_ctrl = of_find_compatible_node(NULL, NULL, + "fsl,mpc5121-i2c-ctrl"); + if (node_ctrl) { + ctrl = of_iomap(node_ctrl, 0); + if (ctrl) { + /* Interrupt enable bits for i2c-0/1/2: bit 24/26/28 */ + pval = of_get_property(node, "reg", NULL); + idx = (*pval & 0xff) / 0x20; + setbits32(ctrl, 1 << (24 + idx * 2)); + iounmap(ctrl); + } + of_node_put(node_ctrl); + } + + /* The clock setup for the 52xx works also fine for the 512x */ + mpc_i2c_setup_52xx(node, i2c, clock, prescaler); +} +#else /* CONFIG_PPC_MPC512x */ +static void __devinit mpc_i2c_setup_512x(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) +{ +} +#endif /* CONFIG_PPC_MPC512x */ + +#ifdef CONFIG_FSL_SOC +static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] __devinitconst = { + {160, 0x0120}, {192, 0x0121}, {224, 0x0122}, {256, 0x0123}, + {288, 0x0100}, {320, 0x0101}, {352, 0x0601}, {384, 0x0102}, + {416, 0x0602}, {448, 0x0126}, {480, 0x0103}, {512, 0x0127}, + {544, 0x0b03}, {576, 0x0104}, {608, 0x1603}, {640, 0x0105}, + {672, 0x2003}, {704, 0x0b05}, {736, 0x2b03}, {768, 0x0106}, + {800, 0x3603}, {832, 0x0b06}, {896, 0x012a}, {960, 0x0107}, + {1024, 0x012b}, {1088, 0x1607}, {1152, 0x0108}, {1216, 0x2b07}, + {1280, 0x0109}, {1408, 0x1609}, {1536, 0x010a}, {1664, 0x160a}, + {1792, 0x012e}, {1920, 0x010b}, {2048, 0x012f}, {2176, 0x2b0b}, + {2304, 0x010c}, {2560, 0x010d}, {2816, 0x2b0d}, {3072, 0x010e}, + {3328, 0x2b0e}, {3584, 0x0132}, {3840, 0x010f}, {4096, 0x0133}, + {4608, 0x0110}, {5120, 0x0111}, {6144, 0x0112}, {7168, 0x0136}, + {7680, 0x0113}, {8192, 0x0137}, {9216, 0x0114}, {10240, 0x0115}, + {12288, 0x0116}, {14336, 0x013a}, {15360, 0x0117}, {16384, 0x013b}, + {18432, 0x0118}, {20480, 0x0119}, {24576, 0x011a}, {28672, 0x013e}, + {30720, 0x011b}, {32768, 0x013f}, {36864, 0x011c}, {40960, 0x011d}, + {49152, 0x011e}, {61440, 0x011f} +}; + +static u32 __devinit mpc_i2c_get_sec_cfg_8xxx(void) +{ + struct device_node *node = NULL; + u32 __iomem *reg; + u32 val = 0; + + node = of_find_node_by_name(NULL, "global-utilities"); + if (node) { + const u32 *prop = of_get_property(node, "reg", NULL); + if (prop) { + /* + * Map and check POR Device Status Register 2 + * (PORDEVSR2) at 0xE0014 + */ + reg = ioremap(get_immrbase() + *prop + 0x14, 0x4); + if (!reg) + printk(KERN_ERR + "Error: couldn't map PORDEVSR2\n"); + else + val = in_be32(reg) & 0x00000080; /* sec-cfg */ + iounmap(reg); + } + } + if (node) + of_node_put(node); + + return val; +} + +static int __devinit mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, + u32 prescaler, u32 *real_clk) +{ + const struct mpc_i2c_divider *div = NULL; + u32 divider; + int i; + + if (clock == MPC_I2C_CLOCK_LEGACY) { + /* see below - default fdr = 0x1031 -> div = 16 * 3072 */ + *real_clk = fsl_get_sys_freq() / prescaler / (16 * 3072); + return -EINVAL; + } + + /* Determine proper divider value */ + if (of_device_is_compatible(node, "fsl,mpc8544-i2c")) + prescaler = mpc_i2c_get_sec_cfg_8xxx() ? 3 : 2; + if (!prescaler) + prescaler = 1; + + divider = fsl_get_sys_freq() / clock / prescaler; + + pr_debug("I2C: src_clock=%d clock=%d divider=%d\n", + fsl_get_sys_freq(), clock, divider); + + /* + * We want to choose an FDR/DFSR that generates an I2C bus speed that + * is equal to or lower than the requested speed. + */ + for (i = 0; i < ARRAY_SIZE(mpc_i2c_dividers_8xxx); i++) { + div = &mpc_i2c_dividers_8xxx[i]; + if (div->divider >= divider) + break; + } + + *real_clk = fsl_get_sys_freq() / prescaler / div->divider; + return div ? (int)div->fdr : -EINVAL; +} + +static void __devinit mpc_i2c_setup_8xxx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) +{ + int ret, fdr; + + if (clock == MPC_I2C_CLOCK_PRESERVE) { + dev_dbg(i2c->dev, "using dfsrr %d, fdr %d\n", + readb(i2c->base + MPC_I2C_DFSRR), + readb(i2c->base + MPC_I2C_FDR)); + return; + } + + ret = mpc_i2c_get_fdr_8xxx(node, clock, prescaler, &i2c->real_clk); + fdr = (ret >= 0) ? ret : 0x1031; /* backward compatibility */ + + writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR); + writeb((fdr >> 8) & 0xff, i2c->base + MPC_I2C_DFSRR); + + if (ret >= 0) + dev_info(i2c->dev, "clock %d Hz (dfsrr=%d fdr=%d)\n", + i2c->real_clk, fdr >> 8, fdr & 0xff); +} + +#else /* !CONFIG_FSL_SOC */ +static void __devinit mpc_i2c_setup_8xxx(struct device_node *node, + struct mpc_i2c *i2c, + u32 clock, u32 prescaler) +{ +} +#endif /* CONFIG_FSL_SOC */ + +static void mpc_i2c_start(struct mpc_i2c *i2c) +{ + /* Clear arbitration */ + writeb(0, i2c->base + MPC_I2C_SR); + /* Start with MEN */ + writeccr(i2c, CCR_MEN); +} + +static void mpc_i2c_stop(struct mpc_i2c *i2c) +{ + writeccr(i2c, CCR_MEN); +} + +static int mpc_write(struct mpc_i2c *i2c, int target, + const u8 *data, int length, int restart) +{ + int i, result; + unsigned timeout = i2c->adap.timeout; + u32 flags = restart ? CCR_RSTA : 0; + + /* Start as master */ + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); + /* Write target byte */ + writeb((target << 1), i2c->base + MPC_I2C_DR); + + result = i2c_wait(i2c, timeout, 1); + if (result < 0) + return result; + + for (i = 0; i < length; i++) { + /* Write data byte */ + writeb(data[i], i2c->base + MPC_I2C_DR); + + result = i2c_wait(i2c, timeout, 1); + if (result < 0) + return result; + } + + return 0; +} + +static int mpc_read(struct mpc_i2c *i2c, int target, + u8 *data, int length, int restart) +{ + unsigned timeout = i2c->adap.timeout; + int i, result; + u32 flags = restart ? CCR_RSTA : 0; + + /* Switch to read - restart */ + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); + /* Write target address byte - this time with the read flag set */ + writeb((target << 1) | 1, i2c->base + MPC_I2C_DR); + + result = i2c_wait(i2c, timeout, 1); + if (result < 0) + return result; + + if (length) { + if (length == 1) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); + else + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA); + /* Dummy read */ + readb(i2c->base + MPC_I2C_DR); + } + + for (i = 0; i < length; i++) { + result = i2c_wait(i2c, timeout, 0); + if (result < 0) + return result; + + /* Generate txack on next to last byte */ + if (i == length - 2) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); + /* Do not generate stop on last byte */ + if (i == length - 1) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX); + data[i] = readb(i2c->base + MPC_I2C_DR); + } + + return length; +} + +static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct i2c_msg *pmsg; + int i; + int ret = 0; + unsigned long orig_jiffies = jiffies; + struct mpc_i2c *i2c = i2c_get_adapdata(adap); + + mpc_i2c_start(i2c); + + /* Allow bus up to 1s to become not busy */ + while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) { + if (signal_pending(current)) { + dev_dbg(i2c->dev, "Interrupted\n"); + writeccr(i2c, 0); + return -EINTR; + } + if (time_after(jiffies, orig_jiffies + HZ)) { + u8 status = readb(i2c->base + MPC_I2C_SR); + + dev_dbg(i2c->dev, "timeout\n"); + if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) { + writeb(status & ~CSR_MAL, + i2c->base + MPC_I2C_SR); + mpc_i2c_fixup(i2c); + } + return -EIO; + } + schedule(); + } + + for (i = 0; ret >= 0 && i < num; i++) { + pmsg = &msgs[i]; + dev_dbg(i2c->dev, + "Doing %s %d bytes to 0x%02x - %d of %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->addr, i + 1, num); + if (pmsg->flags & I2C_M_RD) + ret = + mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); + else + ret = + mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); + } + mpc_i2c_stop(i2c); + return (ret < 0) ? ret : num; +} + +static u32 mpc_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm mpc_algo = { + .master_xfer = mpc_xfer, + .functionality = mpc_functionality, +}; + +static struct i2c_adapter mpc_ops = { + .owner = THIS_MODULE, + .name = "MPC adapter", + .algo = &mpc_algo, + .timeout = HZ, +}; + +static const struct of_device_id mpc_i2c_of_match[]; +static int __devinit fsl_i2c_probe(struct platform_device *op) +{ + const struct of_device_id *match; + struct mpc_i2c *i2c; + const u32 *prop; + u32 clock = MPC_I2C_CLOCK_LEGACY; + int result = 0; + int plen; + + match = of_match_device(mpc_i2c_of_match, &op->dev); + if (!match) + return -EINVAL; + + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->dev = &op->dev; /* for debug and error output */ + + init_waitqueue_head(&i2c->queue); + + i2c->base = of_iomap(op->dev.of_node, 0); + if (!i2c->base) { + dev_err(i2c->dev, "failed to map controller\n"); + result = -ENOMEM; + goto fail_map; + } + + i2c->irq = irq_of_parse_and_map(op->dev.of_node, 0); + if (i2c->irq) { /* no i2c->irq implies polling */ + result = request_irq(i2c->irq, mpc_i2c_isr, + IRQF_SHARED, "i2c-mpc", i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto fail_request; + } + } + + if (of_get_property(op->dev.of_node, "fsl,preserve-clocking", NULL)) { + clock = MPC_I2C_CLOCK_PRESERVE; + } else { + prop = of_get_property(op->dev.of_node, "clock-frequency", + &plen); + if (prop && plen == sizeof(u32)) + clock = *prop; + } + + if (match->data) { + struct mpc_i2c_data *data = match->data; + data->setup(op->dev.of_node, i2c, clock, data->prescaler); + } else { + /* Backwards compatibility */ + if (of_get_property(op->dev.of_node, "dfsrr", NULL)) + mpc_i2c_setup_8xxx(op->dev.of_node, i2c, clock, 0); + } + + prop = of_get_property(op->dev.of_node, "fsl,timeout", &plen); + if (prop && plen == sizeof(u32)) { + mpc_ops.timeout = *prop * HZ / 1000000; + if (mpc_ops.timeout < 5) + mpc_ops.timeout = 5; + } + dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ); + + dev_set_drvdata(&op->dev, i2c); + + i2c->adap = mpc_ops; + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &op->dev; + i2c->adap.dev.of_node = of_node_get(op->dev.of_node); + + result = i2c_add_adapter(&i2c->adap); + if (result < 0) { + dev_err(i2c->dev, "failed to add adapter\n"); + goto fail_add; + } + of_i2c_register_devices(&i2c->adap); + + return result; + + fail_add: + dev_set_drvdata(&op->dev, NULL); + free_irq(i2c->irq, i2c); + fail_request: + irq_dispose_mapping(i2c->irq); + iounmap(i2c->base); + fail_map: + kfree(i2c); + return result; +}; + +static int __devexit fsl_i2c_remove(struct platform_device *op) +{ + struct mpc_i2c *i2c = dev_get_drvdata(&op->dev); + + i2c_del_adapter(&i2c->adap); + dev_set_drvdata(&op->dev, NULL); + + if (i2c->irq) + free_irq(i2c->irq, i2c); + + irq_dispose_mapping(i2c->irq); + iounmap(i2c->base); + kfree(i2c); + return 0; +}; + +static struct mpc_i2c_data mpc_i2c_data_512x __devinitdata = { + .setup = mpc_i2c_setup_512x, +}; + +static struct mpc_i2c_data mpc_i2c_data_52xx __devinitdata = { + .setup = mpc_i2c_setup_52xx, +}; + +static struct mpc_i2c_data mpc_i2c_data_8313 __devinitdata = { + .setup = mpc_i2c_setup_8xxx, +}; + +static struct mpc_i2c_data mpc_i2c_data_8543 __devinitdata = { + .setup = mpc_i2c_setup_8xxx, + .prescaler = 2, +}; + +static struct mpc_i2c_data mpc_i2c_data_8544 __devinitdata = { + .setup = mpc_i2c_setup_8xxx, + .prescaler = 3, +}; + +static const struct of_device_id mpc_i2c_of_match[] = { + {.compatible = "mpc5200-i2c", .data = &mpc_i2c_data_52xx, }, + {.compatible = "fsl,mpc5200b-i2c", .data = &mpc_i2c_data_52xx, }, + {.compatible = "fsl,mpc5200-i2c", .data = &mpc_i2c_data_52xx, }, + {.compatible = "fsl,mpc5121-i2c", .data = &mpc_i2c_data_512x, }, + {.compatible = "fsl,mpc8313-i2c", .data = &mpc_i2c_data_8313, }, + {.compatible = "fsl,mpc8543-i2c", .data = &mpc_i2c_data_8543, }, + {.compatible = "fsl,mpc8544-i2c", .data = &mpc_i2c_data_8544, }, + /* Backward compatibility */ + {.compatible = "fsl-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc_i2c_of_match); + +/* Structure for a device driver */ +static struct platform_driver mpc_i2c_driver = { + .probe = fsl_i2c_probe, + .remove = __devexit_p(fsl_i2c_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = mpc_i2c_of_match, + }, +}; + +static int __init fsl_i2c_init(void) +{ + return platform_driver_register(&mpc_i2c_driver); +} + +static void __exit fsl_i2c_exit(void) +{ + platform_driver_unregister(&mpc_i2c_driver); +} + +module_init(fsl_i2c_init); +module_exit(fsl_i2c_exit); + +MODULE_AUTHOR("Adrian Cox <adrian@humboldt.co.uk>"); +MODULE_DESCRIPTION("I2C-Bus adapter for MPC107 bridge and " + "MPC824x/83xx/85xx/86xx/512x/52xx processors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c new file mode 100644 index 00000000..a9941c65 --- /dev/null +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -0,0 +1,631 @@ +/* + * Driver for the i2c controller on the Marvell line of host bridges + * (e.g, gt642[46]0, mv643[46]0, mv644[46]0, and Orion SoC family). + * + * Author: Mark A. Greer <mgreer@mvista.com> + * + * 2005 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mv643xx_i2c.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +/* Register defines */ +#define MV64XXX_I2C_REG_SLAVE_ADDR 0x00 +#define MV64XXX_I2C_REG_DATA 0x04 +#define MV64XXX_I2C_REG_CONTROL 0x08 +#define MV64XXX_I2C_REG_STATUS 0x0c +#define MV64XXX_I2C_REG_BAUD 0x0c +#define MV64XXX_I2C_REG_EXT_SLAVE_ADDR 0x10 +#define MV64XXX_I2C_REG_SOFT_RESET 0x1c + +#define MV64XXX_I2C_REG_CONTROL_ACK 0x00000004 +#define MV64XXX_I2C_REG_CONTROL_IFLG 0x00000008 +#define MV64XXX_I2C_REG_CONTROL_STOP 0x00000010 +#define MV64XXX_I2C_REG_CONTROL_START 0x00000020 +#define MV64XXX_I2C_REG_CONTROL_TWSIEN 0x00000040 +#define MV64XXX_I2C_REG_CONTROL_INTEN 0x00000080 + +/* Ctlr status values */ +#define MV64XXX_I2C_STATUS_BUS_ERR 0x00 +#define MV64XXX_I2C_STATUS_MAST_START 0x08 +#define MV64XXX_I2C_STATUS_MAST_REPEAT_START 0x10 +#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK 0x18 +#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK 0x20 +#define MV64XXX_I2C_STATUS_MAST_WR_ACK 0x28 +#define MV64XXX_I2C_STATUS_MAST_WR_NO_ACK 0x30 +#define MV64XXX_I2C_STATUS_MAST_LOST_ARB 0x38 +#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK 0x40 +#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK 0x48 +#define MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK 0x50 +#define MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK 0x58 +#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK 0xd0 +#define MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK 0xd8 +#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK 0xe0 +#define MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK 0xe8 +#define MV64XXX_I2C_STATUS_NO_STATUS 0xf8 + +/* Driver states */ +enum { + MV64XXX_I2C_STATE_INVALID, + MV64XXX_I2C_STATE_IDLE, + MV64XXX_I2C_STATE_WAITING_FOR_START_COND, + MV64XXX_I2C_STATE_WAITING_FOR_RESTART, + MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK, + MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK, + MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK, + MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA, +}; + +/* Driver actions */ +enum { + MV64XXX_I2C_ACTION_INVALID, + MV64XXX_I2C_ACTION_CONTINUE, + MV64XXX_I2C_ACTION_SEND_START, + MV64XXX_I2C_ACTION_SEND_RESTART, + MV64XXX_I2C_ACTION_SEND_ADDR_1, + MV64XXX_I2C_ACTION_SEND_ADDR_2, + MV64XXX_I2C_ACTION_SEND_DATA, + MV64XXX_I2C_ACTION_RCV_DATA, + MV64XXX_I2C_ACTION_RCV_DATA_STOP, + MV64XXX_I2C_ACTION_SEND_STOP, +}; + +struct mv64xxx_i2c_data { + int irq; + u32 state; + u32 action; + u32 aborting; + u32 cntl_bits; + void __iomem *reg_base; + u32 reg_base_p; + u32 reg_size; + u32 addr1; + u32 addr2; + u32 bytes_left; + u32 byte_posn; + u32 send_stop; + u32 block; + int rc; + u32 freq_m; + u32 freq_n; + wait_queue_head_t waitq; + spinlock_t lock; + struct i2c_msg *msg; + struct i2c_adapter adapter; +}; + +/* + ***************************************************************************** + * + * Finite State Machine & Interrupt Routines + * + ***************************************************************************** + */ + +/* Reset hardware and initialize FSM */ +static void +mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data) +{ + writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET); + writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)), + drv_data->reg_base + MV64XXX_I2C_REG_BAUD); + writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR); + writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR); + writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + drv_data->state = MV64XXX_I2C_STATE_IDLE; +} + +static void +mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) +{ + /* + * If state is idle, then this is likely the remnants of an old + * operation that driver has given up on or the user has killed. + * If so, issue the stop condition and go to idle. + */ + if (drv_data->state == MV64XXX_I2C_STATE_IDLE) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; + return; + } + + /* The status from the ctlr [mostly] tells us what to do next */ + switch (status) { + /* Start condition interrupt */ + case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */ + case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */ + drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1; + drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK; + break; + + /* Performing a write */ + case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */ + if (drv_data->msg->flags & I2C_M_TEN) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK; + break; + } + /* FALLTHRU */ + case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */ + case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */ + if ((drv_data->bytes_left == 0) + || (drv_data->aborting + && (drv_data->byte_posn != 0))) { + if (drv_data->send_stop) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; + drv_data->state = MV64XXX_I2C_STATE_IDLE; + } else { + drv_data->action = + MV64XXX_I2C_ACTION_SEND_RESTART; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_RESTART; + } + } else { + drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK; + drv_data->bytes_left--; + } + break; + + /* Performing a read */ + case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */ + if (drv_data->msg->flags & I2C_M_TEN) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK; + break; + } + /* FALLTHRU */ + case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */ + if (drv_data->bytes_left == 0) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; + drv_data->state = MV64XXX_I2C_STATE_IDLE; + break; + } + /* FALLTHRU */ + case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */ + if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK) + drv_data->action = MV64XXX_I2C_ACTION_CONTINUE; + else { + drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA; + drv_data->bytes_left--; + } + drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA; + + if ((drv_data->bytes_left == 1) || drv_data->aborting) + drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK; + break; + + case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */ + drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP; + drv_data->state = MV64XXX_I2C_STATE_IDLE; + break; + + case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */ + case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */ + case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */ + /* Doesn't seem to be a device at other end */ + drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; + drv_data->state = MV64XXX_I2C_STATE_IDLE; + drv_data->rc = -ENODEV; + break; + + default: + dev_err(&drv_data->adapter.dev, + "mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, " + "status: 0x%x, addr: 0x%x, flags: 0x%x\n", + drv_data->state, status, drv_data->msg->addr, + drv_data->msg->flags); + drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; + mv64xxx_i2c_hw_init(drv_data); + drv_data->rc = -EIO; + } +} + +static void +mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) +{ + switch(drv_data->action) { + case MV64XXX_I2C_ACTION_SEND_RESTART: + drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_START; + drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; + writel(drv_data->cntl_bits, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + drv_data->block = 0; + wake_up_interruptible(&drv_data->waitq); + break; + + case MV64XXX_I2C_ACTION_CONTINUE: + writel(drv_data->cntl_bits, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + break; + + case MV64XXX_I2C_ACTION_SEND_START: + writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + break; + + case MV64XXX_I2C_ACTION_SEND_ADDR_1: + writel(drv_data->addr1, + drv_data->reg_base + MV64XXX_I2C_REG_DATA); + writel(drv_data->cntl_bits, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + break; + + case MV64XXX_I2C_ACTION_SEND_ADDR_2: + writel(drv_data->addr2, + drv_data->reg_base + MV64XXX_I2C_REG_DATA); + writel(drv_data->cntl_bits, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + break; + + case MV64XXX_I2C_ACTION_SEND_DATA: + writel(drv_data->msg->buf[drv_data->byte_posn++], + drv_data->reg_base + MV64XXX_I2C_REG_DATA); + writel(drv_data->cntl_bits, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + break; + + case MV64XXX_I2C_ACTION_RCV_DATA: + drv_data->msg->buf[drv_data->byte_posn++] = + readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA); + writel(drv_data->cntl_bits, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + break; + + case MV64XXX_I2C_ACTION_RCV_DATA_STOP: + drv_data->msg->buf[drv_data->byte_posn++] = + readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA); + drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; + writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + drv_data->block = 0; + wake_up_interruptible(&drv_data->waitq); + break; + + case MV64XXX_I2C_ACTION_INVALID: + default: + dev_err(&drv_data->adapter.dev, + "mv64xxx_i2c_do_action: Invalid action: %d\n", + drv_data->action); + drv_data->rc = -EIO; + /* FALLTHRU */ + case MV64XXX_I2C_ACTION_SEND_STOP: + drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; + writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP, + drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); + drv_data->block = 0; + wake_up_interruptible(&drv_data->waitq); + break; + } +} + +static irqreturn_t +mv64xxx_i2c_intr(int irq, void *dev_id) +{ + struct mv64xxx_i2c_data *drv_data = dev_id; + unsigned long flags; + u32 status; + irqreturn_t rc = IRQ_NONE; + + spin_lock_irqsave(&drv_data->lock, flags); + while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) & + MV64XXX_I2C_REG_CONTROL_IFLG) { + status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS); + mv64xxx_i2c_fsm(drv_data, status); + mv64xxx_i2c_do_action(drv_data); + rc = IRQ_HANDLED; + } + spin_unlock_irqrestore(&drv_data->lock, flags); + + return rc; +} + +/* + ***************************************************************************** + * + * I2C Msg Execution Routines + * + ***************************************************************************** + */ +static void +mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, + struct i2c_msg *msg) +{ + u32 dir = 0; + + drv_data->msg = msg; + drv_data->byte_posn = 0; + drv_data->bytes_left = msg->len; + drv_data->aborting = 0; + drv_data->rc = 0; + drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK | + MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN; + + if (msg->flags & I2C_M_RD) + dir = 1; + + if (msg->flags & I2C_M_TEN) { + drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir; + drv_data->addr2 = (u32)msg->addr & 0xff; + } else { + drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir; + drv_data->addr2 = 0; + } +} + +static void +mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data) +{ + long time_left; + unsigned long flags; + char abort = 0; + + time_left = wait_event_interruptible_timeout(drv_data->waitq, + !drv_data->block, drv_data->adapter.timeout); + + spin_lock_irqsave(&drv_data->lock, flags); + if (!time_left) { /* Timed out */ + drv_data->rc = -ETIMEDOUT; + abort = 1; + } else if (time_left < 0) { /* Interrupted/Error */ + drv_data->rc = time_left; /* errno value */ + abort = 1; + } + + if (abort && drv_data->block) { + drv_data->aborting = 1; + spin_unlock_irqrestore(&drv_data->lock, flags); + + time_left = wait_event_timeout(drv_data->waitq, + !drv_data->block, drv_data->adapter.timeout); + + if ((time_left <= 0) && drv_data->block) { + drv_data->state = MV64XXX_I2C_STATE_IDLE; + dev_err(&drv_data->adapter.dev, + "mv64xxx: I2C bus locked, block: %d, " + "time_left: %d\n", drv_data->block, + (int)time_left); + mv64xxx_i2c_hw_init(drv_data); + } + } else + spin_unlock_irqrestore(&drv_data->lock, flags); +} + +static int +mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg, + int is_first, int is_last) +{ + unsigned long flags; + + spin_lock_irqsave(&drv_data->lock, flags); + mv64xxx_i2c_prepare_for_io(drv_data, msg); + + if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */ + if (drv_data->msg->flags & I2C_M_RD) { + /* No action to do, wait for slave to send a byte */ + drv_data->action = MV64XXX_I2C_ACTION_CONTINUE; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA; + } else { + drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK; + drv_data->bytes_left--; + } + } else { + if (is_first) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_START; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_START_COND; + } else { + drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1; + drv_data->state = + MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK; + } + } + + drv_data->send_stop = is_last; + drv_data->block = 1; + mv64xxx_i2c_do_action(drv_data); + spin_unlock_irqrestore(&drv_data->lock, flags); + + mv64xxx_i2c_wait_for_completion(drv_data); + return drv_data->rc; +} + +/* + ***************************************************************************** + * + * I2C Core Support Routines (Interface to higher level I2C code) + * + ***************************************************************************** + */ +static u32 +mv64xxx_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; +} + +static int +mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap); + int i, rc; + + for (i = 0; i < num; i++) { + rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i], + i == 0, i + 1 == num); + if (rc < 0) + return rc; + } + + return num; +} + +static const struct i2c_algorithm mv64xxx_i2c_algo = { + .master_xfer = mv64xxx_i2c_xfer, + .functionality = mv64xxx_i2c_functionality, +}; + +/* + ***************************************************************************** + * + * Driver Interface & Early Init Routines + * + ***************************************************************************** + */ +static int __devinit +mv64xxx_i2c_map_regs(struct platform_device *pd, + struct mv64xxx_i2c_data *drv_data) +{ + int size; + struct resource *r = platform_get_resource(pd, IORESOURCE_MEM, 0); + + if (!r) + return -ENODEV; + + size = resource_size(r); + + if (!request_mem_region(r->start, size, drv_data->adapter.name)) + return -EBUSY; + + drv_data->reg_base = ioremap(r->start, size); + drv_data->reg_base_p = r->start; + drv_data->reg_size = size; + + return 0; +} + +static void +mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data) +{ + if (drv_data->reg_base) { + iounmap(drv_data->reg_base); + release_mem_region(drv_data->reg_base_p, drv_data->reg_size); + } + + drv_data->reg_base = NULL; + drv_data->reg_base_p = 0; +} + +static int __devinit +mv64xxx_i2c_probe(struct platform_device *pd) +{ + struct mv64xxx_i2c_data *drv_data; + struct mv64xxx_i2c_pdata *pdata = pd->dev.platform_data; + int rc; + + if ((pd->id != 0) || !pdata) + return -ENODEV; + + drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + if (mv64xxx_i2c_map_regs(pd, drv_data)) { + rc = -ENODEV; + goto exit_kfree; + } + + strlcpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter", + sizeof(drv_data->adapter.name)); + + init_waitqueue_head(&drv_data->waitq); + spin_lock_init(&drv_data->lock); + + drv_data->freq_m = pdata->freq_m; + drv_data->freq_n = pdata->freq_n; + drv_data->irq = platform_get_irq(pd, 0); + if (drv_data->irq < 0) { + rc = -ENXIO; + goto exit_unmap_regs; + } + drv_data->adapter.dev.parent = &pd->dev; + drv_data->adapter.algo = &mv64xxx_i2c_algo; + drv_data->adapter.owner = THIS_MODULE; + drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + drv_data->adapter.timeout = msecs_to_jiffies(pdata->timeout); + drv_data->adapter.nr = pd->id; + platform_set_drvdata(pd, drv_data); + i2c_set_adapdata(&drv_data->adapter, drv_data); + + mv64xxx_i2c_hw_init(drv_data); + + if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0, + MV64XXX_I2C_CTLR_NAME, drv_data)) { + dev_err(&drv_data->adapter.dev, + "mv64xxx: Can't register intr handler irq: %d\n", + drv_data->irq); + rc = -EINVAL; + goto exit_unmap_regs; + } else if ((rc = i2c_add_numbered_adapter(&drv_data->adapter)) != 0) { + dev_err(&drv_data->adapter.dev, + "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc); + goto exit_free_irq; + } + + return 0; + + exit_free_irq: + free_irq(drv_data->irq, drv_data); + exit_unmap_regs: + mv64xxx_i2c_unmap_regs(drv_data); + exit_kfree: + kfree(drv_data); + return rc; +} + +static int __devexit +mv64xxx_i2c_remove(struct platform_device *dev) +{ + struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(dev); + int rc; + + rc = i2c_del_adapter(&drv_data->adapter); + free_irq(drv_data->irq, drv_data); + mv64xxx_i2c_unmap_regs(drv_data); + kfree(drv_data); + + return rc; +} + +static struct platform_driver mv64xxx_i2c_driver = { + .probe = mv64xxx_i2c_probe, + .remove = __devexit_p(mv64xxx_i2c_remove), + .driver = { + .owner = THIS_MODULE, + .name = MV64XXX_I2C_CTLR_NAME, + }, +}; + +static int __init +mv64xxx_i2c_init(void) +{ + return platform_driver_register(&mv64xxx_i2c_driver); +} + +static void __exit +mv64xxx_i2c_exit(void) +{ + platform_driver_unregister(&mv64xxx_i2c_driver); +} + +module_init(mv64xxx_i2c_init); +module_exit(mv64xxx_i2c_exit); + +MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>"); +MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c new file mode 100644 index 00000000..3d471d56 --- /dev/null +++ b/drivers/i2c/busses/i2c-mxs.c @@ -0,0 +1,419 @@ +/* + * Freescale MXS I2C bus driver + * + * Copyright (C) 2011 Wolfram Sang, Pengutronix e.K. + * + * based on a (non-working) driver which was: + * + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * TODO: add dma-support if platform-support for it is available + * + * 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/slab.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/platform_device.h> +#include <linux/jiffies.h> +#include <linux/io.h> + +#include <mach/common.h> + +#define DRIVER_NAME "mxs-i2c" + +#define MXS_I2C_CTRL0 (0x00) +#define MXS_I2C_CTRL0_SET (0x04) + +#define MXS_I2C_CTRL0_SFTRST 0x80000000 +#define MXS_I2C_CTRL0_SEND_NAK_ON_LAST 0x02000000 +#define MXS_I2C_CTRL0_RETAIN_CLOCK 0x00200000 +#define MXS_I2C_CTRL0_POST_SEND_STOP 0x00100000 +#define MXS_I2C_CTRL0_PRE_SEND_START 0x00080000 +#define MXS_I2C_CTRL0_MASTER_MODE 0x00020000 +#define MXS_I2C_CTRL0_DIRECTION 0x00010000 +#define MXS_I2C_CTRL0_XFER_COUNT(v) ((v) & 0x0000FFFF) + +#define MXS_I2C_CTRL1 (0x40) +#define MXS_I2C_CTRL1_SET (0x44) +#define MXS_I2C_CTRL1_CLR (0x48) + +#define MXS_I2C_CTRL1_BUS_FREE_IRQ 0x80 +#define MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ 0x40 +#define MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ 0x20 +#define MXS_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ 0x10 +#define MXS_I2C_CTRL1_EARLY_TERM_IRQ 0x08 +#define MXS_I2C_CTRL1_MASTER_LOSS_IRQ 0x04 +#define MXS_I2C_CTRL1_SLAVE_STOP_IRQ 0x02 +#define MXS_I2C_CTRL1_SLAVE_IRQ 0x01 + +#define MXS_I2C_IRQ_MASK (MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | \ + MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ | \ + MXS_I2C_CTRL1_EARLY_TERM_IRQ | \ + MXS_I2C_CTRL1_MASTER_LOSS_IRQ | \ + MXS_I2C_CTRL1_SLAVE_STOP_IRQ | \ + MXS_I2C_CTRL1_SLAVE_IRQ) + +#define MXS_I2C_QUEUECTRL (0x60) +#define MXS_I2C_QUEUECTRL_SET (0x64) +#define MXS_I2C_QUEUECTRL_CLR (0x68) + +#define MXS_I2C_QUEUECTRL_QUEUE_RUN 0x20 +#define MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE 0x04 + +#define MXS_I2C_QUEUESTAT (0x70) +#define MXS_I2C_QUEUESTAT_RD_QUEUE_EMPTY 0x00002000 +#define MXS_I2C_QUEUESTAT_WRITE_QUEUE_CNT_MASK 0x0000001F + +#define MXS_I2C_QUEUECMD (0x80) + +#define MXS_I2C_QUEUEDATA (0x90) + +#define MXS_I2C_DATA (0xa0) + + +#define MXS_CMD_I2C_SELECT (MXS_I2C_CTRL0_RETAIN_CLOCK | \ + MXS_I2C_CTRL0_PRE_SEND_START | \ + MXS_I2C_CTRL0_MASTER_MODE | \ + MXS_I2C_CTRL0_DIRECTION | \ + MXS_I2C_CTRL0_XFER_COUNT(1)) + +#define MXS_CMD_I2C_WRITE (MXS_I2C_CTRL0_PRE_SEND_START | \ + MXS_I2C_CTRL0_MASTER_MODE | \ + MXS_I2C_CTRL0_DIRECTION) + +#define MXS_CMD_I2C_READ (MXS_I2C_CTRL0_SEND_NAK_ON_LAST | \ + MXS_I2C_CTRL0_MASTER_MODE) + +/** + * struct mxs_i2c_dev - per device, private MXS-I2C data + * + * @dev: driver model device node + * @regs: IO registers pointer + * @cmd_complete: completion object for transaction wait + * @cmd_err: error code for last transaction + * @adapter: i2c subsystem adapter node + */ +struct mxs_i2c_dev { + struct device *dev; + void __iomem *regs; + struct completion cmd_complete; + u32 cmd_err; + struct i2c_adapter adapter; +}; + +/* + * TODO: check if calls to here are really needed. If not, we could get rid of + * mxs_reset_block and the mach-dependency. Needs an I2C analyzer, probably. + */ +static void mxs_i2c_reset(struct mxs_i2c_dev *i2c) +{ + mxs_reset_block(i2c->regs); + writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET); + writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE, + i2c->regs + MXS_I2C_QUEUECTRL_SET); +} + +static void mxs_i2c_pioq_setup_read(struct mxs_i2c_dev *i2c, u8 addr, int len, + int flags) +{ + u32 data; + + writel(MXS_CMD_I2C_SELECT, i2c->regs + MXS_I2C_QUEUECMD); + + data = (addr << 1) | I2C_SMBUS_READ; + writel(data, i2c->regs + MXS_I2C_DATA); + + data = MXS_CMD_I2C_READ | MXS_I2C_CTRL0_XFER_COUNT(len) | flags; + writel(data, i2c->regs + MXS_I2C_QUEUECMD); +} + +static void mxs_i2c_pioq_setup_write(struct mxs_i2c_dev *i2c, + u8 addr, u8 *buf, int len, int flags) +{ + u32 data; + int i, shifts_left; + + data = MXS_CMD_I2C_WRITE | MXS_I2C_CTRL0_XFER_COUNT(len + 1) | flags; + writel(data, i2c->regs + MXS_I2C_QUEUECMD); + + /* + * We have to copy the slave address (u8) and buffer (arbitrary number + * of u8) into the data register (u32). To achieve that, the u8 are put + * into the MSBs of 'data' which is then shifted for the next u8. When + * appropriate, 'data' is written to MXS_I2C_DATA. So, the first u32 + * looks like this: + * + * 3 2 1 0 + * 10987654|32109876|54321098|76543210 + * --------+--------+--------+-------- + * buffer+2|buffer+1|buffer+0|slave_addr + */ + + data = ((addr << 1) | I2C_SMBUS_WRITE) << 24; + + for (i = 0; i < len; i++) { + data >>= 8; + data |= buf[i] << 24; + if ((i & 3) == 2) + writel(data, i2c->regs + MXS_I2C_DATA); + } + + /* Write out the remaining bytes if any */ + shifts_left = 24 - (i & 3) * 8; + if (shifts_left) + writel(data >> shifts_left, i2c->regs + MXS_I2C_DATA); +} + +/* + * TODO: should be replaceable with a waitqueue and RD_QUEUE_IRQ (setting the + * rd_threshold to 1). Couldn't get this to work, though. + */ +static int mxs_i2c_wait_for_data(struct mxs_i2c_dev *i2c) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + while (readl(i2c->regs + MXS_I2C_QUEUESTAT) + & MXS_I2C_QUEUESTAT_RD_QUEUE_EMPTY) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cond_resched(); + } + + return 0; +} + +static int mxs_i2c_finish_read(struct mxs_i2c_dev *i2c, u8 *buf, int len) +{ + u32 data; + int i; + + for (i = 0; i < len; i++) { + if ((i & 3) == 0) { + if (mxs_i2c_wait_for_data(i2c)) + return -ETIMEDOUT; + data = readl(i2c->regs + MXS_I2C_QUEUEDATA); + } + buf[i] = data & 0xff; + data >>= 8; + } + + return 0; +} + +/* + * Low level master read/write transaction. + */ +static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, + int stop) +{ + struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap); + int ret; + int flags; + + dev_dbg(i2c->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", + msg->addr, msg->len, msg->flags, stop); + + if (msg->len == 0) + return -EINVAL; + + init_completion(&i2c->cmd_complete); + + flags = stop ? MXS_I2C_CTRL0_POST_SEND_STOP : 0; + + if (msg->flags & I2C_M_RD) + mxs_i2c_pioq_setup_read(i2c, msg->addr, msg->len, flags); + else + mxs_i2c_pioq_setup_write(i2c, msg->addr, msg->buf, msg->len, + flags); + + writel(MXS_I2C_QUEUECTRL_QUEUE_RUN, + i2c->regs + MXS_I2C_QUEUECTRL_SET); + + ret = wait_for_completion_timeout(&i2c->cmd_complete, + msecs_to_jiffies(1000)); + if (ret == 0) + goto timeout; + + if ((!i2c->cmd_err) && (msg->flags & I2C_M_RD)) { + ret = mxs_i2c_finish_read(i2c, msg->buf, msg->len); + if (ret) + goto timeout; + } + + if (i2c->cmd_err == -ENXIO) + mxs_i2c_reset(i2c); + + dev_dbg(i2c->dev, "Done with err=%d\n", i2c->cmd_err); + + return i2c->cmd_err; + +timeout: + dev_dbg(i2c->dev, "Timeout!\n"); + mxs_i2c_reset(i2c); + return -ETIMEDOUT; +} + +static int mxs_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + int i; + int err; + + for (i = 0; i < num; i++) { + err = mxs_i2c_xfer_msg(adap, &msgs[i], i == (num - 1)); + if (err) + return err; + } + + return num; +} + +static u32 mxs_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id) +{ + struct mxs_i2c_dev *i2c = dev_id; + u32 stat = readl(i2c->regs + MXS_I2C_CTRL1) & MXS_I2C_IRQ_MASK; + bool is_last_cmd; + + if (!stat) + return IRQ_NONE; + + if (stat & MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ) + i2c->cmd_err = -ENXIO; + else if (stat & (MXS_I2C_CTRL1_EARLY_TERM_IRQ | + MXS_I2C_CTRL1_MASTER_LOSS_IRQ | + MXS_I2C_CTRL1_SLAVE_STOP_IRQ | MXS_I2C_CTRL1_SLAVE_IRQ)) + /* MXS_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ is only for slaves */ + i2c->cmd_err = -EIO; + else + i2c->cmd_err = 0; + + is_last_cmd = (readl(i2c->regs + MXS_I2C_QUEUESTAT) & + MXS_I2C_QUEUESTAT_WRITE_QUEUE_CNT_MASK) == 0; + + if (is_last_cmd || i2c->cmd_err) + complete(&i2c->cmd_complete); + + writel(stat, i2c->regs + MXS_I2C_CTRL1_CLR); + + return IRQ_HANDLED; +} + +static const struct i2c_algorithm mxs_i2c_algo = { + .master_xfer = mxs_i2c_xfer, + .functionality = mxs_i2c_func, +}; + +static int __devinit mxs_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mxs_i2c_dev *i2c; + struct i2c_adapter *adap; + struct resource *res; + resource_size_t res_size; + int err, irq; + + i2c = devm_kzalloc(dev, sizeof(struct mxs_i2c_dev), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + res_size = resource_size(res); + if (!devm_request_mem_region(dev, res->start, res_size, res->name)) + return -EBUSY; + + i2c->regs = devm_ioremap_nocache(dev, res->start, res_size); + if (!i2c->regs) + return -EBUSY; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + err = devm_request_irq(dev, irq, mxs_i2c_isr, 0, dev_name(dev), i2c); + if (err) + return err; + + i2c->dev = dev; + platform_set_drvdata(pdev, i2c); + + /* Do reset to enforce correct startup after pinmuxing */ + mxs_i2c_reset(i2c); + + adap = &i2c->adapter; + strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name)); + adap->owner = THIS_MODULE; + adap->algo = &mxs_i2c_algo; + adap->dev.parent = dev; + adap->nr = pdev->id; + i2c_set_adapdata(adap, i2c); + err = i2c_add_numbered_adapter(adap); + if (err) { + dev_err(dev, "Failed to add adapter (%d)\n", err); + writel(MXS_I2C_CTRL0_SFTRST, + i2c->regs + MXS_I2C_CTRL0_SET); + return err; + } + + return 0; +} + +static int __devexit mxs_i2c_remove(struct platform_device *pdev) +{ + struct mxs_i2c_dev *i2c = platform_get_drvdata(pdev); + int ret; + + ret = i2c_del_adapter(&i2c->adapter); + if (ret) + return -EBUSY; + + writel(MXS_I2C_QUEUECTRL_QUEUE_RUN, + i2c->regs + MXS_I2C_QUEUECTRL_CLR); + writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mxs_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .remove = __devexit_p(mxs_i2c_remove), +}; + +static int __init mxs_i2c_init(void) +{ + return platform_driver_probe(&mxs_i2c_driver, mxs_i2c_probe); +} +subsys_initcall(mxs_i2c_init); + +static void __exit mxs_i2c_exit(void) +{ + platform_driver_unregister(&mxs_i2c_driver); +} +module_exit(mxs_i2c_exit); + +MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>"); +MODULE_DESCRIPTION("MXS I2C Bus Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/i2c/busses/i2c-nforce2-s4985.c b/drivers/i2c/busses/i2c-nforce2-s4985.c new file mode 100644 index 00000000..29015eb9 --- /dev/null +++ b/drivers/i2c/busses/i2c-nforce2-s4985.c @@ -0,0 +1,257 @@ +/* + * i2c-nforce2-s4985.c - i2c-nforce2 extras for the Tyan S4985 motherboard + * + * Copyright (C) 2008 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. + */ + +/* + * We select the channels by sending commands to the Philips + * PCA9556 chip at I2C address 0x18. The main adapter is used for + * the non-multiplexed part of the bus, and 4 virtual adapters + * are defined for the multiplexed addresses: 0x50-0x53 (memory + * module EEPROM) located on channels 1-4. We define one virtual + * adapter per CPU, which corresponds to one multiplexed channel: + * CPU0: virtual adapter 1, channel 1 + * CPU1: virtual adapter 2, channel 2 + * CPU2: virtual adapter 3, channel 3 + * CPU3: virtual adapter 4, channel 4 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> + +extern struct i2c_adapter *nforce2_smbus; + +static struct i2c_adapter *s4985_adapter; +static struct i2c_algorithm *s4985_algo; + +/* Wrapper access functions for multiplexed SMBus */ +static DEFINE_MUTEX(nforce2_lock); + +static s32 nforce2_access_virt0(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + int error; + + /* We exclude the multiplexed addresses */ + if ((addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30 + || addr == 0x18) + return -ENXIO; + + mutex_lock(&nforce2_lock); + error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write, + command, size, data); + mutex_unlock(&nforce2_lock); + + return error; +} + +/* We remember the last used channels combination so as to only switch + channels when it is really needed. This greatly reduces the SMBus + overhead, but also assumes that nobody will be writing to the PCA9556 + in our back. */ +static u8 last_channels; + +static inline s32 nforce2_access_channel(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data, + u8 channels) +{ + int error; + + /* We exclude the non-multiplexed addresses */ + if ((addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30) + return -ENXIO; + + mutex_lock(&nforce2_lock); + if (last_channels != channels) { + union i2c_smbus_data mplxdata; + mplxdata.byte = channels; + + error = nforce2_smbus->algo->smbus_xfer(adap, 0x18, 0, + I2C_SMBUS_WRITE, 0x01, + I2C_SMBUS_BYTE_DATA, + &mplxdata); + if (error) + goto UNLOCK; + last_channels = channels; + } + error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write, + command, size, data); + +UNLOCK: + mutex_unlock(&nforce2_lock); + return error; +} + +static s32 nforce2_access_virt1(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + /* CPU0: channel 1 enabled */ + return nforce2_access_channel(adap, addr, flags, read_write, command, + size, data, 0x02); +} + +static s32 nforce2_access_virt2(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + /* CPU1: channel 2 enabled */ + return nforce2_access_channel(adap, addr, flags, read_write, command, + size, data, 0x04); +} + +static s32 nforce2_access_virt3(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + /* CPU2: channel 3 enabled */ + return nforce2_access_channel(adap, addr, flags, read_write, command, + size, data, 0x08); +} + +static s32 nforce2_access_virt4(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + /* CPU3: channel 4 enabled */ + return nforce2_access_channel(adap, addr, flags, read_write, command, + size, data, 0x10); +} + +static int __init nforce2_s4985_init(void) +{ + int i, error; + union i2c_smbus_data ioconfig; + + if (!nforce2_smbus) + return -ENODEV; + + /* Configure the PCA9556 multiplexer */ + ioconfig.byte = 0x00; /* All I/O to output mode */ + error = i2c_smbus_xfer(nforce2_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03, + I2C_SMBUS_BYTE_DATA, &ioconfig); + if (error) { + dev_err(&nforce2_smbus->dev, "PCA9556 configuration failed\n"); + error = -EIO; + goto ERROR0; + } + + /* Unregister physical bus */ + error = i2c_del_adapter(nforce2_smbus); + if (error) { + dev_err(&nforce2_smbus->dev, "Physical bus removal failed\n"); + goto ERROR0; + } + + printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4985\n"); + /* Define the 5 virtual adapters and algorithms structures */ + s4985_adapter = kzalloc(5 * sizeof(struct i2c_adapter), GFP_KERNEL); + if (!s4985_adapter) { + error = -ENOMEM; + goto ERROR1; + } + s4985_algo = kzalloc(5 * sizeof(struct i2c_algorithm), GFP_KERNEL); + if (!s4985_algo) { + error = -ENOMEM; + goto ERROR2; + } + + /* Fill in the new structures */ + s4985_algo[0] = *(nforce2_smbus->algo); + s4985_algo[0].smbus_xfer = nforce2_access_virt0; + s4985_adapter[0] = *nforce2_smbus; + s4985_adapter[0].algo = s4985_algo; + s4985_adapter[0].dev.parent = nforce2_smbus->dev.parent; + for (i = 1; i < 5; i++) { + s4985_algo[i] = *(nforce2_smbus->algo); + s4985_adapter[i] = *nforce2_smbus; + snprintf(s4985_adapter[i].name, sizeof(s4985_adapter[i].name), + "SMBus nForce2 adapter (CPU%d)", i - 1); + s4985_adapter[i].algo = s4985_algo + i; + s4985_adapter[i].dev.parent = nforce2_smbus->dev.parent; + } + s4985_algo[1].smbus_xfer = nforce2_access_virt1; + s4985_algo[2].smbus_xfer = nforce2_access_virt2; + s4985_algo[3].smbus_xfer = nforce2_access_virt3; + s4985_algo[4].smbus_xfer = nforce2_access_virt4; + + /* Register virtual adapters */ + for (i = 0; i < 5; i++) { + error = i2c_add_adapter(s4985_adapter + i); + if (error) { + printk(KERN_ERR "i2c-nforce2-s4985: " + "Virtual adapter %d registration " + "failed, module not inserted\n", i); + for (i--; i >= 0; i--) + i2c_del_adapter(s4985_adapter + i); + goto ERROR3; + } + } + + return 0; + +ERROR3: + kfree(s4985_algo); + s4985_algo = NULL; +ERROR2: + kfree(s4985_adapter); + s4985_adapter = NULL; +ERROR1: + /* Restore physical bus */ + i2c_add_adapter(nforce2_smbus); +ERROR0: + return error; +} + +static void __exit nforce2_s4985_exit(void) +{ + if (s4985_adapter) { + int i; + + for (i = 0; i < 5; i++) + i2c_del_adapter(s4985_adapter+i); + kfree(s4985_adapter); + s4985_adapter = NULL; + } + kfree(s4985_algo); + s4985_algo = NULL; + + /* Restore physical bus */ + if (i2c_add_adapter(nforce2_smbus)) + printk(KERN_ERR "i2c-nforce2-s4985: " + "Physical bus restoration failed\n"); +} + +MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("S4985 SMBus multiplexing"); +MODULE_LICENSE("GPL"); + +module_init(nforce2_s4985_init); +module_exit(nforce2_s4985_exit); diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c new file mode 100644 index 00000000..4853b52a --- /dev/null +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -0,0 +1,468 @@ +/* + SMBus driver for nVidia nForce2 MCP + + Added nForce3 Pro 150 Thomas Leibold <thomas@plx.com>, + Ported to 2.5 Patrick Dreker <patrick@dreker.de>, + Copyright (c) 2003 Hans-Frieder Vogt <hfvogt@arcor.de>, + Based on + SMBus 2.0 driver for AMD-8111 IO-Hub + Copyright (c) 2002 Vojtech Pavlik + + 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. +*/ + +/* + SUPPORTED DEVICES PCI ID + nForce2 MCP 0064 + nForce2 Ultra 400 MCP 0084 + nForce3 Pro150 MCP 00D4 + nForce3 250Gb MCP 00E4 + nForce4 MCP 0052 + nForce4 MCP-04 0034 + nForce MCP51 0264 + nForce MCP55 0368 + nForce MCP61 03EB + nForce MCP65 0446 + nForce MCP67 0542 + nForce MCP73 07D8 + nForce MCP78S 0752 + nForce MCP79 0AA2 + + This driver supports the 2 SMBuses that are included in the MCP of the + nForce2/3/4/5xx chipsets. +*/ + +/* Note: we assume there can only be one nForce2, with two SMBus interfaces */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/dmi.h> +#include <linux/acpi.h> +#include <linux/slab.h> +#include <linux/io.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR ("Hans-Frieder Vogt <hfvogt@gmx.net>"); +MODULE_DESCRIPTION("nForce2/3/4/5xx SMBus driver"); + + +struct nforce2_smbus { + struct i2c_adapter adapter; + int base; + int size; + int blockops; + int can_abort; +}; + + +/* + * nVidia nForce2 SMBus control register definitions + * (Newer incarnations use standard BARs 4 and 5 instead) + */ +#define NFORCE_PCI_SMB1 0x50 +#define NFORCE_PCI_SMB2 0x54 + + +/* + * ACPI 2.0 chapter 13 SMBus 2.0 EC register model + */ +#define NVIDIA_SMB_PRTCL (smbus->base + 0x00) /* protocol, PEC */ +#define NVIDIA_SMB_STS (smbus->base + 0x01) /* status */ +#define NVIDIA_SMB_ADDR (smbus->base + 0x02) /* address */ +#define NVIDIA_SMB_CMD (smbus->base + 0x03) /* command */ +#define NVIDIA_SMB_DATA (smbus->base + 0x04) /* 32 data registers */ +#define NVIDIA_SMB_BCNT (smbus->base + 0x24) /* number of data + bytes */ +#define NVIDIA_SMB_STATUS_ABRT (smbus->base + 0x3c) /* register used to + check the status of + the abort command */ +#define NVIDIA_SMB_CTRL (smbus->base + 0x3e) /* control register */ + +#define NVIDIA_SMB_STATUS_ABRT_STS 0x01 /* Bit to notify that + abort succeeded */ +#define NVIDIA_SMB_CTRL_ABORT 0x20 +#define NVIDIA_SMB_STS_DONE 0x80 +#define NVIDIA_SMB_STS_ALRM 0x40 +#define NVIDIA_SMB_STS_RES 0x20 +#define NVIDIA_SMB_STS_STATUS 0x1f + +#define NVIDIA_SMB_PRTCL_WRITE 0x00 +#define NVIDIA_SMB_PRTCL_READ 0x01 +#define NVIDIA_SMB_PRTCL_QUICK 0x02 +#define NVIDIA_SMB_PRTCL_BYTE 0x04 +#define NVIDIA_SMB_PRTCL_BYTE_DATA 0x06 +#define NVIDIA_SMB_PRTCL_WORD_DATA 0x08 +#define NVIDIA_SMB_PRTCL_BLOCK_DATA 0x0a +#define NVIDIA_SMB_PRTCL_PEC 0x80 + +/* Misc definitions */ +#define MAX_TIMEOUT 100 + +/* We disable the second SMBus channel on these boards */ +static struct dmi_system_id __devinitdata nforce2_dmi_blacklist2[] = { + { + .ident = "DFI Lanparty NF4 Expert", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "DFI Corp,LTD"), + DMI_MATCH(DMI_BOARD_NAME, "LP UT NF4 Expert"), + }, + }, + { } +}; + +static struct pci_driver nforce2_driver; + +/* For multiplexing support, we need a global reference to the 1st + SMBus channel */ +#if defined CONFIG_I2C_NFORCE2_S4985 || defined CONFIG_I2C_NFORCE2_S4985_MODULE +struct i2c_adapter *nforce2_smbus; +EXPORT_SYMBOL_GPL(nforce2_smbus); + +static void nforce2_set_reference(struct i2c_adapter *adap) +{ + nforce2_smbus = adap; +} +#else +static inline void nforce2_set_reference(struct i2c_adapter *adap) { } +#endif + +static void nforce2_abort(struct i2c_adapter *adap) +{ + struct nforce2_smbus *smbus = adap->algo_data; + int timeout = 0; + unsigned char temp; + + dev_dbg(&adap->dev, "Aborting current transaction\n"); + + outb_p(NVIDIA_SMB_CTRL_ABORT, NVIDIA_SMB_CTRL); + do { + msleep(1); + temp = inb_p(NVIDIA_SMB_STATUS_ABRT); + } while (!(temp & NVIDIA_SMB_STATUS_ABRT_STS) && + (timeout++ < MAX_TIMEOUT)); + if (!(temp & NVIDIA_SMB_STATUS_ABRT_STS)) + dev_err(&adap->dev, "Can't reset the smbus\n"); + outb_p(NVIDIA_SMB_STATUS_ABRT_STS, NVIDIA_SMB_STATUS_ABRT); +} + +static int nforce2_check_status(struct i2c_adapter *adap) +{ + struct nforce2_smbus *smbus = adap->algo_data; + int timeout = 0; + unsigned char temp; + + do { + msleep(1); + temp = inb_p(NVIDIA_SMB_STS); + } while ((!temp) && (timeout++ < MAX_TIMEOUT)); + + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "SMBus Timeout!\n"); + if (smbus->can_abort) + nforce2_abort(adap); + return -ETIMEDOUT; + } + if (!(temp & NVIDIA_SMB_STS_DONE) || (temp & NVIDIA_SMB_STS_STATUS)) { + dev_dbg(&adap->dev, "Transaction failed (0x%02x)!\n", temp); + return -EIO; + } + return 0; +} + +/* Return negative errno on error */ +static s32 nforce2_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + struct nforce2_smbus *smbus = adap->algo_data; + unsigned char protocol, pec; + u8 len; + int i, status; + + protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ : + NVIDIA_SMB_PRTCL_WRITE; + pec = (flags & I2C_CLIENT_PEC) ? NVIDIA_SMB_PRTCL_PEC : 0; + + switch (size) { + + case I2C_SMBUS_QUICK: + protocol |= NVIDIA_SMB_PRTCL_QUICK; + read_write = I2C_SMBUS_WRITE; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, NVIDIA_SMB_CMD); + protocol |= NVIDIA_SMB_PRTCL_BYTE; + break; + + case I2C_SMBUS_BYTE_DATA: + outb_p(command, NVIDIA_SMB_CMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, NVIDIA_SMB_DATA); + protocol |= NVIDIA_SMB_PRTCL_BYTE_DATA; + break; + + case I2C_SMBUS_WORD_DATA: + outb_p(command, NVIDIA_SMB_CMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word, NVIDIA_SMB_DATA); + outb_p(data->word >> 8, NVIDIA_SMB_DATA+1); + } + protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec; + break; + + case I2C_SMBUS_BLOCK_DATA: + outb_p(command, NVIDIA_SMB_CMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) { + dev_err(&adap->dev, + "Transaction failed " + "(requested block size: %d)\n", + len); + return -EINVAL; + } + outb_p(len, NVIDIA_SMB_BCNT); + for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++) + outb_p(data->block[i + 1], + NVIDIA_SMB_DATA+i); + } + protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec; + break; + + default: + dev_err(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR); + outb_p(protocol, NVIDIA_SMB_PRTCL); + + status = nforce2_check_status(adap); + if (status) + return status; + + if (read_write == I2C_SMBUS_WRITE) + return 0; + + switch (size) { + + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + data->byte = inb_p(NVIDIA_SMB_DATA); + break; + + case I2C_SMBUS_WORD_DATA: + data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8); + break; + + case I2C_SMBUS_BLOCK_DATA: + len = inb_p(NVIDIA_SMB_BCNT); + if ((len <= 0) || (len > I2C_SMBUS_BLOCK_MAX)) { + dev_err(&adap->dev, "Transaction failed " + "(received block size: 0x%02x)\n", + len); + return -EPROTO; + } + for (i = 0; i < len; i++) + data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i); + data->block[0] = len; + break; + } + + return 0; +} + + +static u32 nforce2_func(struct i2c_adapter *adapter) +{ + /* other functionality might be possible, but is not tested */ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PEC | + (((struct nforce2_smbus*)adapter->algo_data)->blockops ? + I2C_FUNC_SMBUS_BLOCK_DATA : 0); +} + +static struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = nforce2_access, + .functionality = nforce2_func, +}; + + +static const struct pci_device_id nforce2_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS) }, + { 0 } +}; + +MODULE_DEVICE_TABLE (pci, nforce2_ids); + + +static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar, + int alt_reg, struct nforce2_smbus *smbus, const char *name) +{ + int error; + + smbus->base = pci_resource_start(dev, bar); + if (smbus->base) { + smbus->size = pci_resource_len(dev, bar); + } else { + /* Older incarnations of the device used non-standard BARs */ + u16 iobase; + + if (pci_read_config_word(dev, alt_reg, &iobase) + != PCIBIOS_SUCCESSFUL) { + dev_err(&dev->dev, "Error reading PCI config for %s\n", + name); + return -EIO; + } + + smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK; + smbus->size = 64; + } + + error = acpi_check_region(smbus->base, smbus->size, + nforce2_driver.name); + if (error) + return error; + + if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) { + dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n", + smbus->base, smbus->base+smbus->size-1, name); + return -EBUSY; + } + smbus->adapter.owner = THIS_MODULE; + smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus->adapter.algo = &smbus_algorithm; + smbus->adapter.algo_data = smbus; + smbus->adapter.dev.parent = &dev->dev; + snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), + "SMBus nForce2 adapter at %04x", smbus->base); + + error = i2c_add_adapter(&smbus->adapter); + if (error) { + dev_err(&smbus->adapter.dev, "Failed to register adapter.\n"); + release_region(smbus->base, smbus->size); + return error; + } + dev_info(&smbus->adapter.dev, "nForce2 SMBus adapter at %#x\n", smbus->base); + return 0; +} + + +static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct nforce2_smbus *smbuses; + int res1, res2; + + /* we support 2 SMBus adapters */ + if (!(smbuses = kzalloc(2*sizeof(struct nforce2_smbus), GFP_KERNEL))) + return -ENOMEM; + pci_set_drvdata(dev, smbuses); + + switch(dev->device) { + case PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS: + case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS: + case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS: + smbuses[0].blockops = 1; + smbuses[1].blockops = 1; + smbuses[0].can_abort = 1; + smbuses[1].can_abort = 1; + } + + /* SMBus adapter 1 */ + res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1"); + if (res1 < 0) + smbuses[0].base = 0; /* to have a check value */ + + /* SMBus adapter 2 */ + if (dmi_check_system(nforce2_dmi_blacklist2)) { + dev_err(&dev->dev, "Disabling SMB2 for safety reasons.\n"); + res2 = -EPERM; + smbuses[1].base = 0; + } else { + res2 = nforce2_probe_smb(dev, 5, NFORCE_PCI_SMB2, &smbuses[1], + "SMB2"); + if (res2 < 0) + smbuses[1].base = 0; /* to have a check value */ + } + + if ((res1 < 0) && (res2 < 0)) { + /* we did not find even one of the SMBuses, so we give up */ + kfree(smbuses); + return -ENODEV; + } + + nforce2_set_reference(&smbuses[0].adapter); + return 0; +} + + +static void __devexit nforce2_remove(struct pci_dev *dev) +{ + struct nforce2_smbus *smbuses = pci_get_drvdata(dev); + + nforce2_set_reference(NULL); + if (smbuses[0].base) { + i2c_del_adapter(&smbuses[0].adapter); + release_region(smbuses[0].base, smbuses[0].size); + } + if (smbuses[1].base) { + i2c_del_adapter(&smbuses[1].adapter); + release_region(smbuses[1].base, smbuses[1].size); + } + kfree(smbuses); +} + +static struct pci_driver nforce2_driver = { + .name = "nForce2_smbus", + .id_table = nforce2_ids, + .probe = nforce2_probe, + .remove = __devexit_p(nforce2_remove), +}; + +static int __init nforce2_init(void) +{ + return pci_register_driver(&nforce2_driver); +} + +static void __exit nforce2_exit(void) +{ + pci_unregister_driver(&nforce2_driver); +} + +module_init(nforce2_init); +module_exit(nforce2_exit); + diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c new file mode 100644 index 00000000..0c731ca6 --- /dev/null +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -0,0 +1,1064 @@ +/* + * Copyright (C) 2009 ST-Ericsson SA + * Copyright (C) 2009 STMicroelectronics + * + * I2C master mode controller driver, used in Nomadik 8815 + * and Ux500 platforms. + * + * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> + * Author: Sachin Verma <sachin.verma@st.com> + * + * 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. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> + +#include <plat/i2c.h> + +#define DRIVER_NAME "nmk-i2c" + +/* I2C Controller register offsets */ +#define I2C_CR (0x000) +#define I2C_SCR (0x004) +#define I2C_HSMCR (0x008) +#define I2C_MCR (0x00C) +#define I2C_TFR (0x010) +#define I2C_SR (0x014) +#define I2C_RFR (0x018) +#define I2C_TFTR (0x01C) +#define I2C_RFTR (0x020) +#define I2C_DMAR (0x024) +#define I2C_BRCR (0x028) +#define I2C_IMSCR (0x02C) +#define I2C_RISR (0x030) +#define I2C_MISR (0x034) +#define I2C_ICR (0x038) + +/* Control registers */ +#define I2C_CR_PE (0x1 << 0) /* Peripheral Enable */ +#define I2C_CR_OM (0x3 << 1) /* Operating mode */ +#define I2C_CR_SAM (0x1 << 3) /* Slave addressing mode */ +#define I2C_CR_SM (0x3 << 4) /* Speed mode */ +#define I2C_CR_SGCM (0x1 << 6) /* Slave general call mode */ +#define I2C_CR_FTX (0x1 << 7) /* Flush Transmit */ +#define I2C_CR_FRX (0x1 << 8) /* Flush Receive */ +#define I2C_CR_DMA_TX_EN (0x1 << 9) /* DMA Tx enable */ +#define I2C_CR_DMA_RX_EN (0x1 << 10) /* DMA Rx Enable */ +#define I2C_CR_DMA_SLE (0x1 << 11) /* DMA sync. logic enable */ +#define I2C_CR_LM (0x1 << 12) /* Loopback mode */ +#define I2C_CR_FON (0x3 << 13) /* Filtering on */ +#define I2C_CR_FS (0x3 << 15) /* Force stop enable */ + +/* Master controller (MCR) register */ +#define I2C_MCR_OP (0x1 << 0) /* Operation */ +#define I2C_MCR_A7 (0x7f << 1) /* 7-bit address */ +#define I2C_MCR_EA10 (0x7 << 8) /* 10-bit Extended address */ +#define I2C_MCR_SB (0x1 << 11) /* Extended address */ +#define I2C_MCR_AM (0x3 << 12) /* Address type */ +#define I2C_MCR_STOP (0x1 << 14) /* Stop condition */ +#define I2C_MCR_LENGTH (0x7ff << 15) /* Transaction length */ + +/* Status register (SR) */ +#define I2C_SR_OP (0x3 << 0) /* Operation */ +#define I2C_SR_STATUS (0x3 << 2) /* controller status */ +#define I2C_SR_CAUSE (0x7 << 4) /* Abort cause */ +#define I2C_SR_TYPE (0x3 << 7) /* Receive type */ +#define I2C_SR_LENGTH (0x7ff << 9) /* Transfer length */ + +/* Interrupt mask set/clear (IMSCR) bits */ +#define I2C_IT_TXFE (0x1 << 0) +#define I2C_IT_TXFNE (0x1 << 1) +#define I2C_IT_TXFF (0x1 << 2) +#define I2C_IT_TXFOVR (0x1 << 3) +#define I2C_IT_RXFE (0x1 << 4) +#define I2C_IT_RXFNF (0x1 << 5) +#define I2C_IT_RXFF (0x1 << 6) +#define I2C_IT_RFSR (0x1 << 16) +#define I2C_IT_RFSE (0x1 << 17) +#define I2C_IT_WTSR (0x1 << 18) +#define I2C_IT_MTD (0x1 << 19) +#define I2C_IT_STD (0x1 << 20) +#define I2C_IT_MAL (0x1 << 24) +#define I2C_IT_BERR (0x1 << 25) +#define I2C_IT_MTDWS (0x1 << 28) + +#define GEN_MASK(val, mask, sb) (((val) << (sb)) & (mask)) + +/* some bits in ICR are reserved */ +#define I2C_CLEAR_ALL_INTS 0x131f007f + +/* first three msb bits are reserved */ +#define IRQ_MASK(mask) (mask & 0x1fffffff) + +/* maximum threshold value */ +#define MAX_I2C_FIFO_THRESHOLD 15 + +enum i2c_status { + I2C_NOP, + I2C_ON_GOING, + I2C_OK, + I2C_ABORT +}; + +/* operation */ +enum i2c_operation { + I2C_NO_OPERATION = 0xff, + I2C_WRITE = 0x00, + I2C_READ = 0x01 +}; + +/** + * struct i2c_nmk_client - client specific data + * @slave_adr: 7-bit slave address + * @count: no. bytes to be transferred + * @buffer: client data buffer + * @xfer_bytes: bytes transferred till now + * @operation: current I2C operation + */ +struct i2c_nmk_client { + unsigned short slave_adr; + unsigned long count; + unsigned char *buffer; + unsigned long xfer_bytes; + enum i2c_operation operation; +}; + +/** + * struct nmk_i2c_dev - private data structure of the controller + * @pdev: parent platform device + * @adap: corresponding I2C adapter + * @irq: interrupt line for the controller + * @virtbase: virtual io memory area + * @clk: hardware i2c block clock + * @cfg: machine provided controller configuration + * @cli: holder of client specific data + * @stop: stop condition + * @xfer_complete: acknowledge completion for a I2C message + * @result: controller propogated result + * @busy: Busy doing transfer + */ +struct nmk_i2c_dev { + struct platform_device *pdev; + struct i2c_adapter adap; + int irq; + void __iomem *virtbase; + struct clk *clk; + struct nmk_i2c_controller cfg; + struct i2c_nmk_client cli; + int stop; + struct completion xfer_complete; + int result; + struct regulator *regulator; + bool busy; +}; + +/* controller's abort causes */ +static const char *abort_causes[] = { + "no ack received after address transmission", + "no ack received during data phase", + "ack received after xmission of master code", + "master lost arbitration", + "slave restarts", + "slave reset", + "overflow, maxsize is 2047 bytes", +}; + +static inline void i2c_set_bit(void __iomem *reg, u32 mask) +{ + writel(readl(reg) | mask, reg); +} + +static inline void i2c_clr_bit(void __iomem *reg, u32 mask) +{ + writel(readl(reg) & ~mask, reg); +} + +/** + * flush_i2c_fifo() - This function flushes the I2C FIFO + * @dev: private data of I2C Driver + * + * This function flushes the I2C Tx and Rx FIFOs. It returns + * 0 on successful flushing of FIFO + */ +static int flush_i2c_fifo(struct nmk_i2c_dev *dev) +{ +#define LOOP_ATTEMPTS 10 + int i; + unsigned long timeout; + + /* + * flush the transmit and receive FIFO. The flushing + * operation takes several cycles before to be completed. + * On the completion, the I2C internal logic clears these + * bits, until then no one must access Tx, Rx FIFO and + * should poll on these bits waiting for the completion. + */ + writel((I2C_CR_FTX | I2C_CR_FRX), dev->virtbase + I2C_CR); + + for (i = 0; i < LOOP_ATTEMPTS; i++) { + timeout = jiffies + dev->adap.timeout; + + while (!time_after(jiffies, timeout)) { + if ((readl(dev->virtbase + I2C_CR) & + (I2C_CR_FTX | I2C_CR_FRX)) == 0) + return 0; + } + } + + dev_err(&dev->pdev->dev, "flushing operation timed out " + "giving up after %d attempts", LOOP_ATTEMPTS); + + return -ETIMEDOUT; +} + +/** + * disable_all_interrupts() - Disable all interrupts of this I2c Bus + * @dev: private data of I2C Driver + */ +static void disable_all_interrupts(struct nmk_i2c_dev *dev) +{ + u32 mask = IRQ_MASK(0); + writel(mask, dev->virtbase + I2C_IMSCR); +} + +/** + * clear_all_interrupts() - Clear all interrupts of I2C Controller + * @dev: private data of I2C Driver + */ +static void clear_all_interrupts(struct nmk_i2c_dev *dev) +{ + u32 mask; + mask = IRQ_MASK(I2C_CLEAR_ALL_INTS); + writel(mask, dev->virtbase + I2C_ICR); +} + +/** + * init_hw() - initialize the I2C hardware + * @dev: private data of I2C Driver + */ +static int init_hw(struct nmk_i2c_dev *dev) +{ + int stat; + + stat = flush_i2c_fifo(dev); + if (stat) + goto exit; + + /* disable the controller */ + i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE); + + disable_all_interrupts(dev); + + clear_all_interrupts(dev); + + dev->cli.operation = I2C_NO_OPERATION; + +exit: + return stat; +} + +/* enable peripheral, master mode operation */ +#define DEFAULT_I2C_REG_CR ((1 << 1) | I2C_CR_PE) + +/** + * load_i2c_mcr_reg() - load the MCR register + * @dev: private data of controller + */ +static u32 load_i2c_mcr_reg(struct nmk_i2c_dev *dev) +{ + u32 mcr = 0; + + /* 7-bit address transaction */ + mcr |= GEN_MASK(1, I2C_MCR_AM, 12); + mcr |= GEN_MASK(dev->cli.slave_adr, I2C_MCR_A7, 1); + + /* start byte procedure not applied */ + mcr |= GEN_MASK(0, I2C_MCR_SB, 11); + + /* check the operation, master read/write? */ + if (dev->cli.operation == I2C_WRITE) + mcr |= GEN_MASK(I2C_WRITE, I2C_MCR_OP, 0); + else + mcr |= GEN_MASK(I2C_READ, I2C_MCR_OP, 0); + + /* stop or repeated start? */ + if (dev->stop) + mcr |= GEN_MASK(1, I2C_MCR_STOP, 14); + else + mcr &= ~(GEN_MASK(1, I2C_MCR_STOP, 14)); + + mcr |= GEN_MASK(dev->cli.count, I2C_MCR_LENGTH, 15); + + return mcr; +} + +/** + * setup_i2c_controller() - setup the controller + * @dev: private data of controller + */ +static void setup_i2c_controller(struct nmk_i2c_dev *dev) +{ + u32 brcr1, brcr2; + u32 i2c_clk, div; + + writel(0x0, dev->virtbase + I2C_CR); + writel(0x0, dev->virtbase + I2C_HSMCR); + writel(0x0, dev->virtbase + I2C_TFTR); + writel(0x0, dev->virtbase + I2C_RFTR); + writel(0x0, dev->virtbase + I2C_DMAR); + + /* + * set the slsu: + * + * slsu defines the data setup time after SCL clock + * stretching in terms of i2c clk cycles. The + * needed setup time for the three modes are 250ns, + * 100ns, 10ns respectively thus leading to the values + * of 14, 6, 2 for a 48 MHz i2c clk. + */ + writel(dev->cfg.slsu << 16, dev->virtbase + I2C_SCR); + + i2c_clk = clk_get_rate(dev->clk); + + /* fallback to std. mode if machine has not provided it */ + if (dev->cfg.clk_freq == 0) + dev->cfg.clk_freq = 100000; + + /* + * The spec says, in case of std. mode the divider is + * 2 whereas it is 3 for fast and fastplus mode of + * operation. TODO - high speed support. + */ + div = (dev->cfg.clk_freq > 100000) ? 3 : 2; + + /* + * generate the mask for baud rate counters. The controller + * has two baud rate counters. One is used for High speed + * operation, and the other is for std, fast mode, fast mode + * plus operation. Currently we do not supprt high speed mode + * so set brcr1 to 0. + */ + brcr1 = 0 << 16; + brcr2 = (i2c_clk/(dev->cfg.clk_freq * div)) & 0xffff; + + /* set the baud rate counter register */ + writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR); + + /* + * set the speed mode. Currently we support + * only standard and fast mode of operation + * TODO - support for fast mode plus (up to 1Mb/s) + * and high speed (up to 3.4 Mb/s) + */ + if (dev->cfg.sm > I2C_FREQ_MODE_FAST) { + dev_err(&dev->pdev->dev, "do not support this mode " + "defaulting to std. mode\n"); + brcr2 = i2c_clk/(100000 * 2) & 0xffff; + writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR); + writel(I2C_FREQ_MODE_STANDARD << 4, + dev->virtbase + I2C_CR); + } + writel(dev->cfg.sm << 4, dev->virtbase + I2C_CR); + + /* set the Tx and Rx FIFO threshold */ + writel(dev->cfg.tft, dev->virtbase + I2C_TFTR); + writel(dev->cfg.rft, dev->virtbase + I2C_RFTR); +} + +/** + * read_i2c() - Read from I2C client device + * @dev: private data of I2C Driver + * + * This function reads from i2c client device when controller is in + * master mode. There is a completion timeout. If there is no transfer + * before timeout error is returned. + */ +static int read_i2c(struct nmk_i2c_dev *dev) +{ + u32 status = 0; + u32 mcr; + u32 irq_mask = 0; + int timeout; + + mcr = load_i2c_mcr_reg(dev); + writel(mcr, dev->virtbase + I2C_MCR); + + /* load the current CR value */ + writel(readl(dev->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR, + dev->virtbase + I2C_CR); + + /* enable the controller */ + i2c_set_bit(dev->virtbase + I2C_CR, I2C_CR_PE); + + init_completion(&dev->xfer_complete); + + /* enable interrupts by setting the mask */ + irq_mask = (I2C_IT_RXFNF | I2C_IT_RXFF | + I2C_IT_MAL | I2C_IT_BERR); + + if (dev->stop) + irq_mask |= I2C_IT_MTD; + else + irq_mask |= I2C_IT_MTDWS; + + irq_mask = I2C_CLEAR_ALL_INTS & IRQ_MASK(irq_mask); + + writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask, + dev->virtbase + I2C_IMSCR); + + timeout = wait_for_completion_interruptible_timeout( + &dev->xfer_complete, dev->adap.timeout); + + if (timeout < 0) { + dev_err(&dev->pdev->dev, + "wait_for_completion_interruptible_timeout" + "returned %d waiting for event\n", timeout); + status = timeout; + } + + if (timeout == 0) { + /* Controller timed out */ + dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n", + dev->cli.slave_adr); + status = -ETIMEDOUT; + } + return status; +} + +static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes) +{ + int count; + + for (count = (no_bytes - 2); + (count > 0) && + (dev->cli.count != 0); + count--) { + /* write to the Tx FIFO */ + writeb(*dev->cli.buffer, + dev->virtbase + I2C_TFR); + dev->cli.buffer++; + dev->cli.count--; + dev->cli.xfer_bytes++; + } + +} + +/** + * write_i2c() - Write data to I2C client. + * @dev: private data of I2C Driver + * + * This function writes data to I2C client + */ +static int write_i2c(struct nmk_i2c_dev *dev) +{ + u32 status = 0; + u32 mcr; + u32 irq_mask = 0; + int timeout; + + mcr = load_i2c_mcr_reg(dev); + + writel(mcr, dev->virtbase + I2C_MCR); + + /* load the current CR value */ + writel(readl(dev->virtbase + I2C_CR) | DEFAULT_I2C_REG_CR, + dev->virtbase + I2C_CR); + + /* enable the controller */ + i2c_set_bit(dev->virtbase + I2C_CR , I2C_CR_PE); + + init_completion(&dev->xfer_complete); + + /* enable interrupts by settings the masks */ + irq_mask = (I2C_IT_TXFOVR | I2C_IT_MAL | I2C_IT_BERR); + + /* Fill the TX FIFO with transmit data */ + fill_tx_fifo(dev, MAX_I2C_FIFO_THRESHOLD); + + if (dev->cli.count != 0) + irq_mask |= I2C_IT_TXFNE; + + /* + * check if we want to transfer a single or multiple bytes, if so + * set the MTDWS bit (Master Transaction Done Without Stop) + * to start repeated start operation + */ + if (dev->stop) + irq_mask |= I2C_IT_MTD; + else + irq_mask |= I2C_IT_MTDWS; + + irq_mask = I2C_CLEAR_ALL_INTS & IRQ_MASK(irq_mask); + + writel(readl(dev->virtbase + I2C_IMSCR) | irq_mask, + dev->virtbase + I2C_IMSCR); + + timeout = wait_for_completion_interruptible_timeout( + &dev->xfer_complete, dev->adap.timeout); + + if (timeout < 0) { + dev_err(&dev->pdev->dev, + "wait_for_completion_interruptible_timeout" + "returned %d waiting for event\n", timeout); + status = timeout; + } + + if (timeout == 0) { + /* Controller timed out */ + dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n", + dev->cli.slave_adr); + status = -ETIMEDOUT; + } + + return status; +} + +/** + * nmk_i2c_xfer_one() - transmit a single I2C message + * @dev: device with a message encoded into it + * @flags: message flags + */ +static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags) +{ + int status; + + if (flags & I2C_M_RD) { + /* read operation */ + dev->cli.operation = I2C_READ; + status = read_i2c(dev); + } else { + /* write operation */ + dev->cli.operation = I2C_WRITE; + status = write_i2c(dev); + } + + if (status || (dev->result)) { + u32 i2c_sr; + u32 cause; + + i2c_sr = readl(dev->virtbase + I2C_SR); + /* + * Check if the controller I2C operation status + * is set to ABORT(11b). + */ + if (((i2c_sr >> 2) & 0x3) == 0x3) { + /* get the abort cause */ + cause = (i2c_sr >> 4) & 0x7; + dev_err(&dev->pdev->dev, "%s\n", cause + >= ARRAY_SIZE(abort_causes) ? + "unknown reason" : + abort_causes[cause]); + } + + (void) init_hw(dev); + + status = status ? status : dev->result; + } + + return status; +} + +/** + * nmk_i2c_xfer() - I2C transfer function used by kernel framework + * @i2c_adap: Adapter pointer to the controller + * @msgs: Pointer to data to be written. + * @num_msgs: Number of messages to be executed + * + * This is the function called by the generic kernel i2c_transfer() + * or i2c_smbus...() API calls. Note that this code is protected by the + * semaphore set in the kernel i2c_transfer() function. + * + * NOTE: + * READ TRANSFER : We impose a restriction of the first message to be the + * index message for any read transaction. + * - a no index is coded as '0', + * - 2byte big endian index is coded as '3' + * !!! msg[0].buf holds the actual index. + * This is compatible with generic messages of smbus emulator + * that send a one byte index. + * eg. a I2C transation to read 2 bytes from index 0 + * idx = 0; + * msg[0].addr = client->addr; + * msg[0].flags = 0x0; + * msg[0].len = 1; + * msg[0].buf = &idx; + * + * msg[1].addr = client->addr; + * msg[1].flags = I2C_M_RD; + * msg[1].len = 2; + * msg[1].buf = rd_buff + * i2c_transfer(adap, msg, 2); + * + * WRITE TRANSFER : The I2C standard interface interprets all data as payload. + * If you want to emulate an SMBUS write transaction put the + * index as first byte(or first and second) in the payload. + * eg. a I2C transation to write 2 bytes from index 1 + * wr_buff[0] = 0x1; + * wr_buff[1] = 0x23; + * wr_buff[2] = 0x46; + * msg[0].flags = 0x0; + * msg[0].len = 3; + * msg[0].buf = wr_buff; + * i2c_transfer(adap, msg, 1); + * + * To read or write a block of data (multiple bytes) using SMBUS emulation + * please use the i2c_smbus_read_i2c_block_data() + * or i2c_smbus_write_i2c_block_data() API + */ +static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num_msgs) +{ + int status; + int i; + struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap); + int j; + + dev->busy = true; + + if (dev->regulator) + regulator_enable(dev->regulator); + pm_runtime_get_sync(&dev->pdev->dev); + + clk_enable(dev->clk); + + status = init_hw(dev); + if (status) + goto out; + + /* Attempt three times to send the message queue */ + for (j = 0; j < 3; j++) { + /* setup the i2c controller */ + setup_i2c_controller(dev); + + for (i = 0; i < num_msgs; i++) { + if (unlikely(msgs[i].flags & I2C_M_TEN)) { + dev_err(&dev->pdev->dev, "10 bit addressing" + "not supported\n"); + + status = -EINVAL; + goto out; + } + dev->cli.slave_adr = msgs[i].addr; + dev->cli.buffer = msgs[i].buf; + dev->cli.count = msgs[i].len; + dev->stop = (i < (num_msgs - 1)) ? 0 : 1; + dev->result = 0; + + status = nmk_i2c_xfer_one(dev, msgs[i].flags); + if (status != 0) + break; + } + if (status == 0) + break; + } + +out: + clk_disable(dev->clk); + pm_runtime_put_sync(&dev->pdev->dev); + if (dev->regulator) + regulator_disable(dev->regulator); + + dev->busy = false; + + /* return the no. messages processed */ + if (status) + return status; + else + return num_msgs; +} + +/** + * disable_interrupts() - disable the interrupts + * @dev: private data of controller + * @irq: interrupt number + */ +static int disable_interrupts(struct nmk_i2c_dev *dev, u32 irq) +{ + irq = IRQ_MASK(irq); + writel(readl(dev->virtbase + I2C_IMSCR) & ~(I2C_CLEAR_ALL_INTS & irq), + dev->virtbase + I2C_IMSCR); + return 0; +} + +/** + * i2c_irq_handler() - interrupt routine + * @irq: interrupt number + * @arg: data passed to the handler + * + * This is the interrupt handler for the i2c driver. Currently + * it handles the major interrupts like Rx & Tx FIFO management + * interrupts, master transaction interrupts, arbitration and + * bus error interrupts. The rest of the interrupts are treated as + * unhandled. + */ +static irqreturn_t i2c_irq_handler(int irq, void *arg) +{ + struct nmk_i2c_dev *dev = arg; + u32 tft, rft; + u32 count; + u32 misr; + u32 src = 0; + + /* load Tx FIFO and Rx FIFO threshold values */ + tft = readl(dev->virtbase + I2C_TFTR); + rft = readl(dev->virtbase + I2C_RFTR); + + /* read interrupt status register */ + misr = readl(dev->virtbase + I2C_MISR); + + src = __ffs(misr); + switch ((1 << src)) { + + /* Transmit FIFO nearly empty interrupt */ + case I2C_IT_TXFNE: + { + if (dev->cli.operation == I2C_READ) { + /* + * in read operation why do we care for writing? + * so disable the Transmit FIFO interrupt + */ + disable_interrupts(dev, I2C_IT_TXFNE); + } else { + fill_tx_fifo(dev, (MAX_I2C_FIFO_THRESHOLD - tft)); + /* + * if done, close the transfer by disabling the + * corresponding TXFNE interrupt + */ + if (dev->cli.count == 0) + disable_interrupts(dev, I2C_IT_TXFNE); + } + } + break; + + /* + * Rx FIFO nearly full interrupt. + * This is set when the numer of entries in Rx FIFO is + * greater or equal than the threshold value programmed + * in RFT + */ + case I2C_IT_RXFNF: + for (count = rft; count > 0; count--) { + /* Read the Rx FIFO */ + *dev->cli.buffer = readb(dev->virtbase + I2C_RFR); + dev->cli.buffer++; + } + dev->cli.count -= rft; + dev->cli.xfer_bytes += rft; + break; + + /* Rx FIFO full */ + case I2C_IT_RXFF: + for (count = MAX_I2C_FIFO_THRESHOLD; count > 0; count--) { + *dev->cli.buffer = readb(dev->virtbase + I2C_RFR); + dev->cli.buffer++; + } + dev->cli.count -= MAX_I2C_FIFO_THRESHOLD; + dev->cli.xfer_bytes += MAX_I2C_FIFO_THRESHOLD; + break; + + /* Master Transaction Done with/without stop */ + case I2C_IT_MTD: + case I2C_IT_MTDWS: + if (dev->cli.operation == I2C_READ) { + while (!(readl(dev->virtbase + I2C_RISR) + & I2C_IT_RXFE)) { + if (dev->cli.count == 0) + break; + *dev->cli.buffer = + readb(dev->virtbase + I2C_RFR); + dev->cli.buffer++; + dev->cli.count--; + dev->cli.xfer_bytes++; + } + } + + disable_all_interrupts(dev); + clear_all_interrupts(dev); + + if (dev->cli.count) { + dev->result = -EIO; + dev_err(&dev->pdev->dev, "%lu bytes still remain to be" + "xfered\n", dev->cli.count); + (void) init_hw(dev); + } + complete(&dev->xfer_complete); + + break; + + /* Master Arbitration lost interrupt */ + case I2C_IT_MAL: + dev->result = -EIO; + (void) init_hw(dev); + + i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MAL); + complete(&dev->xfer_complete); + + break; + + /* + * Bus Error interrupt. + * This happens when an unexpected start/stop condition occurs + * during the transaction. + */ + case I2C_IT_BERR: + dev->result = -EIO; + /* get the status */ + if (((readl(dev->virtbase + I2C_SR) >> 2) & 0x3) == I2C_ABORT) + (void) init_hw(dev); + + i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_BERR); + complete(&dev->xfer_complete); + + break; + + /* + * Tx FIFO overrun interrupt. + * This is set when a write operation in Tx FIFO is performed and + * the Tx FIFO is full. + */ + case I2C_IT_TXFOVR: + dev->result = -EIO; + (void) init_hw(dev); + + dev_err(&dev->pdev->dev, "Tx Fifo Over run\n"); + complete(&dev->xfer_complete); + + break; + + /* unhandled interrupts by this driver - TODO*/ + case I2C_IT_TXFE: + case I2C_IT_TXFF: + case I2C_IT_RXFE: + case I2C_IT_RFSR: + case I2C_IT_RFSE: + case I2C_IT_WTSR: + case I2C_IT_STD: + dev_err(&dev->pdev->dev, "unhandled Interrupt\n"); + break; + default: + dev_err(&dev->pdev->dev, "spurious Interrupt..\n"); + break; + } + + return IRQ_HANDLED; +} + + +#ifdef CONFIG_PM +static int nmk_i2c_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev); + + if (nmk_i2c->busy) + return -EBUSY; + + return 0; +} + +static int nmk_i2c_resume(struct device *dev) +{ + return 0; +} +#else +#define nmk_i2c_suspend NULL +#define nmk_i2c_resume NULL +#endif + +/* + * We use noirq so that we suspend late and resume before the wakeup interrupt + * to ensure that we do the !pm_runtime_suspended() check in resume before + * there has been a regular pm runtime resume (via pm_runtime_get_sync()). + */ +static const struct dev_pm_ops nmk_i2c_pm = { + .suspend_noirq = nmk_i2c_suspend, + .resume_noirq = nmk_i2c_resume, +}; + +static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm nmk_i2c_algo = { + .master_xfer = nmk_i2c_xfer, + .functionality = nmk_i2c_functionality +}; + +static int __devinit nmk_i2c_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res; + struct nmk_i2c_controller *pdata = + pdev->dev.platform_data; + struct nmk_i2c_dev *dev; + struct i2c_adapter *adap; + + dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL); + if (!dev) { + dev_err(&pdev->dev, "cannot allocate memory\n"); + ret = -ENOMEM; + goto err_no_mem; + } + dev->busy = false; + dev->pdev = pdev; + platform_set_drvdata(pdev, dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOENT; + goto err_no_resource; + } + + if (request_mem_region(res->start, resource_size(res), + DRIVER_NAME "I/O region") == NULL) { + ret = -EBUSY; + goto err_no_region; + } + + dev->virtbase = ioremap(res->start, resource_size(res)); + if (!dev->virtbase) { + ret = -ENOMEM; + goto err_no_ioremap; + } + + dev->irq = platform_get_irq(pdev, 0); + ret = request_irq(dev->irq, i2c_irq_handler, IRQF_DISABLED, + DRIVER_NAME, dev); + if (ret) { + dev_err(&pdev->dev, "cannot claim the irq %d\n", dev->irq); + goto err_irq; + } + + dev->regulator = regulator_get(&pdev->dev, "v-i2c"); + if (IS_ERR(dev->regulator)) { + dev_warn(&pdev->dev, "could not get i2c regulator\n"); + dev->regulator = NULL; + } + + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) { + dev_err(&pdev->dev, "could not get i2c clock\n"); + ret = PTR_ERR(dev->clk); + goto err_no_clk; + } + + adap = &dev->adap; + adap->dev.parent = &pdev->dev; + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->algo = &nmk_i2c_algo; + adap->timeout = pdata->timeout ? msecs_to_jiffies(pdata->timeout) : + msecs_to_jiffies(20000); + snprintf(adap->name, sizeof(adap->name), + "Nomadik I2C%d at %lx", pdev->id, (unsigned long)res->start); + + /* fetch the controller id */ + adap->nr = pdev->id; + + /* fetch the controller configuration from machine */ + dev->cfg.clk_freq = pdata->clk_freq; + dev->cfg.slsu = pdata->slsu; + dev->cfg.tft = pdata->tft; + dev->cfg.rft = pdata->rft; + dev->cfg.sm = pdata->sm; + + i2c_set_adapdata(adap, dev); + + dev_info(&pdev->dev, "initialize %s on virtual " + "base %p\n", adap->name, dev->virtbase); + + ret = i2c_add_numbered_adapter(adap); + if (ret) { + dev_err(&pdev->dev, "failed to add adapter\n"); + goto err_add_adap; + } + + return 0; + + err_add_adap: + clk_put(dev->clk); + err_no_clk: + if (dev->regulator) + regulator_put(dev->regulator); + pm_runtime_disable(&pdev->dev); + free_irq(dev->irq, dev); + err_irq: + iounmap(dev->virtbase); + err_no_ioremap: + release_mem_region(res->start, resource_size(res)); + err_no_region: + platform_set_drvdata(pdev, NULL); + err_no_resource: + kfree(dev); + err_no_mem: + + return ret; +} + +static int __devexit nmk_i2c_remove(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct nmk_i2c_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adap); + flush_i2c_fifo(dev); + disable_all_interrupts(dev); + clear_all_interrupts(dev); + /* disable the controller */ + i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE); + free_irq(dev->irq, dev); + iounmap(dev->virtbase); + if (res) + release_mem_region(res->start, resource_size(res)); + clk_put(dev->clk); + if (dev->regulator) + regulator_put(dev->regulator); + pm_runtime_disable(&pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(dev); + + return 0; +} + +static struct platform_driver nmk_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .pm = &nmk_i2c_pm, + }, + .probe = nmk_i2c_probe, + .remove = __devexit_p(nmk_i2c_remove), +}; + +static int __init nmk_i2c_init(void) +{ + return platform_driver_register(&nmk_i2c_driver); +} + +static void __exit nmk_i2c_exit(void) +{ + platform_driver_unregister(&nmk_i2c_driver); +} + +subsys_initcall(nmk_i2c_init); +module_exit(nmk_i2c_exit); + +MODULE_AUTHOR("Sachin Verma, Srinidhi KASAGAR"); +MODULE_DESCRIPTION("Nomadik/Ux500 I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/i2c/busses/i2c-nuc900.c b/drivers/i2c/busses/i2c-nuc900.c new file mode 100644 index 00000000..72434263 --- /dev/null +++ b/drivers/i2c/busses/i2c-nuc900.c @@ -0,0 +1,708 @@ +/* + * linux/drivers/i2c/busses/i2c-nuc900.c + * + * Copyright (c) 2010 Nuvoton technology corporation. + * + * This driver based on S3C2410 I2C driver of Ben Dooks <ben-Y5A6D6n0/KfQXOPxS62xeg@public.gmane.org>. + * Written by Wan ZongShun <mcuos.com-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.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;version 2 of the License. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/cpufreq.h> +#include <linux/slab.h> +#include <linux/io.h> + +#include <mach/mfp.h> +#include <mach/i2c.h> + +/* nuc900 i2c registers offset */ + +#define CSR 0x00 +#define DIVIDER 0x04 +#define CMDR 0x08 +#define SWR 0x0C +#define RXR 0x10 +#define TXR 0x14 + +/* nuc900 i2c CSR register bits */ + +#define IRQEN 0x003 +#define I2CBUSY 0x400 +#define I2CSTART 0x018 +#define IRQFLAG 0x004 +#define ARBIT_LOST 0x200 +#define SLAVE_ACK 0x800 + +/* nuc900 i2c CMDR register bits */ + +#define I2C_CMD_START 0x10 +#define I2C_CMD_STOP 0x08 +#define I2C_CMD_READ 0x04 +#define I2C_CMD_WRITE 0x02 +#define I2C_CMD_NACK 0x01 + +/* i2c controller state */ + +enum nuc900_i2c_state { + STATE_IDLE, + STATE_START, + STATE_READ, + STATE_WRITE, + STATE_STOP +}; + +/* i2c controller private data */ + +struct nuc900_i2c { + spinlock_t lock; + wait_queue_head_t wait; + + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int msg_idx; + unsigned int msg_ptr; + unsigned int irq; + + enum nuc900_i2c_state state; + + void __iomem *regs; + struct clk *clk; + struct device *dev; + struct resource *ioarea; + struct i2c_adapter adap; +}; + +/* nuc900_i2c_master_complete + * + * complete the message and wake up the caller, using the given return code, + * or zero to mean ok. +*/ + +static inline void nuc900_i2c_master_complete(struct nuc900_i2c *i2c, int ret) +{ + dev_dbg(i2c->dev, "master_complete %d\n", ret); + + i2c->msg_ptr = 0; + i2c->msg = NULL; + i2c->msg_idx++; + i2c->msg_num = 0; + if (ret) + i2c->msg_idx = ret; + + wake_up(&i2c->wait); +} + +/* irq enable/disable functions */ + +static inline void nuc900_i2c_disable_irq(struct nuc900_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + CSR); + writel(tmp & ~IRQEN, i2c->regs + CSR); +} + +static inline void nuc900_i2c_enable_irq(struct nuc900_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + CSR); + writel(tmp | IRQEN, i2c->regs + CSR); +} + + +/* nuc900_i2c_message_start + * + * put the start of a message onto the bus +*/ + +static void nuc900_i2c_message_start(struct nuc900_i2c *i2c, + struct i2c_msg *msg) +{ + unsigned int addr = (msg->addr & 0x7f) << 1; + + if (msg->flags & I2C_M_RD) + addr |= 0x1; + writel(addr & 0xff, i2c->regs + TXR); + writel(I2C_CMD_START | I2C_CMD_WRITE, i2c->regs + CMDR); +} + +static inline void nuc900_i2c_stop(struct nuc900_i2c *i2c, int ret) +{ + + dev_dbg(i2c->dev, "STOP\n"); + + /* stop the transfer */ + i2c->state = STATE_STOP; + writel(I2C_CMD_STOP, i2c->regs + CMDR); + + nuc900_i2c_master_complete(i2c, ret); + nuc900_i2c_disable_irq(i2c); +} + +/* helper functions to determine the current state in the set of + * messages we are sending +*/ + +/* is_lastmsg() + * + * returns TRUE if the current message is the last in the set +*/ + +static inline int is_lastmsg(struct nuc900_i2c *i2c) +{ + return i2c->msg_idx >= (i2c->msg_num - 1); +} + +/* is_msglast + * + * returns TRUE if we this is the last byte in the current message +*/ + +static inline int is_msglast(struct nuc900_i2c *i2c) +{ + return i2c->msg_ptr == i2c->msg->len-1; +} + +/* is_msgend + * + * returns TRUE if we reached the end of the current message +*/ + +static inline int is_msgend(struct nuc900_i2c *i2c) +{ + return i2c->msg_ptr >= i2c->msg->len; +} + +/* i2c_nuc900_irq_nextbyte + * + * process an interrupt and work out what to do + */ + +static void i2c_nuc900_irq_nextbyte(struct nuc900_i2c *i2c, + unsigned long iicstat) +{ + unsigned char byte; + + switch (i2c->state) { + + case STATE_IDLE: + dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__); + break; + + case STATE_STOP: + dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__); + nuc900_i2c_disable_irq(i2c); + break; + + case STATE_START: + /* last thing we did was send a start condition on the + * bus, or started a new i2c message + */ + + if (iicstat & SLAVE_ACK && + !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { + /* ack was not received... */ + + dev_dbg(i2c->dev, "ack was not received\n"); + nuc900_i2c_stop(i2c, -ENXIO); + break; + } + + if (i2c->msg->flags & I2C_M_RD) + i2c->state = STATE_READ; + else + i2c->state = STATE_WRITE; + + /* terminate the transfer if there is nothing to do + * as this is used by the i2c probe to find devices. + */ + + if (is_lastmsg(i2c) && i2c->msg->len == 0) { + nuc900_i2c_stop(i2c, 0); + break; + } + + if (i2c->state == STATE_READ) + goto prepare_read; + + /* fall through to the write state, as we will need to + * send a byte as well + */ + + case STATE_WRITE: + /* we are writing data to the device... check for the + * end of the message, and if so, work out what to do + */ + + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { + if (iicstat & SLAVE_ACK) { + dev_dbg(i2c->dev, "WRITE: No Ack\n"); + + nuc900_i2c_stop(i2c, -ECONNREFUSED); + break; + } + } + +retry_write: + + if (!is_msgend(i2c)) { + byte = i2c->msg->buf[i2c->msg_ptr++]; + writeb(byte, i2c->regs + TXR); + writel(I2C_CMD_WRITE, i2c->regs + CMDR); + + } else if (!is_lastmsg(i2c)) { + /* we need to go to the next i2c message */ + + dev_dbg(i2c->dev, "WRITE: Next Message\n"); + + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + + /* check to see if we need to do another message */ + if (i2c->msg->flags & I2C_M_NOSTART) { + + if (i2c->msg->flags & I2C_M_RD) { + /* cannot do this, the controller + * forces us to send a new START + * when we change direction + */ + + nuc900_i2c_stop(i2c, -EINVAL); + } + + goto retry_write; + } else { + /* send the new start */ + nuc900_i2c_message_start(i2c, i2c->msg); + i2c->state = STATE_START; + } + + } else { + /* send stop */ + + nuc900_i2c_stop(i2c, 0); + } + break; + + case STATE_READ: + /* we have a byte of data in the data register, do + * something with it, and then work out wether we are + * going to do any more read/write + */ + + byte = readb(i2c->regs + RXR); + i2c->msg->buf[i2c->msg_ptr++] = byte; + +prepare_read: + if (is_msglast(i2c)) { + /* last byte of buffer */ + + if (is_lastmsg(i2c)) + writel(I2C_CMD_READ | I2C_CMD_NACK, + i2c->regs + CMDR); + + } else if (is_msgend(i2c)) { + /* ok, we've read the entire buffer, see if there + * is anything else we need to do + */ + + if (is_lastmsg(i2c)) { + /* last message, send stop and complete */ + dev_dbg(i2c->dev, "READ: Send Stop\n"); + + nuc900_i2c_stop(i2c, 0); + } else { + /* go to the next transfer */ + dev_dbg(i2c->dev, "READ: Next Transfer\n"); + + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + + writel(I2C_CMD_READ, i2c->regs + CMDR); + } + + } else { + writel(I2C_CMD_READ, i2c->regs + CMDR); + } + + break; + } +} + +/* nuc900_i2c_irq + * + * top level IRQ servicing routine +*/ + +static irqreturn_t nuc900_i2c_irq(int irqno, void *dev_id) +{ + struct nuc900_i2c *i2c = dev_id; + unsigned long status; + + status = readl(i2c->regs + CSR); + writel(status | IRQFLAG, i2c->regs + CSR); + + if (status & ARBIT_LOST) { + /* deal with arbitration loss */ + dev_err(i2c->dev, "deal with arbitration loss\n"); + goto out; + } + + if (i2c->state == STATE_IDLE) { + dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n"); + goto out; + } + + /* pretty much this leaves us with the fact that we've + * transmitted or received whatever byte we last sent + */ + + i2c_nuc900_irq_nextbyte(i2c, status); + + out: + return IRQ_HANDLED; +} + + +/* nuc900_i2c_set_master + * + * get the i2c bus for a master transaction +*/ + +static int nuc900_i2c_set_master(struct nuc900_i2c *i2c) +{ + int timeout = 400; + + while (timeout-- > 0) { + if (((readl(i2c->regs + SWR) & I2CSTART) == I2CSTART) && + ((readl(i2c->regs + CSR) & I2CBUSY) == 0)) { + return 0; + } + + msleep(1); + } + + return -ETIMEDOUT; +} + +/* nuc900_i2c_doxfer + * + * this starts an i2c transfer +*/ + +static int nuc900_i2c_doxfer(struct nuc900_i2c *i2c, + struct i2c_msg *msgs, int num) +{ + unsigned long iicstat, timeout; + int spins = 20; + int ret; + + ret = nuc900_i2c_set_master(i2c); + if (ret != 0) { + dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); + ret = -EAGAIN; + goto out; + } + + spin_lock_irq(&i2c->lock); + + i2c->msg = msgs; + i2c->msg_num = num; + i2c->msg_ptr = 0; + i2c->msg_idx = 0; + i2c->state = STATE_START; + + nuc900_i2c_message_start(i2c, msgs); + spin_unlock_irq(&i2c->lock); + + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); + + ret = i2c->msg_idx; + + /* having these next two as dev_err() makes life very + * noisy when doing an i2cdetect + */ + + if (timeout == 0) + dev_dbg(i2c->dev, "timeout\n"); + else if (ret != num) + dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + + /* ensure the stop has been through the bus */ + + dev_dbg(i2c->dev, "waiting for bus idle\n"); + + /* first, try busy waiting briefly */ + do { + iicstat = readl(i2c->regs + CSR); + } while ((iicstat & I2CBUSY) && --spins); + + /* if that timed out sleep */ + if (!spins) { + msleep(1); + iicstat = readl(i2c->regs + CSR); + } + + if (iicstat & I2CBUSY) + dev_warn(i2c->dev, "timeout waiting for bus idle\n"); + + out: + return ret; +} + +/* nuc900_i2c_xfer + * + * first port of call from the i2c bus code when an message needs + * transferring across the i2c bus. +*/ + +static int nuc900_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct nuc900_i2c *i2c = (struct nuc900_i2c *)adap->algo_data; + int retry; + int ret; + + nuc900_i2c_enable_irq(i2c); + + for (retry = 0; retry < adap->retries; retry++) { + + ret = nuc900_i2c_doxfer(i2c, msgs, num); + + if (ret != -EAGAIN) + return ret; + + dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); + + udelay(100); + } + + return -EREMOTEIO; +} + +/* declare our i2c functionality */ +static u32 nuc900_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; +} + +/* i2c bus registration info */ + +static const struct i2c_algorithm nuc900_i2c_algorithm = { + .master_xfer = nuc900_i2c_xfer, + .functionality = nuc900_i2c_func, +}; + +/* nuc900_i2c_probe + * + * called by the bus driver when a suitable device is found +*/ + +static int __devinit nuc900_i2c_probe(struct platform_device *pdev) +{ + struct nuc900_i2c *i2c; + struct nuc900_platform_i2c *pdata; + struct resource *res; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + + i2c = kzalloc(sizeof(struct nuc900_i2c), GFP_KERNEL); + if (!i2c) { + dev_err(&pdev->dev, "no memory for state\n"); + return -ENOMEM; + } + + strlcpy(i2c->adap.name, "nuc900-i2c0", sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &nuc900_i2c_algorithm; + i2c->adap.retries = 2; + i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + + /* find the clock and enable it */ + + i2c->dev = &pdev->dev; + i2c->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + ret = -ENOENT; + goto err_noclk; + } + + dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); + + clk_enable(i2c->clk); + + /* map the registers */ + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "cannot find IO resource\n"); + ret = -ENOENT; + goto err_clk; + } + + i2c->ioarea = request_mem_region(res->start, resource_size(res), + pdev->name); + + if (i2c->ioarea == NULL) { + dev_err(&pdev->dev, "cannot request IO\n"); + ret = -ENXIO; + goto err_clk; + } + + i2c->regs = ioremap(res->start, resource_size(res)); + + if (i2c->regs == NULL) { + dev_err(&pdev->dev, "cannot map IO\n"); + ret = -ENXIO; + goto err_ioarea; + } + + dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", + i2c->regs, i2c->ioarea, res); + + /* setup info block for the i2c core */ + + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &pdev->dev; + + mfp_set_groupg(&pdev->dev); + + clk_get_rate(i2c->clk); + + ret = (i2c->clk.apbfreq)/(pdata->bus_freq * 5) - 1; + writel(ret & 0xffff, i2c->regs + DIVIDER); + + /* find the IRQ for this unit (note, this relies on the init call to + * ensure no current IRQs pending + */ + + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + goto err_iomap; + } + + ret = request_irq(i2c->irq, nuc900_i2c_irq, IRQF_DISABLED | IRQF_SHARED, + dev_name(&pdev->dev), i2c); + + if (ret != 0) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); + goto err_iomap; + } + + /* Note, previous versions of the driver used i2c_add_adapter() + * to add the bus at any number. We now pass the bus number via + * the platform data, so if unset it will now default to always + * being bus 0. + */ + + i2c->adap.nr = pdata->bus_num; + + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + goto err_irq; + } + + platform_set_drvdata(pdev, i2c); + + dev_info(&pdev->dev, "%s: NUC900 I2C adapter\n", + dev_name(&i2c->adap.dev)); + return 0; + + err_irq: + free_irq(i2c->irq, i2c); + + err_iomap: + iounmap(i2c->regs); + + err_ioarea: + release_resource(i2c->ioarea); + kfree(i2c->ioarea); + + err_clk: + clk_disable(i2c->clk); + clk_put(i2c->clk); + + err_noclk: + kfree(i2c); + return ret; +} + +/* nuc900_i2c_remove + * + * called when device is removed from the bus +*/ + +static int __devexit nuc900_i2c_remove(struct platform_device *pdev) +{ + struct nuc900_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + free_irq(i2c->irq, i2c); + + clk_disable(i2c->clk); + clk_put(i2c->clk); + + iounmap(i2c->regs); + + release_resource(i2c->ioarea); + kfree(i2c->ioarea); + kfree(i2c); + + return 0; +} + +static struct platform_driver nuc900_i2c_driver = { + .probe = nuc900_i2c_probe, + .remove = __devexit_p(nuc900_i2c_remove), + .driver = { + .owner = THIS_MODULE, + .name = "nuc900-i2c0", + }, +}; + +static int __init i2c_adap_nuc900_init(void) +{ + return platform_driver_register(&nuc900_i2c_driver); +} + +static void __exit i2c_adap_nuc900_exit(void) +{ + platform_driver_unregister(&nuc900_i2c_driver); +} +subsys_initcall(i2c_adap_nuc900_init); +module_exit(i2c_adap_nuc900_exit); + +MODULE_DESCRIPTION("NUC900 I2C Bus driver"); +MODULE_AUTHOR("Wan ZongShun, <mcuos.com-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:nuc900-i2c0"); diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c new file mode 100644 index 00000000..1b46a9d9 --- /dev/null +++ b/drivers/i2c/busses/i2c-ocores.c @@ -0,0 +1,427 @@ +/* + * i2c-ocores.c: I2C bus driver for OpenCores I2C controller + * (http://www.opencores.org/projects.cgi/web/i2c/overview). + * + * Peter Korsgaard <jacmet@sunsite.dk> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/* + * Device tree configuration: + * + * Required properties: + * - compatible : "opencores,i2c-ocores" + * - reg : bus address start and address range size of device + * - interrupts : interrupt number + * - regstep : size of device registers in bytes + * - clock-frequency : frequency of bus clock in Hz + * + * Example: + * + * i2c0: ocores@a0000000 { + * compatible = "opencores,i2c-ocores"; + * reg = <0xa0000000 0x8>; + * interrupts = <10>; + * + * regstep = <1>; + * clock-frequency = <20000000>; + * + * -- Devices connected on this I2C bus get + * -- defined here; address- and size-cells + * -- apply to these child devices + * + * #address-cells = <1>; + * #size-cells = <0>; + * + * dummy@60 { + * compatible = "dummy"; + * reg = <60>; + * }; + * }; + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/i2c-ocores.h> +#include <linux/slab.h> +#include <linux/io.h> + +struct ocores_i2c { + void __iomem *base; + int regstep; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_msg *msg; + int pos; + int nmsgs; + int state; /* see STATE_ */ + int clock_khz; +}; + +/* registers */ +#define OCI2C_PRELOW 0 +#define OCI2C_PREHIGH 1 +#define OCI2C_CONTROL 2 +#define OCI2C_DATA 3 +#define OCI2C_CMD 4 /* write only */ +#define OCI2C_STATUS 4 /* read only, same address as OCI2C_CMD */ + +#define OCI2C_CTRL_IEN 0x40 +#define OCI2C_CTRL_EN 0x80 + +#define OCI2C_CMD_START 0x91 +#define OCI2C_CMD_STOP 0x41 +#define OCI2C_CMD_READ 0x21 +#define OCI2C_CMD_WRITE 0x11 +#define OCI2C_CMD_READ_ACK 0x21 +#define OCI2C_CMD_READ_NACK 0x29 +#define OCI2C_CMD_IACK 0x01 + +#define OCI2C_STAT_IF 0x01 +#define OCI2C_STAT_TIP 0x02 +#define OCI2C_STAT_ARBLOST 0x20 +#define OCI2C_STAT_BUSY 0x40 +#define OCI2C_STAT_NACK 0x80 + +#define STATE_DONE 0 +#define STATE_START 1 +#define STATE_WRITE 2 +#define STATE_READ 3 +#define STATE_ERROR 4 + +static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite8(value, i2c->base + reg * i2c->regstep); +} + +static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) +{ + return ioread8(i2c->base + reg * i2c->regstep); +} + +static void ocores_process(struct ocores_i2c *i2c) +{ + struct i2c_msg *msg = i2c->msg; + u8 stat = oc_getreg(i2c, OCI2C_STATUS); + + if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) { + /* stop has been sent */ + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); + wake_up(&i2c->wait); + return; + } + + /* error? */ + if (stat & OCI2C_STAT_ARBLOST) { + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + return; + } + + if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { + i2c->state = + (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + + if (stat & OCI2C_STAT_NACK) { + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + return; + } + } else + msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); + + /* end of msg? */ + if (i2c->pos == msg->len) { + i2c->nmsgs--; + i2c->msg++; + i2c->pos = 0; + msg = i2c->msg; + + if (i2c->nmsgs) { /* end? */ + /* send start? */ + if (!(msg->flags & I2C_M_NOSTART)) { + u8 addr = (msg->addr << 1); + + if (msg->flags & I2C_M_RD) + addr |= 1; + + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, addr); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + return; + } else + i2c->state = (msg->flags & I2C_M_RD) + ? STATE_READ : STATE_WRITE; + } else { + i2c->state = STATE_DONE; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + return; + } + } + + if (i2c->state == STATE_READ) { + oc_setreg(i2c, OCI2C_CMD, i2c->pos == (msg->len-1) ? + OCI2C_CMD_READ_NACK : OCI2C_CMD_READ_ACK); + } else { + oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE); + } +} + +static irqreturn_t ocores_isr(int irq, void *dev_id) +{ + struct ocores_i2c *i2c = dev_id; + + ocores_process(i2c); + + return IRQ_HANDLED; +} + +static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct ocores_i2c *i2c = i2c_get_adapdata(adap); + + i2c->msg = msgs; + i2c->pos = 0; + i2c->nmsgs = num; + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, + (i2c->msg->addr << 1) | + ((i2c->msg->flags & I2C_M_RD) ? 1:0)); + + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + + if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ)) + return (i2c->state == STATE_DONE) ? num : -EIO; + else + return -ETIMEDOUT; +} + +static void ocores_init(struct ocores_i2c *i2c) +{ + int prescale; + u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* make sure the device is disabled */ + oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + + prescale = (i2c->clock_khz / (5*100)) - 1; + oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff); + oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8); + + /* Init the device */ + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN); +} + + +static u32 ocores_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm ocores_algorithm = { + .master_xfer = ocores_xfer, + .functionality = ocores_func, +}; + +static struct i2c_adapter ocores_adapter = { + .owner = THIS_MODULE, + .name = "i2c-ocores", + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &ocores_algorithm, +}; + +#ifdef CONFIG_OF +static int ocores_i2c_of_probe(struct platform_device* pdev, + struct ocores_i2c* i2c) +{ + const __be32* val; + + val = of_get_property(pdev->dev.of_node, "regstep", NULL); + if (!val) { + dev_err(&pdev->dev, "Missing required parameter 'regstep'"); + return -ENODEV; + } + i2c->regstep = be32_to_cpup(val); + + val = of_get_property(pdev->dev.of_node, "clock-frequency", NULL); + if (!val) { + dev_err(&pdev->dev, + "Missing required parameter 'clock-frequency'"); + return -ENODEV; + } + i2c->clock_khz = be32_to_cpup(val) / 1000; + + return 0; +} +#else +#define ocores_i2c_of_probe(pdev,i2c) -ENODEV +#endif + +static int __devinit ocores_i2c_probe(struct platform_device *pdev) +{ + struct ocores_i2c *i2c; + struct ocores_i2c_platform_data *pdata; + struct resource *res, *res2; + int ret; + int i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res2) + return -ENODEV; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Memory region busy\n"); + return -EBUSY; + } + + i2c->base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!i2c->base) { + dev_err(&pdev->dev, "Unable to map registers\n"); + return -EIO; + } + + pdata = pdev->dev.platform_data; + if (pdata) { + i2c->regstep = pdata->regstep; + i2c->clock_khz = pdata->clock_khz; + } else { + ret = ocores_i2c_of_probe(pdev, i2c); + if (ret) + return ret; + } + + ocores_init(i2c); + + init_waitqueue_head(&i2c->wait); + ret = devm_request_irq(&pdev->dev, res2->start, ocores_isr, 0, + pdev->name, i2c); + if (ret) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + return ret; + } + + /* hook up driver to tree */ + platform_set_drvdata(pdev, i2c); + i2c->adap = ocores_adapter; + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = pdev->dev.of_node; + + /* add i2c adapter to i2c tree */ + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + dev_err(&pdev->dev, "Failed to add adapter\n"); + return ret; + } + + /* add in known devices to the bus */ + if (pdata) { + for (i = 0; i < pdata->num_devices; i++) + i2c_new_device(&i2c->adap, pdata->devices + i); + } + + return 0; +} + +static int __devexit ocores_i2c_remove(struct platform_device* pdev) +{ + struct ocores_i2c *i2c = platform_get_drvdata(pdev); + + /* disable i2c logic */ + oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL) + & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + + /* remove adapter & data */ + i2c_del_adapter(&i2c->adap); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int ocores_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ocores_i2c *i2c = platform_get_drvdata(pdev); + u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* make sure the device is disabled */ + oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + + return 0; +} + +static int ocores_i2c_resume(struct platform_device *pdev) +{ + struct ocores_i2c *i2c = platform_get_drvdata(pdev); + + ocores_init(i2c); + + return 0; +} +#else +#define ocores_i2c_suspend NULL +#define ocores_i2c_resume NULL +#endif + +static struct of_device_id ocores_i2c_match[] = { + { .compatible = "opencores,i2c-ocores", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ocores_i2c_match); + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:ocores-i2c"); + +static struct platform_driver ocores_i2c_driver = { + .probe = ocores_i2c_probe, + .remove = __devexit_p(ocores_i2c_remove), + .suspend = ocores_i2c_suspend, + .resume = ocores_i2c_resume, + .driver = { + .owner = THIS_MODULE, + .name = "ocores-i2c", + .of_match_table = ocores_i2c_match, + }, +}; + +static int __init ocores_i2c_init(void) +{ + return platform_driver_register(&ocores_i2c_driver); +} + +static void __exit ocores_i2c_exit(void) +{ + platform_driver_unregister(&ocores_i2c_driver); +} + +module_init(ocores_i2c_init); +module_exit(ocores_i2c_exit); + +MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>"); +MODULE_DESCRIPTION("OpenCores I2C bus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c new file mode 100644 index 00000000..56dbe54e --- /dev/null +++ b/drivers/i2c/busses/i2c-octeon.c @@ -0,0 +1,652 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick.ext@nsn.com + * + * Portions Copyright (C) 2010 Cavium Networks, Inc. + * + * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/init.h> + +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include <asm/octeon/octeon.h> + +#define DRV_NAME "i2c-octeon" + +/* The previous out-of-tree version was implicitly version 1.0. */ +#define DRV_VERSION "2.0" + +/* register offsets */ +#define SW_TWSI 0x00 +#define TWSI_INT 0x10 + +/* Controller command patterns */ +#define SW_TWSI_V 0x8000000000000000ull +#define SW_TWSI_EOP_TWSI_DATA 0x0C00000100000000ull +#define SW_TWSI_EOP_TWSI_CTL 0x0C00000200000000ull +#define SW_TWSI_EOP_TWSI_CLKCTL 0x0C00000300000000ull +#define SW_TWSI_EOP_TWSI_STAT 0x0C00000300000000ull +#define SW_TWSI_EOP_TWSI_RST 0x0C00000700000000ull +#define SW_TWSI_OP_TWSI_CLK 0x0800000000000000ull +#define SW_TWSI_R 0x0100000000000000ull + +/* Controller command and status bits */ +#define TWSI_CTL_CE 0x80 +#define TWSI_CTL_ENAB 0x40 +#define TWSI_CTL_STA 0x20 +#define TWSI_CTL_STP 0x10 +#define TWSI_CTL_IFLG 0x08 +#define TWSI_CTL_AAK 0x04 + +/* Some status values */ +#define STAT_START 0x08 +#define STAT_RSTART 0x10 +#define STAT_TXADDR_ACK 0x18 +#define STAT_TXDATA_ACK 0x28 +#define STAT_RXADDR_ACK 0x40 +#define STAT_RXDATA_ACK 0x50 +#define STAT_IDLE 0xF8 + +struct octeon_i2c { + wait_queue_head_t queue; + struct i2c_adapter adap; + int irq; + int twsi_freq; + int sys_freq; + resource_size_t twsi_phys; + void __iomem *twsi_base; + resource_size_t regsize; + struct device *dev; +}; + +/** + * octeon_i2c_write_sw - write an I2C core register. + * @i2c: The struct octeon_i2c. + * @eop_reg: Register selector. + * @data: Value to be written. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static void octeon_i2c_write_sw(struct octeon_i2c *i2c, + u64 eop_reg, + u8 data) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI); + } while ((tmp & SW_TWSI_V) != 0); +} + +/** + * octeon_i2c_read_sw - write an I2C core register. + * @i2c: The struct octeon_i2c. + * @eop_reg: Register selector. + * + * Returns the data. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI); + } while ((tmp & SW_TWSI_V) != 0); + + return tmp & 0xFF; +} + +/** + * octeon_i2c_write_int - write the TWSI_INT register + * @i2c: The struct octeon_i2c. + * @data: Value to be written. + */ +static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) +{ + u64 tmp; + + __raw_writeq(data, i2c->twsi_base + TWSI_INT); + tmp = __raw_readq(i2c->twsi_base + TWSI_INT); +} + +/** + * octeon_i2c_int_enable - enable the TS interrupt. + * @i2c: The struct octeon_i2c. + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, 0x40); +} + +/** + * octeon_i2c_int_disable - disable the TS interrupt. + * @i2c: The struct octeon_i2c. + */ +static void octeon_i2c_int_disable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, 0); +} + +/** + * octeon_i2c_unblock - unblock the bus. + * @i2c: The struct octeon_i2c. + * + * If there was a reset while a device was driving 0 to bus, + * bus is blocked. We toggle it free manually by some clock + * cycles and send a stop. + */ +static void octeon_i2c_unblock(struct octeon_i2c *i2c) +{ + int i; + + dev_dbg(i2c->dev, "%s\n", __func__); + for (i = 0; i < 9; i++) { + octeon_i2c_write_int(i2c, 0x0); + udelay(5); + octeon_i2c_write_int(i2c, 0x200); + udelay(5); + } + octeon_i2c_write_int(i2c, 0x300); + udelay(5); + octeon_i2c_write_int(i2c, 0x100); + udelay(5); + octeon_i2c_write_int(i2c, 0x0); +} + +/** + * octeon_i2c_isr - the interrupt service routine. + * @int: The irq, unused. + * @dev_id: Our struct octeon_i2c. + */ +static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + octeon_i2c_int_disable(i2c); + wake_up_interruptible(&i2c->queue); + + return IRQ_HANDLED; +} + + +static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) +{ + return (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set. + * @i2c: The struct octeon_i2c. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + int result; + + octeon_i2c_int_enable(i2c); + + result = wait_event_interruptible_timeout(i2c->queue, + octeon_i2c_test_iflg(i2c), + i2c->adap.timeout); + + octeon_i2c_int_disable(i2c); + + if (result < 0) { + dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); + return result; + } else if (result == 0) { + dev_dbg(i2c->dev, "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * octeon_i2c_start - send START to the bus. + * @i2c: The struct octeon_i2c. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_start(struct octeon_i2c *i2c) +{ + u8 data; + int result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STA); + + result = octeon_i2c_wait(i2c); + if (result) { + if (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) { + /* + * Controller refused to send start flag May + * be a client is holding SDA low - let's try + * to free it. + */ + octeon_i2c_unblock(i2c); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STA); + + result = octeon_i2c_wait(i2c); + } + if (result) + return result; + } + + data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((data != STAT_START) && (data != STAT_RSTART)) { + dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); + return -EIO; + } + + return 0; +} + +/** + * octeon_i2c_stop - send STOP to the bus. + * @i2c: The struct octeon_i2c. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_stop(struct octeon_i2c *i2c) +{ + u8 data; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STP); + + data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + + if (data != STAT_IDLE) { + dev_err(i2c->dev, "%s: bad status(0x%x)\n", __func__, data); + return -EIO; + } + return 0; +} + +/** + * octeon_i2c_write - send data to the bus. + * @i2c: The struct octeon_i2c. + * @target: Target address. + * @data: Pointer to the data to be sent. + * @length: Length of the data. + * + * The address is sent over the bus, then the data. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_write(struct octeon_i2c *i2c, int target, + const u8 *data, int length) +{ + int i, result; + u8 tmp; + + result = octeon_i2c_start(i2c); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + for (i = 0; i < length; i++) { + tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) { + dev_err(i2c->dev, + "%s: bad status before write (0x%x)\n", + __func__, tmp); + return -EIO; + } + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + } + + return 0; +} + +/** + * octeon_i2c_read - receive data from the bus. + * @i2c: The struct octeon_i2c. + * @target: Target address. + * @data: Pointer to the location to store the datae . + * @length: Length of the data. + * + * The address is sent over the bus, then the data is read. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_read(struct octeon_i2c *i2c, int target, + u8 *data, int length) +{ + int i, result; + u8 tmp; + + if (length < 1) + return -EINVAL; + + result = octeon_i2c_start(i2c); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target<<1) | 1); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + for (i = 0; i < length; i++) { + tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) { + dev_err(i2c->dev, + "%s: bad status before read (0x%x)\n", + __func__, tmp); + return -EIO; + } + + if (i+1 < length) + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_AAK); + else + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA); + } + return 0; +} + +/** + * octeon_i2c_xfer - The driver's master_xfer function. + * @adap: Pointer to the i2c_adapter structure. + * @msgs: Pointer to the messages to be processed. + * @num: Length of the MSGS array. + * + * Returns the number of messages processed, or a negative errno on + * failure. + */ +static int octeon_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_msg *pmsg; + int i; + int ret = 0; + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + + for (i = 0; ret == 0 && i < num; i++) { + pmsg = &msgs[i]; + dev_dbg(i2c->dev, + "Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->addr, i + 1, num); + if (pmsg->flags & I2C_M_RD) + ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, + pmsg->len); + else + ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, + pmsg->len); + } + octeon_i2c_stop(i2c); + + return (ret != 0) ? ret : num; +} + +static u32 octeon_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm octeon_i2c_algo = { + .master_xfer = octeon_i2c_xfer, + .functionality = octeon_i2c_functionality, +}; + +static struct i2c_adapter octeon_i2c_ops = { + .owner = THIS_MODULE, + .name = "OCTEON adapter", + .algo = &octeon_i2c_algo, + .timeout = 2, +}; + +/** + * octeon_i2c_setclock - Calculate and set clock divisors. + */ +static int __devinit octeon_i2c_setclock(struct octeon_i2c *i2c) +{ + int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; + int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; + + for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { + /* + * An mdiv value of less than 2 seems to not work well + * with ds1337 RTCs, so we constrain it to larger + * values. + */ + for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { + /* + * For given ndiv and mdiv values check the + * two closest thp values. + */ + tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; + tclk *= (1 << ndiv_idx); + thp_base = (i2c->sys_freq / (tclk * 2)) - 1; + for (inc = 0; inc <= 1; inc++) { + thp_idx = thp_base + inc; + if (thp_idx < 5 || thp_idx > 0xff) + continue; + + foscl = i2c->sys_freq / (2 * (thp_idx + 1)); + foscl = foscl / (1 << ndiv_idx); + foscl = foscl / (mdiv_idx + 1) / 10; + diff = abs(foscl - i2c->twsi_freq); + if (diff < delta_hz) { + delta_hz = diff; + thp = thp_idx; + mdiv = mdiv_idx; + ndiv = ndiv_idx; + } + } + } + } + octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); + + return 0; +} + +static int __devinit octeon_i2c_initlowlevel(struct octeon_i2c *i2c) +{ + u8 status; + int tries; + + /* disable high level controller, enable bus access */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + /* reset controller */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); + + for (tries = 10; tries; tries--) { + udelay(1); + status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if (status == STAT_IDLE) + return 0; + } + dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", __func__, status); + return -EIO; +} + +static int __devinit octeon_i2c_probe(struct platform_device *pdev) +{ + int irq, result = 0; + struct octeon_i2c *i2c; + struct octeon_i2c_data *i2c_data; + struct resource *res_mem; + + /* All adaptors have an irq. */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + if (!i2c) { + dev_err(&pdev->dev, "kzalloc failed\n"); + result = -ENOMEM; + goto out; + } + i2c->dev = &pdev->dev; + i2c_data = pdev->dev.platform_data; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (res_mem == NULL) { + dev_err(i2c->dev, "found no memory resource\n"); + result = -ENXIO; + goto fail_region; + } + + if (i2c_data == NULL) { + dev_err(i2c->dev, "no I2C frequency data\n"); + result = -ENXIO; + goto fail_region; + } + + i2c->twsi_phys = res_mem->start; + i2c->regsize = resource_size(res_mem); + i2c->twsi_freq = i2c_data->i2c_freq; + i2c->sys_freq = i2c_data->sys_freq; + + if (!request_mem_region(i2c->twsi_phys, i2c->regsize, res_mem->name)) { + dev_err(i2c->dev, "request_mem_region failed\n"); + goto fail_region; + } + i2c->twsi_base = ioremap(i2c->twsi_phys, i2c->regsize); + + init_waitqueue_head(&i2c->queue); + + i2c->irq = irq; + + result = request_irq(i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto fail_irq; + } + + result = octeon_i2c_initlowlevel(i2c); + if (result) { + dev_err(i2c->dev, "init low level failed\n"); + goto fail_add; + } + + result = octeon_i2c_setclock(i2c); + if (result) { + dev_err(i2c->dev, "clock init failed\n"); + goto fail_add; + } + + i2c->adap = octeon_i2c_ops; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0; + i2c_set_adapdata(&i2c->adap, i2c); + platform_set_drvdata(pdev, i2c); + + result = i2c_add_numbered_adapter(&i2c->adap); + if (result < 0) { + dev_err(i2c->dev, "failed to add adapter\n"); + goto fail_add; + } + + dev_info(i2c->dev, "version %s\n", DRV_VERSION); + + return result; + +fail_add: + platform_set_drvdata(pdev, NULL); + free_irq(i2c->irq, i2c); +fail_irq: + iounmap(i2c->twsi_base); + release_mem_region(i2c->twsi_phys, i2c->regsize); +fail_region: + kfree(i2c); +out: + return result; +}; + +static int __devexit octeon_i2c_remove(struct platform_device *pdev) +{ + struct octeon_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + platform_set_drvdata(pdev, NULL); + free_irq(i2c->irq, i2c); + iounmap(i2c->twsi_base); + release_mem_region(i2c->twsi_phys, i2c->regsize); + kfree(i2c); + return 0; +}; + +static struct platform_driver octeon_i2c_driver = { + .probe = octeon_i2c_probe, + .remove = __devexit_p(octeon_i2c_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init octeon_i2c_init(void) +{ + int rv; + + rv = platform_driver_register(&octeon_i2c_driver); + return rv; +} + +static void __exit octeon_i2c_exit(void) +{ + platform_driver_unregister(&octeon_i2c_driver); +} + +MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>"); +MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("platform:" DRV_NAME); + +module_init(octeon_i2c_init); +module_exit(octeon_i2c_exit); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c new file mode 100644 index 00000000..137e1a3b --- /dev/null +++ b/drivers/i2c/busses/i2c-omap.c @@ -0,0 +1,1197 @@ +/* + * TI OMAP I2C master mode driver + * + * Copyright (C) 2003 MontaVista Software, Inc. + * Copyright (C) 2005 Nokia Corporation + * Copyright (C) 2004 - 2007 Texas Instruments. + * + * Originally written by MontaVista Software, Inc. + * Additional contributions by: + * Tony Lindgren <tony@atomide.com> + * Imre Deak <imre.deak@nokia.com> + * Juha Yrjölä <juha.yrjola@solidboot.com> + * Syed Khasim <x0khasim@ti.com> + * Nishant Menon <nm@ti.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. + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/i2c-omap.h> +#include <linux/pm_runtime.h> + +/* I2C controller revisions */ +#define OMAP_I2C_REV_2 0x20 + +/* I2C controller revisions present on specific hardware */ +#define OMAP_I2C_REV_ON_2430 0x36 +#define OMAP_I2C_REV_ON_3430 0x3C +#define OMAP_I2C_REV_ON_4430 0x40 + +/* timeout waiting for the controller to respond */ +#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +/* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */ +enum { + OMAP_I2C_REV_REG = 0, + OMAP_I2C_IE_REG, + OMAP_I2C_STAT_REG, + OMAP_I2C_IV_REG, + OMAP_I2C_WE_REG, + OMAP_I2C_SYSS_REG, + OMAP_I2C_BUF_REG, + OMAP_I2C_CNT_REG, + OMAP_I2C_DATA_REG, + OMAP_I2C_SYSC_REG, + OMAP_I2C_CON_REG, + OMAP_I2C_OA_REG, + OMAP_I2C_SA_REG, + OMAP_I2C_PSC_REG, + OMAP_I2C_SCLL_REG, + OMAP_I2C_SCLH_REG, + OMAP_I2C_SYSTEST_REG, + OMAP_I2C_BUFSTAT_REG, + OMAP_I2C_REVNB_LO, + OMAP_I2C_REVNB_HI, + OMAP_I2C_IRQSTATUS_RAW, + OMAP_I2C_IRQENABLE_SET, + OMAP_I2C_IRQENABLE_CLR, +}; + +/* I2C Interrupt Enable Register (OMAP_I2C_IE): */ +#define OMAP_I2C_IE_XDR (1 << 14) /* TX Buffer drain int enable */ +#define OMAP_I2C_IE_RDR (1 << 13) /* RX Buffer drain int enable */ +#define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */ +#define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */ +#define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */ +#define OMAP_I2C_IE_NACK (1 << 1) /* No ack interrupt enable */ +#define OMAP_I2C_IE_AL (1 << 0) /* Arbitration lost int ena */ + +/* I2C Status Register (OMAP_I2C_STAT): */ +#define OMAP_I2C_STAT_XDR (1 << 14) /* TX Buffer draining */ +#define OMAP_I2C_STAT_RDR (1 << 13) /* RX Buffer draining */ +#define OMAP_I2C_STAT_BB (1 << 12) /* Bus busy */ +#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */ +#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ +#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */ +#define OMAP_I2C_STAT_AD0 (1 << 8) /* Address zero */ +#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ +#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */ +#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */ +#define OMAP_I2C_STAT_NACK (1 << 1) /* No ack interrupt enable */ +#define OMAP_I2C_STAT_AL (1 << 0) /* Arbitration lost int ena */ + +/* I2C WE wakeup enable register */ +#define OMAP_I2C_WE_XDR_WE (1 << 14) /* TX drain wakup */ +#define OMAP_I2C_WE_RDR_WE (1 << 13) /* RX drain wakeup */ +#define OMAP_I2C_WE_AAS_WE (1 << 9) /* Address as slave wakeup*/ +#define OMAP_I2C_WE_BF_WE (1 << 8) /* Bus free wakeup */ +#define OMAP_I2C_WE_STC_WE (1 << 6) /* Start condition wakeup */ +#define OMAP_I2C_WE_GC_WE (1 << 5) /* General call wakeup */ +#define OMAP_I2C_WE_DRDY_WE (1 << 3) /* TX/RX data ready wakeup */ +#define OMAP_I2C_WE_ARDY_WE (1 << 2) /* Reg access ready wakeup */ +#define OMAP_I2C_WE_NACK_WE (1 << 1) /* No acknowledgment wakeup */ +#define OMAP_I2C_WE_AL_WE (1 << 0) /* Arbitration lost wakeup */ + +#define OMAP_I2C_WE_ALL (OMAP_I2C_WE_XDR_WE | OMAP_I2C_WE_RDR_WE | \ + OMAP_I2C_WE_AAS_WE | OMAP_I2C_WE_BF_WE | \ + OMAP_I2C_WE_STC_WE | OMAP_I2C_WE_GC_WE | \ + OMAP_I2C_WE_DRDY_WE | OMAP_I2C_WE_ARDY_WE | \ + OMAP_I2C_WE_NACK_WE | OMAP_I2C_WE_AL_WE) + +/* I2C Buffer Configuration Register (OMAP_I2C_BUF): */ +#define OMAP_I2C_BUF_RDMA_EN (1 << 15) /* RX DMA channel enable */ +#define OMAP_I2C_BUF_RXFIF_CLR (1 << 14) /* RX FIFO Clear */ +#define OMAP_I2C_BUF_XDMA_EN (1 << 7) /* TX DMA channel enable */ +#define OMAP_I2C_BUF_TXFIF_CLR (1 << 6) /* TX FIFO Clear */ + +/* I2C Configuration Register (OMAP_I2C_CON): */ +#define OMAP_I2C_CON_EN (1 << 15) /* I2C module enable */ +#define OMAP_I2C_CON_BE (1 << 14) /* Big endian mode */ +#define OMAP_I2C_CON_OPMODE_HS (1 << 12) /* High Speed support */ +#define OMAP_I2C_CON_STB (1 << 11) /* Start byte mode (master) */ +#define OMAP_I2C_CON_MST (1 << 10) /* Master/slave mode */ +#define OMAP_I2C_CON_TRX (1 << 9) /* TX/RX mode (master only) */ +#define OMAP_I2C_CON_XA (1 << 8) /* Expand address */ +#define OMAP_I2C_CON_RM (1 << 2) /* Repeat mode (master only) */ +#define OMAP_I2C_CON_STP (1 << 1) /* Stop cond (master only) */ +#define OMAP_I2C_CON_STT (1 << 0) /* Start condition (master) */ + +/* I2C SCL time value when Master */ +#define OMAP_I2C_SCLL_HSSCLL 8 +#define OMAP_I2C_SCLH_HSSCLH 8 + +/* I2C System Test Register (OMAP_I2C_SYSTEST): */ +#ifdef DEBUG +#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ +#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */ +#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */ +#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */ +#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */ +#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */ +#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */ +#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */ +#endif + +/* OCP_SYSSTATUS bit definitions */ +#define SYSS_RESETDONE_MASK (1 << 0) + +/* OCP_SYSCONFIG bit definitions */ +#define SYSC_CLOCKACTIVITY_MASK (0x3 << 8) +#define SYSC_SIDLEMODE_MASK (0x3 << 3) +#define SYSC_ENAWAKEUP_MASK (1 << 2) +#define SYSC_SOFTRESET_MASK (1 << 1) +#define SYSC_AUTOIDLE_MASK (1 << 0) + +#define SYSC_IDLEMODE_SMART 0x2 +#define SYSC_CLOCKACTIVITY_FCLK 0x2 + +/* Errata definitions */ +#define I2C_OMAP_ERRATA_I207 (1 << 0) +#define I2C_OMAP3_1P153 (1 << 1) + +struct omap_i2c_dev { + struct device *dev; + void __iomem *base; /* virtual */ + int irq; + int reg_shift; /* bit shift for I2C register addresses */ + struct completion cmd_complete; + struct resource *ioarea; + u32 latency; /* maximum mpu wkup latency */ + void (*set_mpu_wkup_lat)(struct device *dev, + long latency); + u32 speed; /* Speed of bus in Khz */ + u16 cmd_err; + u8 *buf; + u8 *regs; + size_t buf_len; + struct i2c_adapter adapter; + u8 fifo_size; /* use as flag and value + * fifo_size==0 implies no fifo + * if set, should be trsh+1 + */ + u8 rev; + unsigned b_hw:1; /* bad h/w fixes */ + unsigned idle:1; + u16 iestate; /* Saved interrupt register */ + u16 pscstate; + u16 scllstate; + u16 sclhstate; + u16 bufstate; + u16 syscstate; + u16 westate; + u16 errata; +}; + +const static u8 reg_map[] = { + [OMAP_I2C_REV_REG] = 0x00, + [OMAP_I2C_IE_REG] = 0x01, + [OMAP_I2C_STAT_REG] = 0x02, + [OMAP_I2C_IV_REG] = 0x03, + [OMAP_I2C_WE_REG] = 0x03, + [OMAP_I2C_SYSS_REG] = 0x04, + [OMAP_I2C_BUF_REG] = 0x05, + [OMAP_I2C_CNT_REG] = 0x06, + [OMAP_I2C_DATA_REG] = 0x07, + [OMAP_I2C_SYSC_REG] = 0x08, + [OMAP_I2C_CON_REG] = 0x09, + [OMAP_I2C_OA_REG] = 0x0a, + [OMAP_I2C_SA_REG] = 0x0b, + [OMAP_I2C_PSC_REG] = 0x0c, + [OMAP_I2C_SCLL_REG] = 0x0d, + [OMAP_I2C_SCLH_REG] = 0x0e, + [OMAP_I2C_SYSTEST_REG] = 0x0f, + [OMAP_I2C_BUFSTAT_REG] = 0x10, +}; + +const static u8 omap4_reg_map[] = { + [OMAP_I2C_REV_REG] = 0x04, + [OMAP_I2C_IE_REG] = 0x2c, + [OMAP_I2C_STAT_REG] = 0x28, + [OMAP_I2C_IV_REG] = 0x34, + [OMAP_I2C_WE_REG] = 0x34, + [OMAP_I2C_SYSS_REG] = 0x90, + [OMAP_I2C_BUF_REG] = 0x94, + [OMAP_I2C_CNT_REG] = 0x98, + [OMAP_I2C_DATA_REG] = 0x9c, + [OMAP_I2C_SYSC_REG] = 0x10, + [OMAP_I2C_CON_REG] = 0xa4, + [OMAP_I2C_OA_REG] = 0xa8, + [OMAP_I2C_SA_REG] = 0xac, + [OMAP_I2C_PSC_REG] = 0xb0, + [OMAP_I2C_SCLL_REG] = 0xb4, + [OMAP_I2C_SCLH_REG] = 0xb8, + [OMAP_I2C_SYSTEST_REG] = 0xbC, + [OMAP_I2C_BUFSTAT_REG] = 0xc0, + [OMAP_I2C_REVNB_LO] = 0x00, + [OMAP_I2C_REVNB_HI] = 0x04, + [OMAP_I2C_IRQSTATUS_RAW] = 0x24, + [OMAP_I2C_IRQENABLE_SET] = 0x2c, + [OMAP_I2C_IRQENABLE_CLR] = 0x30, +}; + +static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, + int reg, u16 val) +{ + __raw_writew(val, i2c_dev->base + + (i2c_dev->regs[reg] << i2c_dev->reg_shift)); +} + +static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg) +{ + return __raw_readw(i2c_dev->base + + (i2c_dev->regs[reg] << i2c_dev->reg_shift)); +} + +static void omap_i2c_unidle(struct omap_i2c_dev *dev) +{ + struct platform_device *pdev; + struct omap_i2c_bus_platform_data *pdata; + + WARN_ON(!dev->idle); + + pdev = to_platform_device(dev->dev); + pdata = pdev->dev.platform_data; + + pm_runtime_get_sync(&pdev->dev); + + if (cpu_is_omap34xx()) { + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate); + omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate); + omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, dev->sclhstate); + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, dev->bufstate); + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, dev->syscstate); + omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + } + dev->idle = 0; + + /* + * Don't write to this register if the IE state is 0 as it can + * cause deadlock. + */ + if (dev->iestate) + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); +} + +static void omap_i2c_idle(struct omap_i2c_dev *dev) +{ + struct platform_device *pdev; + struct omap_i2c_bus_platform_data *pdata; + u16 iv; + + WARN_ON(dev->idle); + + pdev = to_platform_device(dev->dev); + pdata = pdev->dev.platform_data; + + dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); + if (dev->rev >= OMAP_I2C_REV_ON_4430) + omap_i2c_write_reg(dev, OMAP_I2C_IRQENABLE_CLR, 1); + else + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0); + + if (dev->rev < OMAP_I2C_REV_2) { + iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */ + } else { + omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, dev->iestate); + + /* Flush posted write before the dev->idle store occurs */ + omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); + } + dev->idle = 1; + + pm_runtime_put_sync(&pdev->dev); +} + +static int omap_i2c_init(struct omap_i2c_dev *dev) +{ + u16 psc = 0, scll = 0, sclh = 0, buf = 0; + u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0; + unsigned long fclk_rate = 12000000; + unsigned long timeout; + unsigned long internal_clk = 0; + struct clk *fclk; + + if (dev->rev >= OMAP_I2C_REV_2) { + /* Disable I2C controller before soft reset */ + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, + omap_i2c_read_reg(dev, OMAP_I2C_CON_REG) & + ~(OMAP_I2C_CON_EN)); + + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, SYSC_SOFTRESET_MASK); + /* For some reason we need to set the EN bit before the + * reset done bit gets set. */ + timeout = jiffies + OMAP_I2C_TIMEOUT; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + while (!(omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG) & + SYSS_RESETDONE_MASK)) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, "timeout waiting " + "for controller reset\n"); + return -ETIMEDOUT; + } + msleep(1); + } + + /* SYSC register is cleared by the reset; rewrite it */ + if (dev->rev == OMAP_I2C_REV_ON_2430) { + + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, + SYSC_AUTOIDLE_MASK); + + } else if (dev->rev >= OMAP_I2C_REV_ON_3430) { + dev->syscstate = SYSC_AUTOIDLE_MASK; + dev->syscstate |= SYSC_ENAWAKEUP_MASK; + dev->syscstate |= (SYSC_IDLEMODE_SMART << + __ffs(SYSC_SIDLEMODE_MASK)); + dev->syscstate |= (SYSC_CLOCKACTIVITY_FCLK << + __ffs(SYSC_CLOCKACTIVITY_MASK)); + + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, + dev->syscstate); + /* + * Enabling all wakup sources to stop I2C freezing on + * WFI instruction. + * REVISIT: Some wkup sources might not be needed. + */ + dev->westate = OMAP_I2C_WE_ALL; + omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate); + } + } + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + + if (cpu_class_is_omap1()) { + /* + * The I2C functional clock is the armxor_ck, so there's + * no need to get "armxor_ck" separately. Now, if OMAP2420 + * always returns 12MHz for the functional clock, we can + * do this bit unconditionally. + */ + fclk = clk_get(dev->dev, "fck"); + fclk_rate = clk_get_rate(fclk); + clk_put(fclk); + + /* TRM for 5912 says the I2C clock must be prescaled to be + * between 7 - 12 MHz. The XOR input clock is typically + * 12, 13 or 19.2 MHz. So we should have code that produces: + * + * XOR MHz Divider Prescaler + * 12 1 0 + * 13 2 1 + * 19.2 2 1 + */ + if (fclk_rate > 12000000) + psc = fclk_rate / 12000000; + } + + if (!(cpu_class_is_omap1() || cpu_is_omap2420())) { + + /* + * HSI2C controller internal clk rate should be 19.2 Mhz for + * HS and for all modes on 2430. On 34xx we can use lower rate + * to get longer filter period for better noise suppression. + * The filter is iclk (fclk for HS) period. + */ + if (dev->speed > 400 || cpu_is_omap2430()) + internal_clk = 19200; + else if (dev->speed > 100) + internal_clk = 9600; + else + internal_clk = 4000; + fclk = clk_get(dev->dev, "fck"); + fclk_rate = clk_get_rate(fclk) / 1000; + clk_put(fclk); + + /* Compute prescaler divisor */ + psc = fclk_rate / internal_clk; + psc = psc - 1; + + /* If configured for High Speed */ + if (dev->speed > 400) { + unsigned long scl; + + /* For first phase of HS mode */ + scl = internal_clk / 400; + fsscll = scl - (scl / 3) - 7; + fssclh = (scl / 3) - 5; + + /* For second phase of HS mode */ + scl = fclk_rate / dev->speed; + hsscll = scl - (scl / 3) - 7; + hssclh = (scl / 3) - 5; + } else if (dev->speed > 100) { + unsigned long scl; + + /* Fast mode */ + scl = internal_clk / dev->speed; + fsscll = scl - (scl / 3) - 7; + fssclh = (scl / 3) - 5; + } else { + /* Standard mode */ + fsscll = internal_clk / (dev->speed * 2) - 7; + fssclh = internal_clk / (dev->speed * 2) - 5; + } + scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll; + sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh; + } else { + /* Program desired operating rate */ + fclk_rate /= (psc + 1) * 1000; + if (psc > 2) + psc = 2; + scll = fclk_rate / (dev->speed * 2) - 7 + psc; + sclh = fclk_rate / (dev->speed * 2) - 7 + psc; + } + + /* Setup clock prescaler to obtain approx 12MHz I2C module clock: */ + omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, psc); + + /* SCL low and high time values */ + omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll); + omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh); + + if (dev->fifo_size) { + /* Note: setup required fifo size - 1. RTRSH and XTRSH */ + buf = (dev->fifo_size - 1) << 8 | OMAP_I2C_BUF_RXFIF_CLR | + (dev->fifo_size - 1) | OMAP_I2C_BUF_TXFIF_CLR; + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf); + } + + /* Take the I2C module out of reset: */ + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + + dev->errata = 0; + + if (cpu_is_omap2430() || cpu_is_omap34xx()) + dev->errata |= I2C_OMAP_ERRATA_I207; + + /* Enable interrupts */ + dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | + OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | + OMAP_I2C_IE_AL) | ((dev->fifo_size) ? + (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0); + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); + if (cpu_is_omap34xx()) { + dev->pscstate = psc; + dev->scllstate = scll; + dev->sclhstate = sclh; + dev->bufstate = buf; + } + return 0; +} + +/* + * Waiting on Bus Busy + */ +static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev) +{ + unsigned long timeout; + + timeout = jiffies + OMAP_I2C_TIMEOUT; + while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + msleep(1); + } + + return 0; +} + +/* + * Low level master read/write transaction. + */ +static int omap_i2c_xfer_msg(struct i2c_adapter *adap, + struct i2c_msg *msg, int stop) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + int r; + u16 w; + + dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", + msg->addr, msg->len, msg->flags, stop); + + if (msg->len == 0) + return -EINVAL; + + omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr); + + /* REVISIT: Could the STB bit of I2C_CON be used with probing? */ + dev->buf = msg->buf; + dev->buf_len = msg->len; + + omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len); + + /* Clear the FIFO Buffers */ + w = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG); + w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR; + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w); + + init_completion(&dev->cmd_complete); + dev->cmd_err = 0; + + w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT; + + /* High speed configuration */ + if (dev->speed > 400) + w |= OMAP_I2C_CON_OPMODE_HS; + + if (msg->flags & I2C_M_TEN) + w |= OMAP_I2C_CON_XA; + if (!(msg->flags & I2C_M_RD)) + w |= OMAP_I2C_CON_TRX; + + if (!dev->b_hw && stop) + w |= OMAP_I2C_CON_STP; + + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + + /* + * Don't write stt and stp together on some hardware. + */ + if (dev->b_hw && stop) { + unsigned long delay = jiffies + OMAP_I2C_TIMEOUT; + u16 con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); + while (con & OMAP_I2C_CON_STT) { + con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); + + /* Let the user know if i2c is in a bad state */ + if (time_after(jiffies, delay)) { + dev_err(dev->dev, "controller timed out " + "waiting for start condition to finish\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + w |= OMAP_I2C_CON_STP; + w &= ~OMAP_I2C_CON_STT; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + } + + /* + * REVISIT: We should abort the transfer on signals, but the bus goes + * into arbitration and we're currently unable to recover from it. + */ + r = wait_for_completion_timeout(&dev->cmd_complete, + OMAP_I2C_TIMEOUT); + dev->buf_len = 0; + if (r < 0) + return r; + if (r == 0) { + dev_err(dev->dev, "controller timed out\n"); + omap_i2c_init(dev); + return -ETIMEDOUT; + } + + if (likely(!dev->cmd_err)) + return 0; + + /* We have an error */ + if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR | + OMAP_I2C_STAT_XUDF)) { + omap_i2c_init(dev); + return -EIO; + } + + if (dev->cmd_err & OMAP_I2C_STAT_NACK) { + if (msg->flags & I2C_M_IGNORE_NAK) + return 0; + if (stop) { + w = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); + w |= OMAP_I2C_CON_STP; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + } + return -EREMOTEIO; + } + return -EIO; +} + + +/* + * Prepare controller for a transaction and call omap_i2c_xfer_msg + * to do the work during IRQ processing. + */ +static int +omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + int i; + int r; + + omap_i2c_unidle(dev); + + r = omap_i2c_wait_for_bb(dev); + if (r < 0) + goto out; + + if (dev->set_mpu_wkup_lat != NULL) + dev->set_mpu_wkup_lat(dev->dev, dev->latency); + + for (i = 0; i < num; i++) { + r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1))); + if (r != 0) + break; + } + + if (dev->set_mpu_wkup_lat != NULL) + dev->set_mpu_wkup_lat(dev->dev, -1); + + if (r == 0) + r = num; + + omap_i2c_wait_for_bb(dev); +out: + omap_i2c_idle(dev); + return r; +} + +static u32 +omap_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static inline void +omap_i2c_complete_cmd(struct omap_i2c_dev *dev, u16 err) +{ + dev->cmd_err |= err; + complete(&dev->cmd_complete); +} + +static inline void +omap_i2c_ack_stat(struct omap_i2c_dev *dev, u16 stat) +{ + omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); +} + +static inline void i2c_omap_errata_i207(struct omap_i2c_dev *dev, u16 stat) +{ + /* + * I2C Errata(Errata Nos. OMAP2: 1.67, OMAP3: 1.8) + * Not applicable for OMAP4. + * Under certain rare conditions, RDR could be set again + * when the bus is busy, then ignore the interrupt and + * clear the interrupt. + */ + if (stat & OMAP_I2C_STAT_RDR) { + /* Step 1: If RDR is set, clear it */ + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR); + + /* Step 2: */ + if (!(omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) + & OMAP_I2C_STAT_BB)) { + + /* Step 3: */ + if (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) + & OMAP_I2C_STAT_RDR) { + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR); + dev_dbg(dev->dev, "RDR when bus is busy.\n"); + } + + } + } +} + +/* rev1 devices are apparently only on some 15xx */ +#ifdef CONFIG_ARCH_OMAP15XX + +static irqreturn_t +omap_i2c_rev1_isr(int this_irq, void *dev_id) +{ + struct omap_i2c_dev *dev = dev_id; + u16 iv, w; + + if (dev->idle) + return IRQ_NONE; + + iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); + switch (iv) { + case 0x00: /* None */ + break; + case 0x01: /* Arbitration lost */ + dev_err(dev->dev, "Arbitration lost\n"); + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL); + break; + case 0x02: /* No acknowledgement */ + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_STP); + break; + case 0x03: /* Register access ready */ + omap_i2c_complete_cmd(dev, 0); + break; + case 0x04: /* Receive data ready */ + if (dev->buf_len) { + w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); + *dev->buf++ = w; + dev->buf_len--; + if (dev->buf_len) { + *dev->buf++ = w >> 8; + dev->buf_len--; + } + } else + dev_err(dev->dev, "RRDY IRQ while no data requested\n"); + break; + case 0x05: /* Transmit data ready */ + if (dev->buf_len) { + w = *dev->buf++; + dev->buf_len--; + if (dev->buf_len) { + w |= *dev->buf++ << 8; + dev->buf_len--; + } + omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); + } else + dev_err(dev->dev, "XRDY IRQ while no data to send\n"); + break; + default: + return IRQ_NONE; + } + + return IRQ_HANDLED; +} +#else +#define omap_i2c_rev1_isr NULL +#endif + +/* + * OMAP3430 Errata 1.153: When an XRDY/XDR is hit, wait for XUDF before writing + * data to DATA_REG. Otherwise some data bytes can be lost while transferring + * them from the memory to the I2C interface. + */ +static int errata_omap3_1p153(struct omap_i2c_dev *dev, u16 *stat, int *err) +{ + unsigned long timeout = 10000; + + while (--timeout && !(*stat & OMAP_I2C_STAT_XUDF)) { + if (*stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { + omap_i2c_ack_stat(dev, *stat & (OMAP_I2C_STAT_XRDY | + OMAP_I2C_STAT_XDR)); + *err |= OMAP_I2C_STAT_XUDF; + return -ETIMEDOUT; + } + + cpu_relax(); + *stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); + } + + if (!timeout) { + dev_err(dev->dev, "timeout waiting on XUDF bit\n"); + return 0; + } + + return 0; +} + +static irqreturn_t +omap_i2c_isr(int this_irq, void *dev_id) +{ + struct omap_i2c_dev *dev = dev_id; + u16 bits; + u16 stat, w; + int err, count = 0; + + if (dev->idle) + return IRQ_NONE; + + bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); + while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) { + dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat); + if (count++ == 100) { + dev_warn(dev->dev, "Too much work in one IRQ\n"); + break; + } + + err = 0; +complete: + /* + * Ack the stat in one go, but [R/X]DR and [R/X]RDY should be + * acked after the data operation is complete. + * Ref: TRM SWPU114Q Figure 18-31 + */ + omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat & + ~(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR | + OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); + + if (stat & OMAP_I2C_STAT_NACK) { + err |= OMAP_I2C_STAT_NACK; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, + OMAP_I2C_CON_STP); + } + if (stat & OMAP_I2C_STAT_AL) { + dev_err(dev->dev, "Arbitration lost\n"); + err |= OMAP_I2C_STAT_AL; + } + /* + * ProDB0017052: Clear ARDY bit twice + */ + if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | + OMAP_I2C_STAT_AL)) { + omap_i2c_ack_stat(dev, stat & + (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR | + OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR | + OMAP_I2C_STAT_ARDY)); + omap_i2c_complete_cmd(dev, err); + return IRQ_HANDLED; + } + if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) { + u8 num_bytes = 1; + + if (dev->errata & I2C_OMAP_ERRATA_I207) + i2c_omap_errata_i207(dev, stat); + + if (dev->fifo_size) { + if (stat & OMAP_I2C_STAT_RRDY) + num_bytes = dev->fifo_size; + else /* read RXSTAT on RDR interrupt */ + num_bytes = (omap_i2c_read_reg(dev, + OMAP_I2C_BUFSTAT_REG) + >> 8) & 0x3F; + } + while (num_bytes) { + num_bytes--; + w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); + if (dev->buf_len) { + *dev->buf++ = w; + dev->buf_len--; + /* + * Data reg in 2430, omap3 and + * omap4 is 8 bit wide + */ + if (cpu_class_is_omap1() || + cpu_is_omap2420()) { + if (dev->buf_len) { + *dev->buf++ = w >> 8; + dev->buf_len--; + } + } + } else { + if (stat & OMAP_I2C_STAT_RRDY) + dev_err(dev->dev, + "RRDY IRQ while no data" + " requested\n"); + if (stat & OMAP_I2C_STAT_RDR) + dev_err(dev->dev, + "RDR IRQ while no data" + " requested\n"); + break; + } + } + omap_i2c_ack_stat(dev, + stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)); + continue; + } + if (stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)) { + u8 num_bytes = 1; + if (dev->fifo_size) { + if (stat & OMAP_I2C_STAT_XRDY) + num_bytes = dev->fifo_size; + else /* read TXSTAT on XDR interrupt */ + num_bytes = omap_i2c_read_reg(dev, + OMAP_I2C_BUFSTAT_REG) + & 0x3F; + } + while (num_bytes) { + num_bytes--; + w = 0; + if (dev->buf_len) { + w = *dev->buf++; + dev->buf_len--; + /* + * Data reg in 2430, omap3 and + * omap4 is 8 bit wide + */ + if (cpu_class_is_omap1() || + cpu_is_omap2420()) { + if (dev->buf_len) { + w |= *dev->buf++ << 8; + dev->buf_len--; + } + } + } else { + if (stat & OMAP_I2C_STAT_XRDY) + dev_err(dev->dev, + "XRDY IRQ while no " + "data to send\n"); + if (stat & OMAP_I2C_STAT_XDR) + dev_err(dev->dev, + "XDR IRQ while no " + "data to send\n"); + break; + } + + if ((dev->errata & I2C_OMAP3_1P153) && + errata_omap3_1p153(dev, &stat, &err)) + goto complete; + + omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); + } + omap_i2c_ack_stat(dev, + stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); + continue; + } + if (stat & OMAP_I2C_STAT_ROVR) { + dev_err(dev->dev, "Receive overrun\n"); + dev->cmd_err |= OMAP_I2C_STAT_ROVR; + } + if (stat & OMAP_I2C_STAT_XUDF) { + dev_err(dev->dev, "Transmit underflow\n"); + dev->cmd_err |= OMAP_I2C_STAT_XUDF; + } + } + + return count ? IRQ_HANDLED : IRQ_NONE; +} + +static const struct i2c_algorithm omap_i2c_algo = { + .master_xfer = omap_i2c_xfer, + .functionality = omap_i2c_func, +}; + +static int __devinit +omap_i2c_probe(struct platform_device *pdev) +{ + struct omap_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *mem, *irq, *ioarea; + struct omap_i2c_bus_platform_data *pdata = pdev->dev.platform_data; + irq_handler_t isr; + int r; + u32 speed = 0; + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + ioarea = request_mem_region(mem->start, resource_size(mem), + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto err_release_region; + } + + if (pdata != NULL) { + speed = pdata->clkrate; + dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat; + } else { + speed = 100; /* Default speed */ + dev->set_mpu_wkup_lat = NULL; + } + + dev->speed = speed; + dev->idle = 1; + dev->dev = &pdev->dev; + dev->irq = irq->start; + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + r = -ENOMEM; + goto err_free_mem; + } + + platform_set_drvdata(pdev, dev); + + if (cpu_is_omap7xx()) + dev->reg_shift = 1; + else if (cpu_is_omap44xx()) + dev->reg_shift = 0; + else + dev->reg_shift = 2; + + if (cpu_is_omap44xx()) + dev->regs = (u8 *) omap4_reg_map; + else + dev->regs = (u8 *) reg_map; + + pm_runtime_enable(&pdev->dev); + omap_i2c_unidle(dev); + + dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff; + + if (dev->rev <= OMAP_I2C_REV_ON_3430) + dev->errata |= I2C_OMAP3_1P153; + + if (!(cpu_class_is_omap1() || cpu_is_omap2420())) { + u16 s; + + /* Set up the fifo size - Get total size */ + s = (omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3; + dev->fifo_size = 0x8 << s; + + /* + * Set up notification threshold as half the total available + * size. This is to ensure that we can handle the status on int + * call back latencies. + */ + if (dev->rev >= OMAP_I2C_REV_ON_4430) { + dev->fifo_size = 0; + dev->b_hw = 0; /* Disable hardware fixes */ + } else { + dev->fifo_size = (dev->fifo_size / 2); + dev->b_hw = 1; /* Enable hardware fixes */ + } + /* calculate wakeup latency constraint for MPU */ + if (dev->set_mpu_wkup_lat != NULL) + dev->latency = (1000000 * dev->fifo_size) / + (1000 * speed / 8); + } + + /* reset ASAP, clearing any IRQs */ + omap_i2c_init(dev); + + isr = (dev->rev < OMAP_I2C_REV_2) ? omap_i2c_rev1_isr : omap_i2c_isr; + r = request_irq(dev->irq, isr, 0, pdev->name, dev); + + if (r) { + dev_err(dev->dev, "failure requesting irq %i\n", dev->irq); + goto err_unuse_clocks; + } + + dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n", + pdev->id, dev->rev >> 4, dev->rev & 0xf, dev->speed); + + omap_i2c_idle(dev); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); + adap->algo = &omap_i2c_algo; + adap->dev.parent = &pdev->dev; + + /* i2c device drivers may be active on return from add_adapter() */ + adap->nr = pdev->id; + r = i2c_add_numbered_adapter(adap); + if (r) { + dev_err(dev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(dev->irq, dev); +err_unuse_clocks: + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + omap_i2c_idle(dev); + iounmap(dev->base); +err_free_mem: + platform_set_drvdata(pdev, NULL); + kfree(dev); +err_release_region: + release_mem_region(mem->start, resource_size(mem)); + + return r; +} + +static int +omap_i2c_remove(struct platform_device *pdev) +{ + struct omap_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + platform_set_drvdata(pdev, NULL); + + free_irq(dev->irq, dev); + i2c_del_adapter(&dev->adapter); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + iounmap(dev->base); + kfree(dev); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + return 0; +} + +#ifdef CONFIG_SUSPEND +static int omap_i2c_suspend(struct device *dev) +{ + if (!pm_runtime_suspended(dev)) + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) + dev->bus->pm->runtime_suspend(dev); + + return 0; +} + +static int omap_i2c_resume(struct device *dev) +{ + if (!pm_runtime_suspended(dev)) + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) + dev->bus->pm->runtime_resume(dev); + + return 0; +} + +static struct dev_pm_ops omap_i2c_pm_ops = { + .suspend = omap_i2c_suspend, + .resume = omap_i2c_resume, +}; +#define OMAP_I2C_PM_OPS (&omap_i2c_pm_ops) +#else +#define OMAP_I2C_PM_OPS NULL +#endif + +static struct platform_driver omap_i2c_driver = { + .probe = omap_i2c_probe, + .remove = omap_i2c_remove, + .driver = { + .name = "omap_i2c", + .owner = THIS_MODULE, + .pm = OMAP_I2C_PM_OPS, + }, +}; + +/* I2C may be needed to bring up other drivers */ +static int __init +omap_i2c_init_driver(void) +{ + return platform_driver_register(&omap_i2c_driver); +} +subsys_initcall(omap_i2c_init_driver); + +static void __exit omap_i2c_exit_driver(void) +{ + platform_driver_unregister(&omap_i2c_driver); +} +module_exit(omap_i2c_exit_driver); + +MODULE_AUTHOR("MontaVista Software, Inc. (and others)"); +MODULE_DESCRIPTION("TI OMAP I2C bus adapter"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap_i2c"); diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c new file mode 100644 index 00000000..4b95f7a6 --- /dev/null +++ b/drivers/i2c/busses/i2c-parport-light.c @@ -0,0 +1,281 @@ +/* ------------------------------------------------------------------------ * + * i2c-parport-light.c I2C bus over parallel port * + * ------------------------------------------------------------------------ * + Copyright (C) 2003-2010 Jean Delvare <khali@linux-fr.org> + + Based on older i2c-velleman.c driver + Copyright (C) 1995-2000 Simon G. Vogl + With some changes from: + Frodo Looijaard <frodol@dds.nl> + Kyösti Mälkki <kmalkki@cc.hut.fi> + + 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/delay.h> +#include <linux/platform_device.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/i2c-smbus.h> +#include <linux/io.h> +#include "i2c-parport.h" + +#define DEFAULT_BASE 0x378 +#define DRVNAME "i2c-parport-light" + +static struct platform_device *pdev; + +static u16 base; +module_param(base, ushort, 0); +MODULE_PARM_DESC(base, "Base I/O address"); + +static int irq; +module_param(irq, int, 0); +MODULE_PARM_DESC(irq, "IRQ (optional)"); + +/* ----- Low-level parallel port access ----------------------------------- */ + +static inline void port_write(unsigned char p, unsigned char d) +{ + outb(d, base+p); +} + +static inline unsigned char port_read(unsigned char p) +{ + return inb(base+p); +} + +/* ----- Unified line operation functions --------------------------------- */ + +static inline void line_set(int state, const struct lineop *op) +{ + u8 oldval = port_read(op->port); + + /* Touch only the bit(s) needed */ + if ((op->inverted && !state) || (!op->inverted && state)) + port_write(op->port, oldval | op->val); + else + port_write(op->port, oldval & ~op->val); +} + +static inline int line_get(const struct lineop *op) +{ + u8 oldval = port_read(op->port); + + return ((op->inverted && (oldval & op->val) != op->val) + || (!op->inverted && (oldval & op->val) == op->val)); +} + +/* ----- I2C algorithm call-back functions and structures ----------------- */ + +static void parport_setscl(void *data, int state) +{ + line_set(state, &adapter_parm[type].setscl); +} + +static void parport_setsda(void *data, int state) +{ + line_set(state, &adapter_parm[type].setsda); +} + +static int parport_getscl(void *data) +{ + return line_get(&adapter_parm[type].getscl); +} + +static int parport_getsda(void *data) +{ + return line_get(&adapter_parm[type].getsda); +} + +/* Encapsulate the functions above in the correct structure + Note that getscl will be set to NULL by the attaching code for adapters + that cannot read SCL back */ +static struct i2c_algo_bit_data parport_algo_data = { + .setsda = parport_setsda, + .setscl = parport_setscl, + .getsda = parport_getsda, + .getscl = parport_getscl, + .udelay = 50, + .timeout = HZ, +}; + +/* ----- Driver registration ---------------------------------------------- */ + +static struct i2c_adapter parport_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON, + .algo_data = &parport_algo_data, + .name = "Parallel port adapter (light)", +}; + +/* SMBus alert support */ +static struct i2c_smbus_alert_setup alert_data = { + .alert_edge_triggered = 1, +}; +static struct i2c_client *ara; +static struct lineop parport_ctrl_irq = { + .val = (1 << 4), + .port = PORT_CTRL, +}; + +static int __devinit i2c_parport_probe(struct platform_device *pdev) +{ + int err; + + /* Reset hardware to a sane state (SCL and SDA high) */ + parport_setsda(NULL, 1); + parport_setscl(NULL, 1); + /* Other init if needed (power on...) */ + if (adapter_parm[type].init.val) { + line_set(1, &adapter_parm[type].init); + /* Give powered devices some time to settle */ + msleep(100); + } + + parport_adapter.dev.parent = &pdev->dev; + err = i2c_bit_add_bus(&parport_adapter); + if (err) { + dev_err(&pdev->dev, "Unable to register with I2C\n"); + return err; + } + + /* Setup SMBus alert if supported */ + if (adapter_parm[type].smbus_alert && irq) { + alert_data.irq = irq; + ara = i2c_setup_smbus_alert(&parport_adapter, &alert_data); + if (ara) + line_set(1, &parport_ctrl_irq); + else + dev_warn(&pdev->dev, "Failed to register ARA client\n"); + } + + return 0; +} + +static int __devexit i2c_parport_remove(struct platform_device *pdev) +{ + if (ara) { + line_set(0, &parport_ctrl_irq); + i2c_unregister_device(ara); + ara = NULL; + } + i2c_del_adapter(&parport_adapter); + + /* Un-init if needed (power off...) */ + if (adapter_parm[type].init.val) + line_set(0, &adapter_parm[type].init); + + return 0; +} + +static struct platform_driver i2c_parport_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = i2c_parport_probe, + .remove = __devexit_p(i2c_parport_remove), +}; + +static int __init i2c_parport_device_add(u16 address) +{ + int err; + + pdev = platform_device_alloc(DRVNAME, -1); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + goto exit; + } + + err = platform_device_add(pdev); + if (err) { + printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", + err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(pdev); +exit: + return err; +} + +static int __init i2c_parport_init(void) +{ + int err; + + if (type < 0) { + printk(KERN_ERR DRVNAME ": adapter type unspecified\n"); + return -ENODEV; + } + + if (type >= ARRAY_SIZE(adapter_parm)) { + printk(KERN_ERR DRVNAME ": invalid type (%d)\n", type); + return -ENODEV; + } + + if (base == 0) { + pr_info(DRVNAME ": using default base 0x%x\n", DEFAULT_BASE); + base = DEFAULT_BASE; + } + + if (!request_region(base, 3, DRVNAME)) + return -EBUSY; + + if (irq != 0) + pr_info(DRVNAME ": using irq %d\n", irq); + + if (!adapter_parm[type].getscl.val) + parport_algo_data.getscl = NULL; + + /* Sets global pdev as a side effect */ + err = i2c_parport_device_add(base); + if (err) + goto exit_release; + + err = platform_driver_register(&i2c_parport_driver); + if (err) + goto exit_device; + + return 0; + +exit_device: + platform_device_unregister(pdev); +exit_release: + release_region(base, 3); + return err; +} + +static void __exit i2c_parport_exit(void) +{ + platform_driver_unregister(&i2c_parport_driver); + platform_device_unregister(pdev); + release_region(base, 3); +} + +MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("I2C bus over parallel port (light)"); +MODULE_LICENSE("GPL"); + +module_init(i2c_parport_init); +module_exit(i2c_parport_exit); diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c new file mode 100644 index 00000000..24565687 --- /dev/null +++ b/drivers/i2c/busses/i2c-parport.c @@ -0,0 +1,306 @@ +/* ------------------------------------------------------------------------ * + * i2c-parport.c I2C bus over parallel port * + * ------------------------------------------------------------------------ * + Copyright (C) 2003-2011 Jean Delvare <khali@linux-fr.org> + + Based on older i2c-philips-par.c driver + Copyright (C) 1995-2000 Simon G. Vogl + With some changes from: + Frodo Looijaard <frodol@dds.nl> + Kyösti Mälkki <kmalkki@cc.hut.fi> + + 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/delay.h> +#include <linux/parport.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/i2c-smbus.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include "i2c-parport.h" + +/* ----- Device list ------------------------------------------------------ */ + +struct i2c_par { + struct pardevice *pdev; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo_data; + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; + struct list_head node; +}; + +static LIST_HEAD(adapter_list); +static DEFINE_MUTEX(adapter_list_lock); + +/* ----- Low-level parallel port access ----------------------------------- */ + +static void port_write_data(struct parport *p, unsigned char d) +{ + parport_write_data(p, d); +} + +static void port_write_control(struct parport *p, unsigned char d) +{ + parport_write_control(p, d); +} + +static unsigned char port_read_data(struct parport *p) +{ + return parport_read_data(p); +} + +static unsigned char port_read_status(struct parport *p) +{ + return parport_read_status(p); +} + +static unsigned char port_read_control(struct parport *p) +{ + return parport_read_control(p); +} + +static void (* const port_write[])(struct parport *, unsigned char) = { + port_write_data, + NULL, + port_write_control, +}; + +static unsigned char (* const port_read[])(struct parport *) = { + port_read_data, + port_read_status, + port_read_control, +}; + +/* ----- Unified line operation functions --------------------------------- */ + +static inline void line_set(struct parport *data, int state, + const struct lineop *op) +{ + u8 oldval = port_read[op->port](data); + + /* Touch only the bit(s) needed */ + if ((op->inverted && !state) || (!op->inverted && state)) + port_write[op->port](data, oldval | op->val); + else + port_write[op->port](data, oldval & ~op->val); +} + +static inline int line_get(struct parport *data, + const struct lineop *op) +{ + u8 oldval = port_read[op->port](data); + + return ((op->inverted && (oldval & op->val) != op->val) + || (!op->inverted && (oldval & op->val) == op->val)); +} + +/* ----- I2C algorithm call-back functions and structures ----------------- */ + +static void parport_setscl(void *data, int state) +{ + line_set((struct parport *) data, state, &adapter_parm[type].setscl); +} + +static void parport_setsda(void *data, int state) +{ + line_set((struct parport *) data, state, &adapter_parm[type].setsda); +} + +static int parport_getscl(void *data) +{ + return line_get((struct parport *) data, &adapter_parm[type].getscl); +} + +static int parport_getsda(void *data) +{ + return line_get((struct parport *) data, &adapter_parm[type].getsda); +} + +/* Encapsulate the functions above in the correct structure. + Note that this is only a template, from which the real structures are + copied. The attaching code will set getscl to NULL for adapters that + cannot read SCL back, and will also make the data field point to + the parallel port structure. */ +static const struct i2c_algo_bit_data parport_algo_data = { + .setsda = parport_setsda, + .setscl = parport_setscl, + .getsda = parport_getsda, + .getscl = parport_getscl, + .udelay = 10, /* ~50 kbps */ + .timeout = HZ, +}; + +/* ----- I2c and parallel port call-back functions and structures --------- */ + +void i2c_parport_irq(void *data) +{ + struct i2c_par *adapter = data; + struct i2c_client *ara = adapter->ara; + + if (ara) { + dev_dbg(&ara->dev, "SMBus alert received\n"); + i2c_handle_smbus_alert(ara); + } else + dev_dbg(&adapter->adapter.dev, + "SMBus alert received but no ARA client!\n"); +} + +static void i2c_parport_attach(struct parport *port) +{ + struct i2c_par *adapter; + + adapter = kzalloc(sizeof(struct i2c_par), GFP_KERNEL); + if (adapter == NULL) { + printk(KERN_ERR "i2c-parport: Failed to kzalloc\n"); + return; + } + + pr_debug("i2c-parport: attaching to %s\n", port->name); + parport_disable_irq(port); + adapter->pdev = parport_register_device(port, "i2c-parport", + NULL, NULL, i2c_parport_irq, PARPORT_FLAG_EXCL, adapter); + if (!adapter->pdev) { + printk(KERN_ERR "i2c-parport: Unable to register with parport\n"); + goto err_free; + } + + /* Fill the rest of the structure */ + adapter->adapter.owner = THIS_MODULE; + adapter->adapter.class = I2C_CLASS_HWMON; + strlcpy(adapter->adapter.name, "Parallel port adapter", + sizeof(adapter->adapter.name)); + adapter->algo_data = parport_algo_data; + /* Slow down if we can't sense SCL */ + if (!adapter_parm[type].getscl.val) { + adapter->algo_data.getscl = NULL; + adapter->algo_data.udelay = 50; /* ~10 kbps */ + } + adapter->algo_data.data = port; + adapter->adapter.algo_data = &adapter->algo_data; + adapter->adapter.dev.parent = port->physport->dev; + + if (parport_claim_or_block(adapter->pdev) < 0) { + printk(KERN_ERR "i2c-parport: Could not claim parallel port\n"); + goto err_unregister; + } + + /* Reset hardware to a sane state (SCL and SDA high) */ + parport_setsda(port, 1); + parport_setscl(port, 1); + /* Other init if needed (power on...) */ + if (adapter_parm[type].init.val) { + line_set(port, 1, &adapter_parm[type].init); + /* Give powered devices some time to settle */ + msleep(100); + } + + if (i2c_bit_add_bus(&adapter->adapter) < 0) { + printk(KERN_ERR "i2c-parport: Unable to register with I2C\n"); + goto err_unregister; + } + + /* Setup SMBus alert if supported */ + if (adapter_parm[type].smbus_alert) { + adapter->alert_data.alert_edge_triggered = 1; + adapter->ara = i2c_setup_smbus_alert(&adapter->adapter, + &adapter->alert_data); + if (adapter->ara) + parport_enable_irq(port); + else + printk(KERN_WARNING "i2c-parport: Failed to register " + "ARA client\n"); + } + + /* Add the new adapter to the list */ + mutex_lock(&adapter_list_lock); + list_add_tail(&adapter->node, &adapter_list); + mutex_unlock(&adapter_list_lock); + return; + + err_unregister: + parport_release(adapter->pdev); + parport_unregister_device(adapter->pdev); + err_free: + kfree(adapter); +} + +static void i2c_parport_detach(struct parport *port) +{ + struct i2c_par *adapter, *_n; + + /* Walk the list */ + mutex_lock(&adapter_list_lock); + list_for_each_entry_safe(adapter, _n, &adapter_list, node) { + if (adapter->pdev->port == port) { + if (adapter->ara) { + parport_disable_irq(port); + i2c_unregister_device(adapter->ara); + } + i2c_del_adapter(&adapter->adapter); + + /* Un-init if needed (power off...) */ + if (adapter_parm[type].init.val) + line_set(port, 0, &adapter_parm[type].init); + + parport_release(adapter->pdev); + parport_unregister_device(adapter->pdev); + list_del(&adapter->node); + kfree(adapter); + } + } + mutex_unlock(&adapter_list_lock); +} + +static struct parport_driver i2c_parport_driver = { + .name = "i2c-parport", + .attach = i2c_parport_attach, + .detach = i2c_parport_detach, +}; + +/* ----- Module loading, unloading and information ------------------------ */ + +static int __init i2c_parport_init(void) +{ + if (type < 0) { + printk(KERN_WARNING "i2c-parport: adapter type unspecified\n"); + return -ENODEV; + } + + if (type >= ARRAY_SIZE(adapter_parm)) { + printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type); + return -ENODEV; + } + + return parport_register_driver(&i2c_parport_driver); +} + +static void __exit i2c_parport_exit(void) +{ + parport_unregister_driver(&i2c_parport_driver); +} + +MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("I2C bus over parallel port"); +MODULE_LICENSE("GPL"); + +module_init(i2c_parport_init); +module_exit(i2c_parport_exit); diff --git a/drivers/i2c/busses/i2c-parport.h b/drivers/i2c/busses/i2c-parport.h new file mode 100644 index 00000000..3fe65230 --- /dev/null +++ b/drivers/i2c/busses/i2c-parport.h @@ -0,0 +1,110 @@ +/* ------------------------------------------------------------------------ * + * i2c-parport.h I2C bus over parallel port * + * ------------------------------------------------------------------------ * + Copyright (C) 2003-2010 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. + * ------------------------------------------------------------------------ */ + +#define PORT_DATA 0 +#define PORT_STAT 1 +#define PORT_CTRL 2 + +struct lineop { + u8 val; + u8 port; + u8 inverted; +}; + +struct adapter_parm { + struct lineop setsda; + struct lineop setscl; + struct lineop getsda; + struct lineop getscl; + struct lineop init; + unsigned int smbus_alert:1; +}; + +static const struct adapter_parm adapter_parm[] = { + /* type 0: Philips adapter */ + { + .setsda = { 0x80, PORT_DATA, 1 }, + .setscl = { 0x08, PORT_CTRL, 0 }, + .getsda = { 0x80, PORT_STAT, 0 }, + .getscl = { 0x08, PORT_STAT, 0 }, + }, + /* type 1: home brew teletext adapter */ + { + .setsda = { 0x02, PORT_DATA, 0 }, + .setscl = { 0x01, PORT_DATA, 0 }, + .getsda = { 0x80, PORT_STAT, 1 }, + }, + /* type 2: Velleman K8000 adapter */ + { + .setsda = { 0x02, PORT_CTRL, 1 }, + .setscl = { 0x08, PORT_CTRL, 1 }, + .getsda = { 0x10, PORT_STAT, 0 }, + }, + /* type 3: ELV adapter */ + { + .setsda = { 0x02, PORT_DATA, 1 }, + .setscl = { 0x01, PORT_DATA, 1 }, + .getsda = { 0x40, PORT_STAT, 1 }, + .getscl = { 0x08, PORT_STAT, 1 }, + }, + /* type 4: ADM1032 evaluation board */ + { + .setsda = { 0x02, PORT_DATA, 1 }, + .setscl = { 0x01, PORT_DATA, 1 }, + .getsda = { 0x10, PORT_STAT, 1 }, + .init = { 0xf0, PORT_DATA, 0 }, + .smbus_alert = 1, + }, + /* type 5: ADM1025, ADM1030 and ADM1031 evaluation boards */ + { + .setsda = { 0x02, PORT_DATA, 1 }, + .setscl = { 0x01, PORT_DATA, 1 }, + .getsda = { 0x10, PORT_STAT, 1 }, + }, + /* type 6: Barco LPT->DVI (K5800236) adapter */ + { + .setsda = { 0x02, PORT_DATA, 1 }, + .setscl = { 0x01, PORT_DATA, 1 }, + .getsda = { 0x20, PORT_STAT, 0 }, + .getscl = { 0x40, PORT_STAT, 0 }, + .init = { 0xfc, PORT_DATA, 0 }, + }, + /* type 7: One For All JP1 parallel port adapter */ + { + .setsda = { 0x01, PORT_DATA, 0 }, + .setscl = { 0x02, PORT_DATA, 0 }, + .getsda = { 0x80, PORT_STAT, 1 }, + .init = { 0x04, PORT_DATA, 1 }, + }, +}; + +static int type = -1; +module_param(type, int, 0); +MODULE_PARM_DESC(type, + "Type of adapter:\n" + " 0 = Philips adapter\n" + " 1 = home brew teletext adapter\n" + " 2 = Velleman K8000 adapter\n" + " 3 = ELV adapter\n" + " 4 = ADM1032 evaluation board\n" + " 5 = ADM1025, ADM1030 and ADM1031 evaluation boards\n" + " 6 = Barco LPT->DVI (K5800236) adapter\n" + " 7 = One For All JP1 parallel port adapter\n" +); diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c new file mode 100644 index 00000000..837b8c1a --- /dev/null +++ b/drivers/i2c/busses/i2c-pasemi.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * SMBus host driver for PA Semi PWRficient + * + * 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. + * + * 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/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/sched.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/io.h> + +static struct pci_driver pasemi_smb_driver; + +struct pasemi_smbus { + struct pci_dev *dev; + struct i2c_adapter adapter; + unsigned long base; + int size; +}; + +/* Register offsets */ +#define REG_MTXFIFO 0x00 +#define REG_MRXFIFO 0x04 +#define REG_SMSTA 0x14 +#define REG_CTL 0x1c + +/* Register defs */ +#define MTXFIFO_READ 0x00000400 +#define MTXFIFO_STOP 0x00000200 +#define MTXFIFO_START 0x00000100 +#define MTXFIFO_DATA_M 0x000000ff + +#define MRXFIFO_EMPTY 0x00000100 +#define MRXFIFO_DATA_M 0x000000ff + +#define SMSTA_XEN 0x08000000 +#define SMSTA_MTN 0x00200000 + +#define CTL_MRR 0x00000400 +#define CTL_MTR 0x00000200 +#define CTL_CLK_M 0x000000ff + +#define CLK_100K_DIV 84 +#define CLK_400K_DIV 21 + +static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val) +{ + dev_dbg(&smbus->dev->dev, "smbus write reg %lx val %08x\n", + smbus->base + reg, val); + outl(val, smbus->base + reg); +} + +static inline int reg_read(struct pasemi_smbus *smbus, int reg) +{ + int ret; + ret = inl(smbus->base + reg); + dev_dbg(&smbus->dev->dev, "smbus read reg %lx val %08x\n", + smbus->base + reg, ret); + return ret; +} + +#define TXFIFO_WR(smbus, reg) reg_write((smbus), REG_MTXFIFO, (reg)) +#define RXFIFO_RD(smbus) reg_read((smbus), REG_MRXFIFO) + +static void pasemi_smb_clear(struct pasemi_smbus *smbus) +{ + unsigned int status; + + status = reg_read(smbus, REG_SMSTA); + reg_write(smbus, REG_SMSTA, status); +} + +static int pasemi_smb_waitready(struct pasemi_smbus *smbus) +{ + int timeout = 10; + unsigned int status; + + status = reg_read(smbus, REG_SMSTA); + + while (!(status & SMSTA_XEN) && timeout--) { + msleep(1); + status = reg_read(smbus, REG_SMSTA); + } + + /* Got NACK? */ + if (status & SMSTA_MTN) + return -ENXIO; + + if (timeout < 0) { + dev_warn(&smbus->dev->dev, "Timeout, status 0x%08x\n", status); + reg_write(smbus, REG_SMSTA, status); + return -ETIME; + } + + /* Clear XEN */ + reg_write(smbus, REG_SMSTA, SMSTA_XEN); + + return 0; +} + +static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter, + struct i2c_msg *msg, int stop) +{ + struct pasemi_smbus *smbus = adapter->algo_data; + int read, i, err; + u32 rd; + + read = msg->flags & I2C_M_RD ? 1 : 0; + + TXFIFO_WR(smbus, MTXFIFO_START | (msg->addr << 1) | read); + + if (read) { + TXFIFO_WR(smbus, msg->len | MTXFIFO_READ | + (stop ? MTXFIFO_STOP : 0)); + + err = pasemi_smb_waitready(smbus); + if (err) + goto reset_out; + + for (i = 0; i < msg->len; i++) { + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + msg->buf[i] = rd & MRXFIFO_DATA_M; + } + } else { + for (i = 0; i < msg->len - 1; i++) + TXFIFO_WR(smbus, msg->buf[i]); + + TXFIFO_WR(smbus, msg->buf[msg->len-1] | + (stop ? MTXFIFO_STOP : 0)); + } + + return 0; + + reset_out: + reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | + (CLK_100K_DIV & CTL_CLK_M))); + return err; +} + +static int pasemi_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct pasemi_smbus *smbus = adapter->algo_data; + int ret, i; + + pasemi_smb_clear(smbus); + + ret = 0; + + for (i = 0; i < num && !ret; i++) + ret = pasemi_i2c_xfer_msg(adapter, &msgs[i], (i == (num - 1))); + + return ret ? ret : num; +} + +static int pasemi_smb_xfer(struct i2c_adapter *adapter, + u16 addr, unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct pasemi_smbus *smbus = adapter->algo_data; + unsigned int rd; + int read_flag, err; + int len = 0, i; + + /* All our ops take 8-bit shifted addresses */ + addr <<= 1; + read_flag = read_write == I2C_SMBUS_READ; + + pasemi_smb_clear(smbus); + + switch (size) { + case I2C_SMBUS_QUICK: + TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START | + MTXFIFO_STOP); + break; + case I2C_SMBUS_BYTE: + TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START); + if (read_write) + TXFIFO_WR(smbus, 1 | MTXFIFO_STOP | MTXFIFO_READ); + else + TXFIFO_WR(smbus, MTXFIFO_STOP | command); + break; + case I2C_SMBUS_BYTE_DATA: + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + if (read_write) { + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); + TXFIFO_WR(smbus, 1 | MTXFIFO_READ | MTXFIFO_STOP); + } else { + TXFIFO_WR(smbus, MTXFIFO_STOP | data->byte); + } + break; + case I2C_SMBUS_WORD_DATA: + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + if (read_write) { + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); + TXFIFO_WR(smbus, 2 | MTXFIFO_READ | MTXFIFO_STOP); + } else { + TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M); + TXFIFO_WR(smbus, MTXFIFO_STOP | (data->word >> 8)); + } + break; + case I2C_SMBUS_BLOCK_DATA: + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + if (read_write) { + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); + TXFIFO_WR(smbus, 1 | MTXFIFO_READ); + rd = RXFIFO_RD(smbus); + len = min_t(u8, (rd & MRXFIFO_DATA_M), + I2C_SMBUS_BLOCK_MAX); + TXFIFO_WR(smbus, len | MTXFIFO_READ | + MTXFIFO_STOP); + } else { + len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX); + TXFIFO_WR(smbus, len); + for (i = 1; i < len; i++) + TXFIFO_WR(smbus, data->block[i]); + TXFIFO_WR(smbus, data->block[len] | MTXFIFO_STOP); + } + break; + case I2C_SMBUS_PROC_CALL: + read_write = I2C_SMBUS_READ; + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M); + TXFIFO_WR(smbus, (data->word >> 8) & MTXFIFO_DATA_M); + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START); + TXFIFO_WR(smbus, 2 | MTXFIFO_STOP | MTXFIFO_READ); + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX - 1); + read_write = I2C_SMBUS_READ; + TXFIFO_WR(smbus, addr | MTXFIFO_START); + TXFIFO_WR(smbus, command); + TXFIFO_WR(smbus, len); + for (i = 1; i <= len; i++) + TXFIFO_WR(smbus, data->block[i]); + TXFIFO_WR(smbus, addr | I2C_SMBUS_READ); + TXFIFO_WR(smbus, MTXFIFO_READ | 1); + rd = RXFIFO_RD(smbus); + len = min_t(u8, (rd & MRXFIFO_DATA_M), + I2C_SMBUS_BLOCK_MAX - len); + TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP); + break; + + default: + dev_warn(&adapter->dev, "Unsupported transaction %d\n", size); + return -EINVAL; + } + + err = pasemi_smb_waitready(smbus); + if (err) + goto reset_out; + + if (read_write == I2C_SMBUS_WRITE) + return 0; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + data->byte = rd & MRXFIFO_DATA_M; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + data->word = rd & MRXFIFO_DATA_M; + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + data->word |= (rd & MRXFIFO_DATA_M) << 8; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + data->block[0] = len; + for (i = 1; i <= len; i ++) { + rd = RXFIFO_RD(smbus); + if (rd & MRXFIFO_EMPTY) { + err = -ENODATA; + goto reset_out; + } + data->block[i] = rd & MRXFIFO_DATA_M; + } + break; + } + + return 0; + + reset_out: + reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | + (CLK_100K_DIV & CTL_CLK_M))); + return err; +} + +static u32 pasemi_smb_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_I2C; +} + +static const struct i2c_algorithm smbus_algorithm = { + .master_xfer = pasemi_i2c_xfer, + .smbus_xfer = pasemi_smb_xfer, + .functionality = pasemi_smb_func, +}; + +static int __devinit pasemi_smb_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct pasemi_smbus *smbus; + int error; + + if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO)) + return -ENODEV; + + smbus = kzalloc(sizeof(struct pasemi_smbus), GFP_KERNEL); + if (!smbus) + return -ENOMEM; + + smbus->dev = dev; + smbus->base = pci_resource_start(dev, 0); + smbus->size = pci_resource_len(dev, 0); + + if (!request_region(smbus->base, smbus->size, + pasemi_smb_driver.name)) { + error = -EBUSY; + goto out_kfree; + } + + smbus->adapter.owner = THIS_MODULE; + snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), + "PA Semi SMBus adapter at 0x%lx", smbus->base); + smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus->adapter.algo = &smbus_algorithm; + smbus->adapter.algo_data = smbus; + smbus->adapter.nr = PCI_FUNC(dev->devfn); + + /* set up the sysfs linkage to our parent device */ + smbus->adapter.dev.parent = &dev->dev; + + reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR | + (CLK_100K_DIV & CTL_CLK_M))); + + error = i2c_add_numbered_adapter(&smbus->adapter); + if (error) + goto out_release_region; + + pci_set_drvdata(dev, smbus); + + return 0; + + out_release_region: + release_region(smbus->base, smbus->size); + out_kfree: + kfree(smbus); + return error; +} + +static void __devexit pasemi_smb_remove(struct pci_dev *dev) +{ + struct pasemi_smbus *smbus = pci_get_drvdata(dev); + + i2c_del_adapter(&smbus->adapter); + release_region(smbus->base, smbus->size); + kfree(smbus); +} + +static const struct pci_device_id pasemi_smb_ids[] = { + { PCI_DEVICE(0x1959, 0xa003) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, pasemi_smb_ids); + +static struct pci_driver pasemi_smb_driver = { + .name = "i2c-pasemi", + .id_table = pasemi_smb_ids, + .probe = pasemi_smb_probe, + .remove = __devexit_p(pasemi_smb_remove), +}; + +static int __init pasemi_smb_init(void) +{ + return pci_register_driver(&pasemi_smb_driver); +} + +static void __exit pasemi_smb_exit(void) +{ + pci_unregister_driver(&pasemi_smb_driver); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>"); +MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver"); + +module_init(pasemi_smb_init); +module_exit(pasemi_smb_exit); diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c new file mode 100644 index 00000000..29933f87 --- /dev/null +++ b/drivers/i2c/busses/i2c-pca-isa.c @@ -0,0 +1,229 @@ +/* + * i2c-pca-isa.c driver for PCA9564 on ISA boards + * Copyright (C) 2004 Arcom Control Systems + * Copyright (C) 2008 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. + * + * 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/ioport.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/isa.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-pca.h> +#include <linux/io.h> + +#include <asm/irq.h> + +#define DRIVER "i2c-pca-isa" +#define IO_SIZE 4 + +static unsigned long base; +static int irq = -1; + +/* Data sheet recommends 59kHz for 100kHz operation due to variation + * in the actual clock rate */ +static int clock = 59000; + +static struct i2c_adapter pca_isa_ops; +static wait_queue_head_t pca_wait; + +static void pca_isa_writebyte(void *pd, int reg, int val) +{ +#ifdef DEBUG_IO + static char *names[] = { "T/O", "DAT", "ADR", "CON" }; + printk(KERN_DEBUG "*** write %s at %#lx <= %#04x\n", names[reg], + base+reg, val); +#endif + outb(val, base+reg); +} + +static int pca_isa_readbyte(void *pd, int reg) +{ + int res = inb(base+reg); +#ifdef DEBUG_IO + { + static char *names[] = { "STA", "DAT", "ADR", "CON" }; + printk(KERN_DEBUG "*** read %s => %#04x\n", names[reg], res); + } +#endif + return res; +} + +static int pca_isa_waitforcompletion(void *pd) +{ + unsigned long timeout; + long ret; + + if (irq > -1) { + ret = wait_event_timeout(pca_wait, + pca_isa_readbyte(pd, I2C_PCA_CON) + & I2C_PCA_CON_SI, pca_isa_ops.timeout); + } else { + /* Do polling */ + timeout = jiffies + pca_isa_ops.timeout; + do { + ret = time_before(jiffies, timeout); + if (pca_isa_readbyte(pd, I2C_PCA_CON) + & I2C_PCA_CON_SI) + break; + udelay(100); + } while (ret); + } + + return ret > 0; +} + +static void pca_isa_resetchip(void *pd) +{ + /* apparently only an external reset will do it. not a lot can be done */ + printk(KERN_WARNING DRIVER ": Haven't figured out how to do a reset yet\n"); +} + +static irqreturn_t pca_handler(int this_irq, void *dev_id) { + wake_up(&pca_wait); + return IRQ_HANDLED; +} + +static struct i2c_algo_pca_data pca_isa_data = { + /* .data intentionally left NULL, not needed with ISA */ + .write_byte = pca_isa_writebyte, + .read_byte = pca_isa_readbyte, + .wait_for_completion = pca_isa_waitforcompletion, + .reset_chip = pca_isa_resetchip, +}; + +static struct i2c_adapter pca_isa_ops = { + .owner = THIS_MODULE, + .algo_data = &pca_isa_data, + .name = "PCA9564/PCA9665 ISA Adapter", + .timeout = HZ, +}; + +static int __devinit pca_isa_match(struct device *dev, unsigned int id) +{ + int match = base != 0; + + if (match) { + if (irq <= -1) + dev_warn(dev, "Using polling mode (specify irq)\n"); + } else + dev_err(dev, "Please specify I/O base\n"); + + return match; +} + +static int __devinit pca_isa_probe(struct device *dev, unsigned int id) +{ + init_waitqueue_head(&pca_wait); + + dev_info(dev, "i/o base %#08lx. irq %d\n", base, irq); + +#ifdef CONFIG_PPC + if (check_legacy_ioport(base)) { + dev_err(dev, "I/O address %#08lx is not available\n", base); + goto out; + } +#endif + + if (!request_region(base, IO_SIZE, "i2c-pca-isa")) { + dev_err(dev, "I/O address %#08lx is in use\n", base); + goto out; + } + + if (irq > -1) { + if (request_irq(irq, pca_handler, 0, "i2c-pca-isa", &pca_isa_ops) < 0) { + dev_err(dev, "Request irq%d failed\n", irq); + goto out_region; + } + } + + pca_isa_data.i2c_clock = clock; + if (i2c_pca_add_bus(&pca_isa_ops) < 0) { + dev_err(dev, "Failed to add i2c bus\n"); + goto out_irq; + } + + return 0; + + out_irq: + if (irq > -1) + free_irq(irq, &pca_isa_ops); + out_region: + release_region(base, IO_SIZE); + out: + return -ENODEV; +} + +static int __devexit pca_isa_remove(struct device *dev, unsigned int id) +{ + i2c_del_adapter(&pca_isa_ops); + + if (irq > -1) { + disable_irq(irq); + free_irq(irq, &pca_isa_ops); + } + release_region(base, IO_SIZE); + + return 0; +} + +static struct isa_driver pca_isa_driver = { + .match = pca_isa_match, + .probe = pca_isa_probe, + .remove = __devexit_p(pca_isa_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRIVER, + } +}; + +static int __init pca_isa_init(void) +{ + return isa_register_driver(&pca_isa_driver, 1); +} + +static void __exit pca_isa_exit(void) +{ + isa_unregister_driver(&pca_isa_driver); +} + +MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>"); +MODULE_DESCRIPTION("ISA base PCA9564/PCA9665 driver"); +MODULE_LICENSE("GPL"); + +module_param(base, ulong, 0); +MODULE_PARM_DESC(base, "I/O base address"); + +module_param(irq, int, 0); +MODULE_PARM_DESC(irq, "IRQ"); +module_param(clock, int, 0); +MODULE_PARM_DESC(clock, "Clock rate in hertz.\n\t\t" + "For PCA9564: 330000,288000,217000,146000," + "88000,59000,44000,36000\n" + "\t\tFor PCA9665:\tStandard: 60300 - 100099\n" + "\t\t\t\tFast: 100100 - 400099\n" + "\t\t\t\tFast+: 400100 - 10000099\n" + "\t\t\t\tTurbo: Up to 1265800"); + +module_init(pca_isa_init); +module_exit(pca_isa_exit); diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c new file mode 100644 index 00000000..ace67995 --- /dev/null +++ b/drivers/i2c/busses/i2c-pca-platform.c @@ -0,0 +1,305 @@ +/* + * i2c_pca_platform.c + * + * Platform driver for the PCA9564 I2C controller. + * + * Copyright (C) 2008 Pengutronix + * + * 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. + + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/i2c-algo-pca.h> +#include <linux/i2c-pca-platform.h> +#include <linux/gpio.h> +#include <linux/io.h> + +#include <asm/irq.h> + +struct i2c_pca_pf_data { + void __iomem *reg_base; + int irq; /* if 0, use polling */ + int gpio; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_algo_pca_data algo_data; + unsigned long io_base; + unsigned long io_size; +}; + +/* Read/Write functions for different register alignments */ + +static int i2c_pca_pf_readbyte8(void *pd, int reg) +{ + struct i2c_pca_pf_data *i2c = pd; + return ioread8(i2c->reg_base + reg); +} + +static int i2c_pca_pf_readbyte16(void *pd, int reg) +{ + struct i2c_pca_pf_data *i2c = pd; + return ioread8(i2c->reg_base + reg * 2); +} + +static int i2c_pca_pf_readbyte32(void *pd, int reg) +{ + struct i2c_pca_pf_data *i2c = pd; + return ioread8(i2c->reg_base + reg * 4); +} + +static void i2c_pca_pf_writebyte8(void *pd, int reg, int val) +{ + struct i2c_pca_pf_data *i2c = pd; + iowrite8(val, i2c->reg_base + reg); +} + +static void i2c_pca_pf_writebyte16(void *pd, int reg, int val) +{ + struct i2c_pca_pf_data *i2c = pd; + iowrite8(val, i2c->reg_base + reg * 2); +} + +static void i2c_pca_pf_writebyte32(void *pd, int reg, int val) +{ + struct i2c_pca_pf_data *i2c = pd; + iowrite8(val, i2c->reg_base + reg * 4); +} + + +static int i2c_pca_pf_waitforcompletion(void *pd) +{ + struct i2c_pca_pf_data *i2c = pd; + unsigned long timeout; + long ret; + + if (i2c->irq) { + ret = wait_event_timeout(i2c->wait, + i2c->algo_data.read_byte(i2c, I2C_PCA_CON) + & I2C_PCA_CON_SI, i2c->adap.timeout); + } else { + /* Do polling */ + timeout = jiffies + i2c->adap.timeout; + do { + ret = time_before(jiffies, timeout); + if (i2c->algo_data.read_byte(i2c, I2C_PCA_CON) + & I2C_PCA_CON_SI) + break; + udelay(100); + } while (ret); + } + + return ret > 0; +} + +static void i2c_pca_pf_dummyreset(void *pd) +{ + struct i2c_pca_pf_data *i2c = pd; + printk(KERN_WARNING "%s: No reset-pin found. Chip may get stuck!\n", + i2c->adap.name); +} + +static void i2c_pca_pf_resetchip(void *pd) +{ + struct i2c_pca_pf_data *i2c = pd; + + gpio_set_value(i2c->gpio, 0); + ndelay(100); + gpio_set_value(i2c->gpio, 1); +} + +static irqreturn_t i2c_pca_pf_handler(int this_irq, void *dev_id) +{ + struct i2c_pca_pf_data *i2c = dev_id; + + if ((i2c->algo_data.read_byte(i2c, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0) + return IRQ_NONE; + + wake_up(&i2c->wait); + + return IRQ_HANDLED; +} + + +static int __devinit i2c_pca_pf_probe(struct platform_device *pdev) +{ + struct i2c_pca_pf_data *i2c; + struct resource *res; + struct i2c_pca9564_pf_platform_data *platform_data = + pdev->dev.platform_data; + int ret = 0; + int irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + /* If irq is 0, we do polling. */ + + if (res == NULL) { + ret = -ENODEV; + goto e_print; + } + + if (!request_mem_region(res->start, resource_size(res), res->name)) { + ret = -ENOMEM; + goto e_print; + } + + i2c = kzalloc(sizeof(struct i2c_pca_pf_data), GFP_KERNEL); + if (!i2c) { + ret = -ENOMEM; + goto e_alloc; + } + + init_waitqueue_head(&i2c->wait); + + i2c->reg_base = ioremap(res->start, resource_size(res)); + if (!i2c->reg_base) { + ret = -ENOMEM; + goto e_remap; + } + i2c->io_base = res->start; + i2c->io_size = resource_size(res); + i2c->irq = irq; + + i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0; + i2c->adap.owner = THIS_MODULE; + snprintf(i2c->adap.name, sizeof(i2c->adap.name), + "PCA9564/PCA9665 at 0x%08lx", + (unsigned long) res->start); + i2c->adap.algo_data = &i2c->algo_data; + i2c->adap.dev.parent = &pdev->dev; + + if (platform_data) { + i2c->adap.timeout = platform_data->timeout; + i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed; + i2c->gpio = platform_data->gpio; + } else { + i2c->adap.timeout = HZ; + i2c->algo_data.i2c_clock = 59000; + i2c->gpio = -1; + } + + i2c->algo_data.data = i2c; + i2c->algo_data.wait_for_completion = i2c_pca_pf_waitforcompletion; + i2c->algo_data.reset_chip = i2c_pca_pf_dummyreset; + + switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { + case IORESOURCE_MEM_32BIT: + i2c->algo_data.write_byte = i2c_pca_pf_writebyte32; + i2c->algo_data.read_byte = i2c_pca_pf_readbyte32; + break; + case IORESOURCE_MEM_16BIT: + i2c->algo_data.write_byte = i2c_pca_pf_writebyte16; + i2c->algo_data.read_byte = i2c_pca_pf_readbyte16; + break; + case IORESOURCE_MEM_8BIT: + default: + i2c->algo_data.write_byte = i2c_pca_pf_writebyte8; + i2c->algo_data.read_byte = i2c_pca_pf_readbyte8; + break; + } + + /* Use gpio_is_valid() when in mainline */ + if (i2c->gpio > -1) { + ret = gpio_request(i2c->gpio, i2c->adap.name); + if (ret == 0) { + gpio_direction_output(i2c->gpio, 1); + i2c->algo_data.reset_chip = i2c_pca_pf_resetchip; + } else { + printk(KERN_WARNING "%s: Registering gpio failed!\n", + i2c->adap.name); + i2c->gpio = ret; + } + } + + if (irq) { + ret = request_irq(irq, i2c_pca_pf_handler, + IRQF_TRIGGER_FALLING, pdev->name, i2c); + if (ret) + goto e_reqirq; + } + + if (i2c_pca_add_numbered_bus(&i2c->adap) < 0) { + ret = -ENODEV; + goto e_adapt; + } + + platform_set_drvdata(pdev, i2c); + + printk(KERN_INFO "%s registered.\n", i2c->adap.name); + + return 0; + +e_adapt: + if (irq) + free_irq(irq, i2c); +e_reqirq: + if (i2c->gpio > -1) + gpio_free(i2c->gpio); + + iounmap(i2c->reg_base); +e_remap: + kfree(i2c); +e_alloc: + release_mem_region(res->start, resource_size(res)); +e_print: + printk(KERN_ERR "Registering PCA9564/PCA9665 FAILED! (%d)\n", ret); + return ret; +} + +static int __devexit i2c_pca_pf_remove(struct platform_device *pdev) +{ + struct i2c_pca_pf_data *i2c = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + + i2c_del_adapter(&i2c->adap); + + if (i2c->irq) + free_irq(i2c->irq, i2c); + + if (i2c->gpio > -1) + gpio_free(i2c->gpio); + + iounmap(i2c->reg_base); + release_mem_region(i2c->io_base, i2c->io_size); + kfree(i2c); + + return 0; +} + +static struct platform_driver i2c_pca_pf_driver = { + .probe = i2c_pca_pf_probe, + .remove = __devexit_p(i2c_pca_pf_remove), + .driver = { + .name = "i2c-pca-platform", + .owner = THIS_MODULE, + }, +}; + +static int __init i2c_pca_pf_init(void) +{ + return platform_driver_register(&i2c_pca_pf_driver); +} + +static void __exit i2c_pca_pf_exit(void) +{ + platform_driver_unregister(&i2c_pca_pf_driver); +} + +MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>"); +MODULE_DESCRIPTION("I2C-PCA9564/PCA9665 platform driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_pca_pf_init); +module_exit(i2c_pca_pf_exit); + diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c new file mode 100644 index 00000000..6d14ac2e --- /dev/null +++ b/drivers/i2c/busses/i2c-piix4.c @@ -0,0 +1,563 @@ +/* + Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and + Philip Edelbrock <phil@netroedge.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. + + 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. +*/ + +/* + Supports: + Intel PIIX4, 440MX + Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 + ATI IXP200, IXP300, IXP400, SB600, SB700, SB800 + AMD Hudson-2 + SMSC Victory66 + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/dmi.h> +#include <linux/acpi.h> +#include <linux/io.h> + + +/* PIIX4 SMBus address offsets */ +#define SMBHSTSTS (0 + piix4_smba) +#define SMBHSLVSTS (1 + piix4_smba) +#define SMBHSTCNT (2 + piix4_smba) +#define SMBHSTCMD (3 + piix4_smba) +#define SMBHSTADD (4 + piix4_smba) +#define SMBHSTDAT0 (5 + piix4_smba) +#define SMBHSTDAT1 (6 + piix4_smba) +#define SMBBLKDAT (7 + piix4_smba) +#define SMBSLVCNT (8 + piix4_smba) +#define SMBSHDWCMD (9 + piix4_smba) +#define SMBSLVEVT (0xA + piix4_smba) +#define SMBSLVDAT (0xC + piix4_smba) + +/* count for request_region */ +#define SMBIOSIZE 8 + +/* PCI Address Constants */ +#define SMBBA 0x090 +#define SMBHSTCFG 0x0D2 +#define SMBSLVC 0x0D3 +#define SMBSHDW1 0x0D4 +#define SMBSHDW2 0x0D5 +#define SMBREV 0x0D6 + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* PIIX4 constants */ +#define PIIX4_QUICK 0x00 +#define PIIX4_BYTE 0x04 +#define PIIX4_BYTE_DATA 0x08 +#define PIIX4_WORD_DATA 0x0C +#define PIIX4_BLOCK_DATA 0x14 + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + PIIX4. DANGEROUS! */ +static int force; +module_param (force, int, 0); +MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the PIIX4 at the given address. VERY DANGEROUS! */ +static int force_addr; +module_param (force_addr, int, 0); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the PIIX4 at the given address. " + "EXTREMELY DANGEROUS!"); + +static unsigned short piix4_smba; +static int srvrworks_csb5_delay; +static struct pci_driver piix4_driver; +static struct i2c_adapter piix4_adapter; + +static struct dmi_system_id __devinitdata piix4_dmi_blacklist[] = { + { + .ident = "Sapphire AM2RD790", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "SAPPHIRE Inc."), + DMI_MATCH(DMI_BOARD_NAME, "PC-AM2RD790"), + }, + }, + { + .ident = "DFI Lanparty UT 790FX", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "DFI Inc."), + DMI_MATCH(DMI_BOARD_NAME, "LP UT 790FX"), + }, + }, + { } +}; + +/* The IBM entry is in a separate table because we only check it + on Intel-based systems */ +static struct dmi_system_id __devinitdata piix4_dmi_ibm[] = { + { + .ident = "IBM", + .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), }, + }, + { }, +}; + +static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, + const struct pci_device_id *id) +{ + unsigned char temp; + + if ((PIIX4_dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && + (PIIX4_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5)) + srvrworks_csb5_delay = 1; + + /* On some motherboards, it was reported that accessing the SMBus + caused severe hardware problems */ + if (dmi_check_system(piix4_dmi_blacklist)) { + dev_err(&PIIX4_dev->dev, + "Accessing the SMBus on this system is unsafe!\n"); + return -EPERM; + } + + /* Don't access SMBus on IBM systems which get corrupted eeproms */ + if (dmi_check_system(piix4_dmi_ibm) && + PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) { + dev_err(&PIIX4_dev->dev, "IBM system detected; this module " + "may corrupt your serial eeprom! Refusing to load " + "module!\n"); + return -EPERM; + } + + /* Determine the address of the SMBus areas */ + if (force_addr) { + piix4_smba = force_addr & 0xfff0; + force = 0; + } else { + pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); + piix4_smba &= 0xfff0; + if(piix4_smba == 0) { + dev_err(&PIIX4_dev->dev, "SMBus base address " + "uninitialized - upgrade BIOS or use " + "force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) + return -ENODEV; + + if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { + dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", + piix4_smba); + return -EBUSY; + } + + pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); + + /* If force_addr is set, we program the new address here. Just to make + sure, we disable the PIIX4 first. */ + if (force_addr) { + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); + dev_info(&PIIX4_dev->dev, "WARNING: SMBus interface set to " + "new address %04x!\n", piix4_smba); + } else if ((temp & 1) == 0) { + if (force) { + /* This should never need to be done, but has been + * noted that many Dell machines have the SMBus + * interface on the PIIX4 disabled!? NOTE: This assumes + * I/O space and other allocations WERE done by the + * Bios! Don't complain if your hardware does weird + * things after enabling this. :') Check for Bios + * updates before resorting to this. + */ + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, + temp | 1); + dev_printk(KERN_NOTICE, &PIIX4_dev->dev, + "WARNING: SMBus interface has been " + "FORCEFULLY ENABLED!\n"); + } else { + dev_err(&PIIX4_dev->dev, + "Host SMBus controller not enabled!\n"); + release_region(piix4_smba, SMBIOSIZE); + piix4_smba = 0; + return -ENODEV; + } + } + + if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2)) + dev_dbg(&PIIX4_dev->dev, "Using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + dev_dbg(&PIIX4_dev->dev, "Using Interrupt SMI# for SMBus.\n"); + else + dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration " + "(or code out of date)!\n"); + + pci_read_config_byte(PIIX4_dev, SMBREV, &temp); + dev_info(&PIIX4_dev->dev, + "SMBus Host Controller at 0x%x, revision %d\n", + piix4_smba, temp); + + return 0; +} + +static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev, + const struct pci_device_id *id) +{ + unsigned short smba_idx = 0xcd6; + u8 smba_en_lo, smba_en_hi, i2ccfg, i2ccfg_offset = 0x10, smb_en = 0x2c; + + /* SB800 and later SMBus does not support forcing address */ + if (force || force_addr) { + dev_err(&PIIX4_dev->dev, "SMBus does not support " + "forcing address!\n"); + return -EINVAL; + } + + /* Determine the address of the SMBus areas */ + if (!request_region(smba_idx, 2, "smba_idx")) { + dev_err(&PIIX4_dev->dev, "SMBus base address index region " + "0x%x already in use!\n", smba_idx); + return -EBUSY; + } + outb_p(smb_en, smba_idx); + smba_en_lo = inb_p(smba_idx + 1); + outb_p(smb_en + 1, smba_idx); + smba_en_hi = inb_p(smba_idx + 1); + release_region(smba_idx, 2); + + if ((smba_en_lo & 1) == 0) { + dev_err(&PIIX4_dev->dev, + "Host SMBus controller not enabled!\n"); + return -ENODEV; + } + + piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; + if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) + return -ENODEV; + + if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { + dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", + piix4_smba); + return -EBUSY; + } + + /* Request the SMBus I2C bus config region */ + if (!request_region(piix4_smba + i2ccfg_offset, 1, "i2ccfg")) { + dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region " + "0x%x already in use!\n", piix4_smba + i2ccfg_offset); + release_region(piix4_smba, SMBIOSIZE); + piix4_smba = 0; + return -EBUSY; + } + i2ccfg = inb_p(piix4_smba + i2ccfg_offset); + release_region(piix4_smba + i2ccfg_offset, 1); + + if (i2ccfg & 1) + dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus.\n"); + else + dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus.\n"); + + dev_info(&PIIX4_dev->dev, + "SMBus Host Controller at 0x%x, revision %d\n", + piix4_smba, i2ccfg >> 4); + + return 0; +} + +static int piix4_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&piix4_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + dev_dbg(&piix4_adapter.dev, "SMBus busy (%02x). " + "Resetting...\n", temp); + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + dev_err(&piix4_adapter.dev, "Failed! (%02x)\n", temp); + return -EBUSY; + } else { + dev_dbg(&piix4_adapter.dev, "Successful!\n"); + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ + if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */ + msleep(2); + else + msleep(1); + + while ((++timeout < MAX_TIMEOUT) && + ((temp = inb_p(SMBHSTSTS)) & 0x01)) + msleep(1); + + /* If the SMBus is still busy, we give up */ + if (timeout == MAX_TIMEOUT) { + dev_err(&piix4_adapter.dev, "SMBus Timeout!\n"); + result = -ETIMEDOUT; + } + + if (temp & 0x10) { + result = -EIO; + dev_err(&piix4_adapter.dev, "Error: Failed bus transaction\n"); + } + + if (temp & 0x08) { + result = -EIO; + dev_dbg(&piix4_adapter.dev, "Bus collision! SMBus may be " + "locked until next hard reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -ENXIO; + dev_dbg(&piix4_adapter.dev, "Error: no response!\n"); + } + + if (inb_p(SMBHSTSTS) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + dev_err(&piix4_adapter.dev, "Failed reset at end of " + "transaction (%02x)\n", temp); + } + dev_dbg(&piix4_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); + return result; +} + +/* Return negative errno on error. */ +static s32 piix4_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p((addr << 1) | read_write, + SMBHSTADD); + size = PIIX4_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p((addr << 1) | read_write, + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = PIIX4_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p((addr << 1) | read_write, + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = PIIX4_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p((addr << 1) | read_write, + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = PIIX4_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p((addr << 1) | read_write, + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + outb_p(len, SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = PIIX4_BLOCK_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + status = piix4_transaction(); + if (status) + return status; + + if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) + return 0; + + + switch (size) { + case PIIX4_BYTE: + case PIIX4_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case PIIX4_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; +} + +static u32 piix4_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = piix4_access, + .functionality = piix4_func, +}; + +static struct i2c_adapter piix4_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, +}; + +static const struct pci_device_id piix4_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_OSB4) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB5) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB6) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_HT1000SB) }, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_HT1100LD) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, piix4_ids); + +static int __devinit piix4_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + int retval; + + if ((dev->vendor == PCI_VENDOR_ID_ATI && + dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && + dev->revision >= 0x40) || + dev->vendor == PCI_VENDOR_ID_AMD) + /* base address location etc changed in SB800 */ + retval = piix4_setup_sb800(dev, id); + else + retval = piix4_setup(dev, id); + + if (retval) + return retval; + + /* set up the sysfs linkage to our parent device */ + piix4_adapter.dev.parent = &dev->dev; + + snprintf(piix4_adapter.name, sizeof(piix4_adapter.name), + "SMBus PIIX4 adapter at %04x", piix4_smba); + + if ((retval = i2c_add_adapter(&piix4_adapter))) { + dev_err(&dev->dev, "Couldn't register adapter!\n"); + release_region(piix4_smba, SMBIOSIZE); + piix4_smba = 0; + } + + return retval; +} + +static void __devexit piix4_remove(struct pci_dev *dev) +{ + if (piix4_smba) { + i2c_del_adapter(&piix4_adapter); + release_region(piix4_smba, SMBIOSIZE); + piix4_smba = 0; + } +} + +static struct pci_driver piix4_driver = { + .name = "piix4_smbus", + .id_table = piix4_ids, + .probe = piix4_probe, + .remove = __devexit_p(piix4_remove), +}; + +static int __init i2c_piix4_init(void) +{ + return pci_register_driver(&piix4_driver); +} + +static void __exit i2c_piix4_exit(void) +{ + pci_unregister_driver(&piix4_driver); +} + +MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " + "Philip Edelbrock <phil@netroedge.com>"); +MODULE_DESCRIPTION("PIIX4 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_piix4_init); +module_exit(i2c_piix4_exit); diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c new file mode 100644 index 00000000..dfa7ae9c --- /dev/null +++ b/drivers/i2c/busses/i2c-pmcmsp.c @@ -0,0 +1,656 @@ +/* + * Specific bus support for PMC-TWI compliant implementation on MSP71xx. + * + * Copyright 2005-2007 PMC-Sierra, Inc. + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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/platform_device.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/io.h> + +#define DRV_NAME "pmcmsptwi" + +#define MSP_TWI_SF_CLK_REG_OFFSET 0x00 +#define MSP_TWI_HS_CLK_REG_OFFSET 0x04 +#define MSP_TWI_CFG_REG_OFFSET 0x08 +#define MSP_TWI_CMD_REG_OFFSET 0x0c +#define MSP_TWI_ADD_REG_OFFSET 0x10 +#define MSP_TWI_DAT_0_REG_OFFSET 0x14 +#define MSP_TWI_DAT_1_REG_OFFSET 0x18 +#define MSP_TWI_INT_STS_REG_OFFSET 0x1c +#define MSP_TWI_INT_MSK_REG_OFFSET 0x20 +#define MSP_TWI_BUSY_REG_OFFSET 0x24 + +#define MSP_TWI_INT_STS_DONE (1 << 0) +#define MSP_TWI_INT_STS_LOST_ARBITRATION (1 << 1) +#define MSP_TWI_INT_STS_NO_RESPONSE (1 << 2) +#define MSP_TWI_INT_STS_DATA_COLLISION (1 << 3) +#define MSP_TWI_INT_STS_BUSY (1 << 4) +#define MSP_TWI_INT_STS_ALL 0x1f + +#define MSP_MAX_BYTES_PER_RW 8 +#define MSP_MAX_POLL 5 +#define MSP_POLL_DELAY 10 +#define MSP_IRQ_TIMEOUT (MSP_MAX_POLL * MSP_POLL_DELAY) + +/* IO Operation macros */ +#define pmcmsptwi_readl __raw_readl +#define pmcmsptwi_writel __raw_writel + +/* TWI command type */ +enum pmcmsptwi_cmd_type { + MSP_TWI_CMD_WRITE = 0, /* Write only */ + MSP_TWI_CMD_READ = 1, /* Read only */ + MSP_TWI_CMD_WRITE_READ = 2, /* Write then Read */ +}; + +/* The possible results of the xferCmd */ +enum pmcmsptwi_xfer_result { + MSP_TWI_XFER_OK = 0, + MSP_TWI_XFER_TIMEOUT, + MSP_TWI_XFER_BUSY, + MSP_TWI_XFER_DATA_COLLISION, + MSP_TWI_XFER_NO_RESPONSE, + MSP_TWI_XFER_LOST_ARBITRATION, +}; + +/* Corresponds to a PMCTWI clock configuration register */ +struct pmcmsptwi_clock { + u8 filter; /* Bits 15:12, default = 0x03 */ + u16 clock; /* Bits 9:0, default = 0x001f */ +}; + +struct pmcmsptwi_clockcfg { + struct pmcmsptwi_clock standard; /* The standard/fast clock config */ + struct pmcmsptwi_clock highspeed; /* The highspeed clock config */ +}; + +/* Corresponds to the main TWI configuration register */ +struct pmcmsptwi_cfg { + u8 arbf; /* Bits 15:12, default=0x03 */ + u8 nak; /* Bits 11:8, default=0x03 */ + u8 add10; /* Bit 7, default=0x00 */ + u8 mst_code; /* Bits 6:4, default=0x00 */ + u8 arb; /* Bit 1, default=0x01 */ + u8 highspeed; /* Bit 0, default=0x00 */ +}; + +/* A single pmctwi command to issue */ +struct pmcmsptwi_cmd { + u16 addr; /* The slave address (7 or 10 bits) */ + enum pmcmsptwi_cmd_type type; /* The command type */ + u8 write_len; /* Number of bytes in the write buffer */ + u8 read_len; /* Number of bytes in the read buffer */ + u8 *write_data; /* Buffer of characters to send */ + u8 *read_data; /* Buffer to fill with incoming data */ +}; + +/* The private data */ +struct pmcmsptwi_data { + void __iomem *iobase; /* iomapped base for IO */ + int irq; /* IRQ to use (0 disables) */ + struct completion wait; /* Completion for xfer */ + struct mutex lock; /* Used for threadsafeness */ + enum pmcmsptwi_xfer_result last_result; /* result of last xfer */ +}; + +/* The default settings */ +static const struct pmcmsptwi_clockcfg pmcmsptwi_defclockcfg = { + .standard = { + .filter = 0x3, + .clock = 0x1f, + }, + .highspeed = { + .filter = 0x3, + .clock = 0x1f, + }, +}; + +static const struct pmcmsptwi_cfg pmcmsptwi_defcfg = { + .arbf = 0x03, + .nak = 0x03, + .add10 = 0x00, + .mst_code = 0x00, + .arb = 0x01, + .highspeed = 0x00, +}; + +static struct pmcmsptwi_data pmcmsptwi_data; + +static struct i2c_adapter pmcmsptwi_adapter; + +/* inline helper functions */ +static inline u32 pmcmsptwi_clock_to_reg( + const struct pmcmsptwi_clock *clock) +{ + return ((clock->filter & 0xf) << 12) | (clock->clock & 0x03ff); +} + +static inline void pmcmsptwi_reg_to_clock( + u32 reg, struct pmcmsptwi_clock *clock) +{ + clock->filter = (reg >> 12) & 0xf; + clock->clock = reg & 0x03ff; +} + +static inline u32 pmcmsptwi_cfg_to_reg(const struct pmcmsptwi_cfg *cfg) +{ + return ((cfg->arbf & 0xf) << 12) | + ((cfg->nak & 0xf) << 8) | + ((cfg->add10 & 0x1) << 7) | + ((cfg->mst_code & 0x7) << 4) | + ((cfg->arb & 0x1) << 1) | + (cfg->highspeed & 0x1); +} + +static inline void pmcmsptwi_reg_to_cfg(u32 reg, struct pmcmsptwi_cfg *cfg) +{ + cfg->arbf = (reg >> 12) & 0xf; + cfg->nak = (reg >> 8) & 0xf; + cfg->add10 = (reg >> 7) & 0x1; + cfg->mst_code = (reg >> 4) & 0x7; + cfg->arb = (reg >> 1) & 0x1; + cfg->highspeed = reg & 0x1; +} + +/* + * Sets the current clock configuration + */ +static void pmcmsptwi_set_clock_config(const struct pmcmsptwi_clockcfg *cfg, + struct pmcmsptwi_data *data) +{ + mutex_lock(&data->lock); + pmcmsptwi_writel(pmcmsptwi_clock_to_reg(&cfg->standard), + data->iobase + MSP_TWI_SF_CLK_REG_OFFSET); + pmcmsptwi_writel(pmcmsptwi_clock_to_reg(&cfg->highspeed), + data->iobase + MSP_TWI_HS_CLK_REG_OFFSET); + mutex_unlock(&data->lock); +} + +/* + * Gets the current TWI bus configuration + */ +static void pmcmsptwi_get_twi_config(struct pmcmsptwi_cfg *cfg, + struct pmcmsptwi_data *data) +{ + mutex_lock(&data->lock); + pmcmsptwi_reg_to_cfg(pmcmsptwi_readl( + data->iobase + MSP_TWI_CFG_REG_OFFSET), cfg); + mutex_unlock(&data->lock); +} + +/* + * Sets the current TWI bus configuration + */ +static void pmcmsptwi_set_twi_config(const struct pmcmsptwi_cfg *cfg, + struct pmcmsptwi_data *data) +{ + mutex_lock(&data->lock); + pmcmsptwi_writel(pmcmsptwi_cfg_to_reg(cfg), + data->iobase + MSP_TWI_CFG_REG_OFFSET); + mutex_unlock(&data->lock); +} + +/* + * Parses the 'int_sts' register and returns a well-defined error code + */ +static enum pmcmsptwi_xfer_result pmcmsptwi_get_result(u32 reg) +{ + if (reg & MSP_TWI_INT_STS_LOST_ARBITRATION) { + dev_dbg(&pmcmsptwi_adapter.dev, + "Result: Lost arbitration\n"); + return MSP_TWI_XFER_LOST_ARBITRATION; + } else if (reg & MSP_TWI_INT_STS_NO_RESPONSE) { + dev_dbg(&pmcmsptwi_adapter.dev, + "Result: No response\n"); + return MSP_TWI_XFER_NO_RESPONSE; + } else if (reg & MSP_TWI_INT_STS_DATA_COLLISION) { + dev_dbg(&pmcmsptwi_adapter.dev, + "Result: Data collision\n"); + return MSP_TWI_XFER_DATA_COLLISION; + } else if (reg & MSP_TWI_INT_STS_BUSY) { + dev_dbg(&pmcmsptwi_adapter.dev, + "Result: Bus busy\n"); + return MSP_TWI_XFER_BUSY; + } + + dev_dbg(&pmcmsptwi_adapter.dev, "Result: Operation succeeded\n"); + return MSP_TWI_XFER_OK; +} + +/* + * In interrupt mode, handle the interrupt. + * NOTE: Assumes data->lock is held. + */ +static irqreturn_t pmcmsptwi_interrupt(int irq, void *ptr) +{ + struct pmcmsptwi_data *data = ptr; + + u32 reason = pmcmsptwi_readl(data->iobase + + MSP_TWI_INT_STS_REG_OFFSET); + pmcmsptwi_writel(reason, data->iobase + MSP_TWI_INT_STS_REG_OFFSET); + + dev_dbg(&pmcmsptwi_adapter.dev, "Got interrupt 0x%08x\n", reason); + if (!(reason & MSP_TWI_INT_STS_DONE)) + return IRQ_NONE; + + data->last_result = pmcmsptwi_get_result(reason); + complete(&data->wait); + + return IRQ_HANDLED; +} + +/* + * Probe for and register the device and return 0 if there is one. + */ +static int __devinit pmcmsptwi_probe(struct platform_device *pldev) +{ + struct resource *res; + int rc = -ENODEV; + + /* get the static platform resources */ + res = platform_get_resource(pldev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pldev->dev, "IOMEM resource not found\n"); + goto ret_err; + } + + /* reserve the memory region */ + if (!request_mem_region(res->start, resource_size(res), + pldev->name)) { + dev_err(&pldev->dev, + "Unable to get memory/io address region 0x%08x\n", + res->start); + rc = -EBUSY; + goto ret_err; + } + + /* remap the memory */ + pmcmsptwi_data.iobase = ioremap_nocache(res->start, + resource_size(res)); + if (!pmcmsptwi_data.iobase) { + dev_err(&pldev->dev, + "Unable to ioremap address 0x%08x\n", res->start); + rc = -EIO; + goto ret_unreserve; + } + + /* request the irq */ + pmcmsptwi_data.irq = platform_get_irq(pldev, 0); + if (pmcmsptwi_data.irq) { + rc = request_irq(pmcmsptwi_data.irq, &pmcmsptwi_interrupt, + IRQF_SHARED | IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + pldev->name, &pmcmsptwi_data); + if (rc == 0) { + /* + * Enable 'DONE' interrupt only. + * + * If you enable all interrupts, you will get one on + * error and another when the operation completes. + * This way you only have to handle one interrupt, + * but you can still check all result flags. + */ + pmcmsptwi_writel(MSP_TWI_INT_STS_DONE, + pmcmsptwi_data.iobase + + MSP_TWI_INT_MSK_REG_OFFSET); + } else { + dev_warn(&pldev->dev, + "Could not assign TWI IRQ handler " + "to irq %d (continuing with poll)\n", + pmcmsptwi_data.irq); + pmcmsptwi_data.irq = 0; + } + } + + init_completion(&pmcmsptwi_data.wait); + mutex_init(&pmcmsptwi_data.lock); + + pmcmsptwi_set_clock_config(&pmcmsptwi_defclockcfg, &pmcmsptwi_data); + pmcmsptwi_set_twi_config(&pmcmsptwi_defcfg, &pmcmsptwi_data); + + printk(KERN_INFO DRV_NAME ": Registering MSP71xx I2C adapter\n"); + + pmcmsptwi_adapter.dev.parent = &pldev->dev; + platform_set_drvdata(pldev, &pmcmsptwi_adapter); + i2c_set_adapdata(&pmcmsptwi_adapter, &pmcmsptwi_data); + + rc = i2c_add_adapter(&pmcmsptwi_adapter); + if (rc) { + dev_err(&pldev->dev, "Unable to register I2C adapter\n"); + goto ret_unmap; + } + + return 0; + +ret_unmap: + platform_set_drvdata(pldev, NULL); + if (pmcmsptwi_data.irq) { + pmcmsptwi_writel(0, + pmcmsptwi_data.iobase + MSP_TWI_INT_MSK_REG_OFFSET); + free_irq(pmcmsptwi_data.irq, &pmcmsptwi_data); + } + + iounmap(pmcmsptwi_data.iobase); + +ret_unreserve: + release_mem_region(res->start, resource_size(res)); + +ret_err: + return rc; +} + +/* + * Release the device and return 0 if there is one. + */ +static int __devexit pmcmsptwi_remove(struct platform_device *pldev) +{ + struct resource *res; + + i2c_del_adapter(&pmcmsptwi_adapter); + + platform_set_drvdata(pldev, NULL); + if (pmcmsptwi_data.irq) { + pmcmsptwi_writel(0, + pmcmsptwi_data.iobase + MSP_TWI_INT_MSK_REG_OFFSET); + free_irq(pmcmsptwi_data.irq, &pmcmsptwi_data); + } + + iounmap(pmcmsptwi_data.iobase); + + res = platform_get_resource(pldev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + return 0; +} + +/* + * Polls the 'busy' register until the command is complete. + * NOTE: Assumes data->lock is held. + */ +static void pmcmsptwi_poll_complete(struct pmcmsptwi_data *data) +{ + int i; + + for (i = 0; i < MSP_MAX_POLL; i++) { + u32 val = pmcmsptwi_readl(data->iobase + + MSP_TWI_BUSY_REG_OFFSET); + if (val == 0) { + u32 reason = pmcmsptwi_readl(data->iobase + + MSP_TWI_INT_STS_REG_OFFSET); + pmcmsptwi_writel(reason, data->iobase + + MSP_TWI_INT_STS_REG_OFFSET); + data->last_result = pmcmsptwi_get_result(reason); + return; + } + udelay(MSP_POLL_DELAY); + } + + dev_dbg(&pmcmsptwi_adapter.dev, "Result: Poll timeout\n"); + data->last_result = MSP_TWI_XFER_TIMEOUT; +} + +/* + * Do the transfer (low level): + * May use interrupt-driven or polling, depending on if an IRQ is + * presently registered. + * NOTE: Assumes data->lock is held. + */ +static enum pmcmsptwi_xfer_result pmcmsptwi_do_xfer( + u32 reg, struct pmcmsptwi_data *data) +{ + dev_dbg(&pmcmsptwi_adapter.dev, "Writing cmd reg 0x%08x\n", reg); + pmcmsptwi_writel(reg, data->iobase + MSP_TWI_CMD_REG_OFFSET); + if (data->irq) { + unsigned long timeleft = wait_for_completion_timeout( + &data->wait, MSP_IRQ_TIMEOUT); + if (timeleft == 0) { + dev_dbg(&pmcmsptwi_adapter.dev, + "Result: IRQ timeout\n"); + complete(&data->wait); + data->last_result = MSP_TWI_XFER_TIMEOUT; + } + } else + pmcmsptwi_poll_complete(data); + + return data->last_result; +} + +/* + * Helper routine, converts 'pmctwi_cmd' struct to register format + */ +static inline u32 pmcmsptwi_cmd_to_reg(const struct pmcmsptwi_cmd *cmd) +{ + return ((cmd->type & 0x3) << 8) | + (((cmd->write_len - 1) & 0x7) << 4) | + ((cmd->read_len - 1) & 0x7); +} + +/* + * Do the transfer (high level) + */ +static enum pmcmsptwi_xfer_result pmcmsptwi_xfer_cmd( + struct pmcmsptwi_cmd *cmd, + struct pmcmsptwi_data *data) +{ + enum pmcmsptwi_xfer_result retval; + + if ((cmd->type == MSP_TWI_CMD_WRITE && cmd->write_len == 0) || + (cmd->type == MSP_TWI_CMD_READ && cmd->read_len == 0) || + (cmd->type == MSP_TWI_CMD_WRITE_READ && + (cmd->read_len == 0 || cmd->write_len == 0))) { + dev_err(&pmcmsptwi_adapter.dev, + "%s: Cannot transfer less than 1 byte\n", + __func__); + return -EINVAL; + } + + if (cmd->read_len > MSP_MAX_BYTES_PER_RW || + cmd->write_len > MSP_MAX_BYTES_PER_RW) { + dev_err(&pmcmsptwi_adapter.dev, + "%s: Cannot transfer more than %d bytes\n", + __func__, MSP_MAX_BYTES_PER_RW); + return -EINVAL; + } + + mutex_lock(&data->lock); + dev_dbg(&pmcmsptwi_adapter.dev, + "Setting address to 0x%04x\n", cmd->addr); + pmcmsptwi_writel(cmd->addr, data->iobase + MSP_TWI_ADD_REG_OFFSET); + + if (cmd->type == MSP_TWI_CMD_WRITE || + cmd->type == MSP_TWI_CMD_WRITE_READ) { + u64 tmp = be64_to_cpup((__be64 *)cmd->write_data); + tmp >>= (MSP_MAX_BYTES_PER_RW - cmd->write_len) * 8; + dev_dbg(&pmcmsptwi_adapter.dev, "Writing 0x%016llx\n", tmp); + pmcmsptwi_writel(tmp & 0x00000000ffffffffLL, + data->iobase + MSP_TWI_DAT_0_REG_OFFSET); + if (cmd->write_len > 4) + pmcmsptwi_writel(tmp >> 32, + data->iobase + MSP_TWI_DAT_1_REG_OFFSET); + } + + retval = pmcmsptwi_do_xfer(pmcmsptwi_cmd_to_reg(cmd), data); + if (retval != MSP_TWI_XFER_OK) + goto xfer_err; + + if (cmd->type == MSP_TWI_CMD_READ || + cmd->type == MSP_TWI_CMD_WRITE_READ) { + int i; + u64 rmsk = ~(0xffffffffffffffffLL << (cmd->read_len * 8)); + u64 tmp = (u64)pmcmsptwi_readl(data->iobase + + MSP_TWI_DAT_0_REG_OFFSET); + if (cmd->read_len > 4) + tmp |= (u64)pmcmsptwi_readl(data->iobase + + MSP_TWI_DAT_1_REG_OFFSET) << 32; + tmp &= rmsk; + dev_dbg(&pmcmsptwi_adapter.dev, "Read 0x%016llx\n", tmp); + + for (i = 0; i < cmd->read_len; i++) + cmd->read_data[i] = tmp >> i; + } + +xfer_err: + mutex_unlock(&data->lock); + + return retval; +} + +/* -- Algorithm functions -- */ + +/* + * Sends an i2c command out on the adapter + */ +static int pmcmsptwi_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msg, int num) +{ + struct pmcmsptwi_data *data = i2c_get_adapdata(adap); + struct pmcmsptwi_cmd cmd; + struct pmcmsptwi_cfg oldcfg, newcfg; + int ret; + + if (num > 2) { + dev_dbg(&adap->dev, "%d messages unsupported\n", num); + return -EINVAL; + } else if (num == 2) { + /* Check for a dual write-then-read command */ + struct i2c_msg *nextmsg = msg + 1; + if (!(msg->flags & I2C_M_RD) && + (nextmsg->flags & I2C_M_RD) && + msg->addr == nextmsg->addr) { + cmd.type = MSP_TWI_CMD_WRITE_READ; + cmd.write_len = msg->len; + cmd.write_data = msg->buf; + cmd.read_len = nextmsg->len; + cmd.read_data = nextmsg->buf; + } else { + dev_dbg(&adap->dev, + "Non write-read dual messages unsupported\n"); + return -EINVAL; + } + } else if (msg->flags & I2C_M_RD) { + cmd.type = MSP_TWI_CMD_READ; + cmd.read_len = msg->len; + cmd.read_data = msg->buf; + cmd.write_len = 0; + cmd.write_data = NULL; + } else { + cmd.type = MSP_TWI_CMD_WRITE; + cmd.read_len = 0; + cmd.read_data = NULL; + cmd.write_len = msg->len; + cmd.write_data = msg->buf; + } + + if (msg->len == 0) { + dev_err(&adap->dev, "Zero-byte messages unsupported\n"); + return -EINVAL; + } + + cmd.addr = msg->addr; + + if (msg->flags & I2C_M_TEN) { + pmcmsptwi_get_twi_config(&newcfg, data); + memcpy(&oldcfg, &newcfg, sizeof(oldcfg)); + + /* Set the special 10-bit address flag */ + newcfg.add10 = 1; + + pmcmsptwi_set_twi_config(&newcfg, data); + } + + /* Execute the command */ + ret = pmcmsptwi_xfer_cmd(&cmd, data); + + if (msg->flags & I2C_M_TEN) + pmcmsptwi_set_twi_config(&oldcfg, data); + + dev_dbg(&adap->dev, "I2C %s of %d bytes %s\n", + (msg->flags & I2C_M_RD) ? "read" : "write", msg->len, + (ret == MSP_TWI_XFER_OK) ? "succeeded" : "failed"); + + if (ret != MSP_TWI_XFER_OK) { + /* + * TODO: We could potentially loop and retry in the case + * of MSP_TWI_XFER_TIMEOUT. + */ + return -1; + } + + return 0; +} + +static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL; +} + +/* -- Initialization -- */ + +static struct i2c_algorithm pmcmsptwi_algo = { + .master_xfer = pmcmsptwi_master_xfer, + .functionality = pmcmsptwi_i2c_func, +}; + +static struct i2c_adapter pmcmsptwi_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &pmcmsptwi_algo, + .name = DRV_NAME, +}; + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:" DRV_NAME); + +static struct platform_driver pmcmsptwi_driver = { + .probe = pmcmsptwi_probe, + .remove = __devexit_p(pmcmsptwi_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init pmcmsptwi_init(void) +{ + return platform_driver_register(&pmcmsptwi_driver); +} + +static void __exit pmcmsptwi_exit(void) +{ + platform_driver_unregister(&pmcmsptwi_driver); +} + +MODULE_DESCRIPTION("PMC MSP TWI/SMBus/I2C driver"); +MODULE_LICENSE("GPL"); + +module_init(pmcmsptwi_init); +module_exit(pmcmsptwi_exit); diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c new file mode 100644 index 00000000..eb8ad538 --- /dev/null +++ b/drivers/i2c/busses/i2c-pnx.c @@ -0,0 +1,733 @@ +/* + * Provides I2C support for Philips PNX010x/PNX4008 boards. + * + * Authors: Dennis Kovalev <dkovalev@ru.mvista.com> + * Vitaly Wool <vwool@ru.mvista.com> + * + * 2004-2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/timer.h> +#include <linux/completion.h> +#include <linux/platform_device.h> +#include <linux/i2c-pnx.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/slab.h> + +#include <mach/hardware.h> +#include <mach/i2c.h> + +#define I2C_PNX_TIMEOUT 10 /* msec */ +#define I2C_PNX_SPEED_KHZ 100 +#define I2C_PNX_REGION_SIZE 0x100 + +static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data) +{ + while (timeout > 0 && + (ioread32(I2C_REG_STS(data)) & mstatus_active)) { + mdelay(1); + timeout--; + } + return (timeout <= 0); +} + +static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data) +{ + while (timeout > 0 && + (ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) { + mdelay(1); + timeout--; + } + return (timeout <= 0); +} + +static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data) +{ + struct timer_list *timer = &alg_data->mif.timer; + unsigned long expires = msecs_to_jiffies(I2C_PNX_TIMEOUT); + + if (expires <= 1) + expires = 2; + + del_timer_sync(timer); + + dev_dbg(&alg_data->adapter.dev, "Timer armed at %lu plus %lu jiffies.\n", + jiffies, expires); + + timer->expires = jiffies + expires; + timer->data = (unsigned long)alg_data; + + add_timer(timer); +} + +/** + * i2c_pnx_start - start a device + * @slave_addr: slave address + * @adap: pointer to adapter structure + * + * Generate a START signal in the desired mode. + */ +static int i2c_pnx_start(unsigned char slave_addr, + struct i2c_pnx_algo_data *alg_data) +{ + dev_dbg(&alg_data->adapter.dev, "%s(): addr 0x%x mode %d\n", __func__, + slave_addr, alg_data->mif.mode); + + /* Check for 7 bit slave addresses only */ + if (slave_addr & ~0x7f) { + dev_err(&alg_data->adapter.dev, + "%s: Invalid slave address %x. Only 7-bit addresses are supported\n", + alg_data->adapter.name, slave_addr); + return -EINVAL; + } + + /* First, make sure bus is idle */ + if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) { + /* Somebody else is monopolizing the bus */ + dev_err(&alg_data->adapter.dev, + "%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = %x\n", + alg_data->adapter.name, slave_addr, + ioread32(I2C_REG_CTL(alg_data)), + ioread32(I2C_REG_STS(alg_data))); + return -EBUSY; + } else if (ioread32(I2C_REG_STS(alg_data)) & mstatus_afi) { + /* Sorry, we lost the bus */ + dev_err(&alg_data->adapter.dev, + "%s: Arbitration failure. Slave addr = %02x\n", + alg_data->adapter.name, slave_addr); + return -EIO; + } + + /* + * OK, I2C is enabled and we have the bus. + * Clear the current TDI and AFI status flags. + */ + iowrite32(ioread32(I2C_REG_STS(alg_data)) | mstatus_tdi | mstatus_afi, + I2C_REG_STS(alg_data)); + + dev_dbg(&alg_data->adapter.dev, "%s(): sending %#x\n", __func__, + (slave_addr << 1) | start_bit | alg_data->mif.mode); + + /* Write the slave address, START bit and R/W bit */ + iowrite32((slave_addr << 1) | start_bit | alg_data->mif.mode, + I2C_REG_TX(alg_data)); + + dev_dbg(&alg_data->adapter.dev, "%s(): exit\n", __func__); + + return 0; +} + +/** + * i2c_pnx_stop - stop a device + * @adap: pointer to I2C adapter structure + * + * Generate a STOP signal to terminate the master transaction. + */ +static void i2c_pnx_stop(struct i2c_pnx_algo_data *alg_data) +{ + /* Only 1 msec max timeout due to interrupt context */ + long timeout = 1000; + + dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + /* Write a STOP bit to TX FIFO */ + iowrite32(0xff | stop_bit, I2C_REG_TX(alg_data)); + + /* Wait until the STOP is seen. */ + while (timeout > 0 && + (ioread32(I2C_REG_STS(alg_data)) & mstatus_active)) { + /* may be called from interrupt context */ + udelay(1); + timeout--; + } + + dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); +} + +/** + * i2c_pnx_master_xmit - transmit data to slave + * @adap: pointer to I2C adapter structure + * + * Sends one byte of data to the slave + */ +static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data) +{ + u32 val; + + dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + if (alg_data->mif.len > 0) { + /* We still have something to talk about... */ + val = *alg_data->mif.buf++; + + if (alg_data->mif.len == 1) + val |= stop_bit; + + alg_data->mif.len--; + iowrite32(val, I2C_REG_TX(alg_data)); + + dev_dbg(&alg_data->adapter.dev, "%s(): xmit %#x [%d]\n", + __func__, val, alg_data->mif.len + 1); + + if (alg_data->mif.len == 0) { + if (alg_data->last) { + /* Wait until the STOP is seen. */ + if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) + dev_err(&alg_data->adapter.dev, + "The bus is still active after timeout\n"); + } + /* Disable master interrupts */ + iowrite32(ioread32(I2C_REG_CTL(alg_data)) & + ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie), + I2C_REG_CTL(alg_data)); + + del_timer_sync(&alg_data->mif.timer); + + dev_dbg(&alg_data->adapter.dev, + "%s(): Waking up xfer routine.\n", + __func__); + + complete(&alg_data->mif.complete); + } + } else if (alg_data->mif.len == 0) { + /* zero-sized transfer */ + i2c_pnx_stop(alg_data); + + /* Disable master interrupts. */ + iowrite32(ioread32(I2C_REG_CTL(alg_data)) & + ~(mcntrl_afie | mcntrl_naie | mcntrl_drmie), + I2C_REG_CTL(alg_data)); + + /* Stop timer. */ + del_timer_sync(&alg_data->mif.timer); + dev_dbg(&alg_data->adapter.dev, + "%s(): Waking up xfer routine after zero-xfer.\n", + __func__); + + complete(&alg_data->mif.complete); + } + + dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + return 0; +} + +/** + * i2c_pnx_master_rcv - receive data from slave + * @adap: pointer to I2C adapter structure + * + * Reads one byte data from the slave + */ +static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) +{ + unsigned int val = 0; + u32 ctl = 0; + + dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + /* Check, whether there is already data, + * or we didn't 'ask' for it yet. + */ + if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) { + dev_dbg(&alg_data->adapter.dev, + "%s(): Write dummy data to fill Rx-fifo...\n", + __func__); + + if (alg_data->mif.len == 1) { + /* Last byte, do not acknowledge next rcv. */ + val |= stop_bit; + + /* + * Enable interrupt RFDAIE (data in Rx fifo), + * and disable DRMIE (need data for Tx) + */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl |= mcntrl_rffie | mcntrl_daie; + ctl &= ~mcntrl_drmie; + iowrite32(ctl, I2C_REG_CTL(alg_data)); + } + + /* + * Now we'll 'ask' for data: + * For each byte we want to receive, we must + * write a (dummy) byte to the Tx-FIFO. + */ + iowrite32(val, I2C_REG_TX(alg_data)); + + return 0; + } + + /* Handle data. */ + if (alg_data->mif.len > 0) { + val = ioread32(I2C_REG_RX(alg_data)); + *alg_data->mif.buf++ = (u8) (val & 0xff); + dev_dbg(&alg_data->adapter.dev, "%s(): rcv 0x%x [%d]\n", + __func__, val, alg_data->mif.len); + + alg_data->mif.len--; + if (alg_data->mif.len == 0) { + if (alg_data->last) + /* Wait until the STOP is seen. */ + if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) + dev_err(&alg_data->adapter.dev, + "The bus is still active after timeout\n"); + + /* Disable master interrupts */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | + mcntrl_drmie | mcntrl_daie); + iowrite32(ctl, I2C_REG_CTL(alg_data)); + + /* Kill timer. */ + del_timer_sync(&alg_data->mif.timer); + complete(&alg_data->mif.complete); + } + } + + dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + return 0; +} + +static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id) +{ + struct i2c_pnx_algo_data *alg_data = dev_id; + u32 stat, ctl; + + dev_dbg(&alg_data->adapter.dev, + "%s(): mstat = %x mctrl = %x, mode = %d\n", + __func__, + ioread32(I2C_REG_STS(alg_data)), + ioread32(I2C_REG_CTL(alg_data)), + alg_data->mif.mode); + stat = ioread32(I2C_REG_STS(alg_data)); + + /* let's see what kind of event this is */ + if (stat & mstatus_afi) { + /* We lost arbitration in the midst of a transfer */ + alg_data->mif.ret = -EIO; + + /* Disable master interrupts. */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | + mcntrl_drmie); + iowrite32(ctl, I2C_REG_CTL(alg_data)); + + /* Stop timer, to prevent timeout. */ + del_timer_sync(&alg_data->mif.timer); + complete(&alg_data->mif.complete); + } else if (stat & mstatus_nai) { + /* Slave did not acknowledge, generate a STOP */ + dev_dbg(&alg_data->adapter.dev, + "%s(): Slave did not acknowledge, generating a STOP.\n", + __func__); + i2c_pnx_stop(alg_data); + + /* Disable master interrupts. */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | + mcntrl_drmie); + iowrite32(ctl, I2C_REG_CTL(alg_data)); + + /* Our return value. */ + alg_data->mif.ret = -EIO; + + /* Stop timer, to prevent timeout. */ + del_timer_sync(&alg_data->mif.timer); + complete(&alg_data->mif.complete); + } else { + /* + * Two options: + * - Master Tx needs data. + * - There is data in the Rx-fifo + * The latter is only the case if we have requested for data, + * via a dummy write. (See 'i2c_pnx_master_rcv'.) + * We therefore check, as a sanity check, whether that interrupt + * has been enabled. + */ + if ((stat & mstatus_drmi) || !(stat & mstatus_rfe)) { + if (alg_data->mif.mode == I2C_SMBUS_WRITE) { + i2c_pnx_master_xmit(alg_data); + } else if (alg_data->mif.mode == I2C_SMBUS_READ) { + i2c_pnx_master_rcv(alg_data); + } + } + } + + /* Clear TDI and AFI bits */ + stat = ioread32(I2C_REG_STS(alg_data)); + iowrite32(stat | mstatus_tdi | mstatus_afi, I2C_REG_STS(alg_data)); + + dev_dbg(&alg_data->adapter.dev, + "%s(): exiting, stat = %x ctrl = %x.\n", + __func__, ioread32(I2C_REG_STS(alg_data)), + ioread32(I2C_REG_CTL(alg_data))); + + return IRQ_HANDLED; +} + +static void i2c_pnx_timeout(unsigned long data) +{ + struct i2c_pnx_algo_data *alg_data = (struct i2c_pnx_algo_data *)data; + u32 ctl; + + dev_err(&alg_data->adapter.dev, + "Master timed out. stat = %04x, cntrl = %04x. Resetting master...\n", + ioread32(I2C_REG_STS(alg_data)), + ioread32(I2C_REG_CTL(alg_data))); + + /* Reset master and disable interrupts */ + ctl = ioread32(I2C_REG_CTL(alg_data)); + ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie); + iowrite32(ctl, I2C_REG_CTL(alg_data)); + + ctl |= mcntrl_reset; + iowrite32(ctl, I2C_REG_CTL(alg_data)); + wait_reset(I2C_PNX_TIMEOUT, alg_data); + alg_data->mif.ret = -EIO; + complete(&alg_data->mif.complete); +} + +static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data) +{ + u32 stat; + + if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_active) { + dev_err(&alg_data->adapter.dev, + "%s: Bus is still active after xfer. Reset it...\n", + alg_data->adapter.name); + iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, + I2C_REG_CTL(alg_data)); + wait_reset(I2C_PNX_TIMEOUT, alg_data); + } else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) { + /* If there is data in the fifo's after transfer, + * flush fifo's by reset. + */ + iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, + I2C_REG_CTL(alg_data)); + wait_reset(I2C_PNX_TIMEOUT, alg_data); + } else if (stat & mstatus_nai) { + iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, + I2C_REG_CTL(alg_data)); + wait_reset(I2C_PNX_TIMEOUT, alg_data); + } +} + +/** + * i2c_pnx_xfer - generic transfer entry point + * @adap: pointer to I2C adapter structure + * @msgs: array of messages + * @num: number of messages + * + * Initiates the transfer + */ +static int +i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct i2c_msg *pmsg; + int rc = 0, completed = 0, i; + struct i2c_pnx_algo_data *alg_data = adap->algo_data; + u32 stat = ioread32(I2C_REG_STS(alg_data)); + + dev_dbg(&alg_data->adapter.dev, + "%s(): entering: %d messages, stat = %04x.\n", + __func__, num, ioread32(I2C_REG_STS(alg_data))); + + bus_reset_if_active(alg_data); + + /* Process transactions in a loop. */ + for (i = 0; rc >= 0 && i < num; i++) { + u8 addr; + + pmsg = &msgs[i]; + addr = pmsg->addr; + + if (pmsg->flags & I2C_M_TEN) { + dev_err(&alg_data->adapter.dev, + "%s: 10 bits addr not supported!\n", + alg_data->adapter.name); + rc = -EINVAL; + break; + } + + alg_data->mif.buf = pmsg->buf; + alg_data->mif.len = pmsg->len; + alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ? + I2C_SMBUS_READ : I2C_SMBUS_WRITE; + alg_data->mif.ret = 0; + alg_data->last = (i == num - 1); + + dev_dbg(&alg_data->adapter.dev, "%s(): mode %d, %d bytes\n", + __func__, alg_data->mif.mode, alg_data->mif.len); + + i2c_pnx_arm_timer(alg_data); + + /* initialize the completion var */ + init_completion(&alg_data->mif.complete); + + /* Enable master interrupt */ + iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_afie | + mcntrl_naie | mcntrl_drmie, + I2C_REG_CTL(alg_data)); + + /* Put start-code and slave-address on the bus. */ + rc = i2c_pnx_start(addr, alg_data); + if (rc < 0) + break; + + /* Wait for completion */ + wait_for_completion(&alg_data->mif.complete); + + if (!(rc = alg_data->mif.ret)) + completed++; + dev_dbg(&alg_data->adapter.dev, + "%s(): Complete, return code = %d.\n", + __func__, rc); + + /* Clear TDI and AFI bits in case they are set. */ + if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_tdi) { + dev_dbg(&alg_data->adapter.dev, + "%s: TDI still set... clearing now.\n", + alg_data->adapter.name); + iowrite32(stat, I2C_REG_STS(alg_data)); + } + if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_afi) { + dev_dbg(&alg_data->adapter.dev, + "%s: AFI still set... clearing now.\n", + alg_data->adapter.name); + iowrite32(stat, I2C_REG_STS(alg_data)); + } + } + + bus_reset_if_active(alg_data); + + /* Cleanup to be sure... */ + alg_data->mif.buf = NULL; + alg_data->mif.len = 0; + + dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n", + __func__, ioread32(I2C_REG_STS(alg_data))); + + if (completed != num) + return ((rc < 0) ? rc : -EREMOTEIO); + + return num; +} + +static u32 i2c_pnx_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm pnx_algorithm = { + .master_xfer = i2c_pnx_xfer, + .functionality = i2c_pnx_func, +}; + +#ifdef CONFIG_PM +static int i2c_pnx_controller_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); + + clk_disable(alg_data->clk); + + return 0; +} + +static int i2c_pnx_controller_resume(struct platform_device *pdev) +{ + struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); + + return clk_enable(alg_data->clk); +} +#else +#define i2c_pnx_controller_suspend NULL +#define i2c_pnx_controller_resume NULL +#endif + +static int __devinit i2c_pnx_probe(struct platform_device *pdev) +{ + unsigned long tmp; + int ret = 0; + struct i2c_pnx_algo_data *alg_data; + unsigned long freq; + struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data; + + if (!i2c_pnx || !i2c_pnx->name) { + dev_err(&pdev->dev, "%s: no platform data supplied\n", + __func__); + ret = -EINVAL; + goto out; + } + + alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL); + if (!alg_data) { + ret = -ENOMEM; + goto err_kzalloc; + } + + platform_set_drvdata(pdev, alg_data); + + strlcpy(alg_data->adapter.name, i2c_pnx->name, + sizeof(alg_data->adapter.name)); + alg_data->adapter.dev.parent = &pdev->dev; + alg_data->adapter.algo = &pnx_algorithm; + alg_data->adapter.algo_data = alg_data; + alg_data->adapter.nr = pdev->id; + alg_data->i2c_pnx = i2c_pnx; + + alg_data->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(alg_data->clk)) { + ret = PTR_ERR(alg_data->clk); + goto out_drvdata; + } + + init_timer(&alg_data->mif.timer); + alg_data->mif.timer.function = i2c_pnx_timeout; + alg_data->mif.timer.data = (unsigned long)alg_data; + + /* Register I/O resource */ + if (!request_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE, + pdev->name)) { + dev_err(&pdev->dev, + "I/O region 0x%08x for I2C already in use.\n", + i2c_pnx->base); + ret = -ENODEV; + goto out_clkget; + } + + alg_data->ioaddr = ioremap(i2c_pnx->base, I2C_PNX_REGION_SIZE); + if (!alg_data->ioaddr) { + dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n"); + ret = -ENOMEM; + goto out_release; + } + + ret = clk_enable(alg_data->clk); + if (ret) + goto out_unmap; + + freq = clk_get_rate(alg_data->clk); + + /* + * Clock Divisor High This value is the number of system clocks + * the serial clock (SCL) will be high. + * For example, if the system clock period is 50 ns and the maximum + * desired serial period is 10000 ns (100 kHz), then CLKHI would be + * set to 0.5*(f_sys/f_i2c)-2=0.5*(20e6/100e3)-2=98. The actual value + * programmed into CLKHI will vary from this slightly due to + * variations in the output pad's rise and fall times as well as + * the deglitching filter length. + */ + + tmp = ((freq / 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2; + if (tmp > 0x3FF) + tmp = 0x3FF; + iowrite32(tmp, I2C_REG_CKH(alg_data)); + iowrite32(tmp, I2C_REG_CKL(alg_data)); + + iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data)); + if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) { + ret = -ENODEV; + goto out_clock; + } + init_completion(&alg_data->mif.complete); + + ret = request_irq(i2c_pnx->irq, i2c_pnx_interrupt, + 0, pdev->name, alg_data); + if (ret) + goto out_clock; + + /* Register this adapter with the I2C subsystem */ + ret = i2c_add_numbered_adapter(&alg_data->adapter); + if (ret < 0) { + dev_err(&pdev->dev, "I2C: Failed to add bus\n"); + goto out_irq; + } + + dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n", + alg_data->adapter.name, i2c_pnx->base, i2c_pnx->irq); + + return 0; + +out_irq: + free_irq(i2c_pnx->irq, alg_data); +out_clock: + clk_disable(alg_data->clk); +out_unmap: + iounmap(alg_data->ioaddr); +out_release: + release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE); +out_clkget: + clk_put(alg_data->clk); +out_drvdata: + kfree(alg_data); +err_kzalloc: + platform_set_drvdata(pdev, NULL); +out: + return ret; +} + +static int __devexit i2c_pnx_remove(struct platform_device *pdev) +{ + struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); + struct i2c_pnx_data *i2c_pnx = alg_data->i2c_pnx; + + free_irq(i2c_pnx->irq, alg_data); + i2c_del_adapter(&alg_data->adapter); + clk_disable(alg_data->clk); + iounmap(alg_data->ioaddr); + release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE); + clk_put(alg_data->clk); + kfree(alg_data); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver i2c_pnx_driver = { + .driver = { + .name = "pnx-i2c", + .owner = THIS_MODULE, + }, + .probe = i2c_pnx_probe, + .remove = __devexit_p(i2c_pnx_remove), + .suspend = i2c_pnx_controller_suspend, + .resume = i2c_pnx_controller_resume, +}; + +static int __init i2c_adap_pnx_init(void) +{ + return platform_driver_register(&i2c_pnx_driver); +} + +static void __exit i2c_adap_pnx_exit(void) +{ + platform_driver_unregister(&i2c_pnx_driver); +} + +MODULE_AUTHOR("Vitaly Wool, Dennis Kovalev <source@mvista.com>"); +MODULE_DESCRIPTION("I2C driver for Philips IP3204-based I2C busses"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pnx-i2c"); + +/* We need to make sure I2C is initialized before USB */ +subsys_initcall(i2c_adap_pnx_init); +module_exit(i2c_adap_pnx_exit); diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c new file mode 100644 index 00000000..b289ec99 --- /dev/null +++ b/drivers/i2c/busses/i2c-powermac.c @@ -0,0 +1,341 @@ +/* + i2c Support for Apple SMU Controller + + Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp. + <benh@kernel.crashing.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/kernel.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <asm/prom.h> +#include <asm/pmac_low_i2c.h> + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("I2C driver for Apple PowerMac"); +MODULE_LICENSE("GPL"); + +/* + * SMBUS-type transfer entrypoint + */ +static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, + u16 addr, + unsigned short flags, + char read_write, + u8 command, + int size, + union i2c_smbus_data* data) +{ + struct pmac_i2c_bus *bus = i2c_get_adapdata(adap); + int rc = 0; + int read = (read_write == I2C_SMBUS_READ); + int addrdir = (addr << 1) | read; + int mode, subsize, len; + u32 subaddr; + u8 *buf; + u8 local[2]; + + if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) { + mode = pmac_i2c_mode_std; + subsize = 0; + subaddr = 0; + } else { + mode = read ? pmac_i2c_mode_combined : pmac_i2c_mode_stdsub; + subsize = 1; + subaddr = command; + } + + switch (size) { + case I2C_SMBUS_QUICK: + buf = NULL; + len = 0; + break; + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + buf = &data->byte; + len = 1; + break; + case I2C_SMBUS_WORD_DATA: + if (!read) { + local[0] = data->word & 0xff; + local[1] = (data->word >> 8) & 0xff; + } + buf = local; + len = 2; + break; + + /* Note that these are broken vs. the expected smbus API where + * on reads, the length is actually returned from the function, + * but I think the current API makes no sense and I don't want + * any driver that I haven't verified for correctness to go + * anywhere near a pmac i2c bus anyway ... + * + * I'm also not completely sure what kind of phases to do between + * the actual command and the data (what I am _supposed_ to do that + * is). For now, I assume writes are a single stream and reads have + * a repeat start/addr phase (but not stop in between) + */ + case I2C_SMBUS_BLOCK_DATA: + buf = data->block; + len = data->block[0] + 1; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + buf = &data->block[1]; + len = data->block[0]; + break; + + default: + return -EINVAL; + } + + rc = pmac_i2c_open(bus, 0); + if (rc) { + dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc); + return rc; + } + + rc = pmac_i2c_setmode(bus, mode); + if (rc) { + dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n", + mode, rc); + goto bail; + } + + rc = pmac_i2c_xfer(bus, addrdir, subsize, subaddr, buf, len); + if (rc) { + if (rc == -ENXIO) + dev_dbg(&adap->dev, + "I2C transfer at 0x%02x failed, size %d, " + "err %d\n", addrdir >> 1, size, rc); + else + dev_err(&adap->dev, + "I2C transfer at 0x%02x failed, size %d, " + "err %d\n", addrdir >> 1, size, rc); + goto bail; + } + + if (size == I2C_SMBUS_WORD_DATA && read) { + data->word = ((u16)local[1]) << 8; + data->word |= local[0]; + } + + bail: + pmac_i2c_close(bus); + return rc; +} + +/* + * Generic i2c master transfer entrypoint. This driver only support single + * messages (for "lame i2c" transfers). Anything else should use the smbus + * entry point + */ +static int i2c_powermac_master_xfer( struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct pmac_i2c_bus *bus = i2c_get_adapdata(adap); + int rc = 0; + int read; + int addrdir; + + if (num != 1) { + dev_err(&adap->dev, + "Multi-message I2C transactions not supported\n"); + return -EOPNOTSUPP; + } + + if (msgs->flags & I2C_M_TEN) + return -EINVAL; + read = (msgs->flags & I2C_M_RD) != 0; + addrdir = (msgs->addr << 1) | read; + + rc = pmac_i2c_open(bus, 0); + if (rc) { + dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc); + return rc; + } + rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); + if (rc) { + dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n", + pmac_i2c_mode_std, rc); + goto bail; + } + rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len); + if (rc < 0) { + if (rc == -ENXIO) + dev_dbg(&adap->dev, "I2C %s 0x%02x failed, err %d\n", + addrdir & 1 ? "read from" : "write to", + addrdir >> 1, rc); + else + dev_err(&adap->dev, "I2C %s 0x%02x failed, err %d\n", + addrdir & 1 ? "read from" : "write to", + addrdir >> 1, rc); + } + bail: + pmac_i2c_close(bus); + return rc < 0 ? rc : 1; +} + +static u32 i2c_powermac_func(struct i2c_adapter * adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_I2C; +} + +/* For now, we only handle smbus */ +static const struct i2c_algorithm i2c_powermac_algorithm = { + .smbus_xfer = i2c_powermac_smbus_xfer, + .master_xfer = i2c_powermac_master_xfer, + .functionality = i2c_powermac_func, +}; + + +static int __devexit i2c_powermac_remove(struct platform_device *dev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(dev); + int rc; + + rc = i2c_del_adapter(adapter); + /* We aren't that prepared to deal with this... */ + if (rc) + printk(KERN_WARNING + "i2c-powermac.c: Failed to remove bus %s !\n", + adapter->name); + platform_set_drvdata(dev, NULL); + memset(adapter, 0, sizeof(*adapter)); + + return 0; +} + + +static int __devinit i2c_powermac_probe(struct platform_device *dev) +{ + struct pmac_i2c_bus *bus = dev->dev.platform_data; + struct device_node *parent = NULL; + struct i2c_adapter *adapter; + const char *basename; + int rc; + + if (bus == NULL) + return -EINVAL; + adapter = pmac_i2c_get_adapter(bus); + + /* Ok, now we need to make up a name for the interface that will + * match what we used to do in the past, that is basically the + * controller's parent device node for keywest. PMU didn't have a + * naming convention and SMU has a different one + */ + switch(pmac_i2c_get_type(bus)) { + case pmac_i2c_bus_keywest: + parent = of_get_parent(pmac_i2c_get_controller(bus)); + if (parent == NULL) + return -EINVAL; + basename = parent->name; + break; + case pmac_i2c_bus_pmu: + basename = "pmu"; + break; + case pmac_i2c_bus_smu: + /* This is not what we used to do but I'm fixing drivers at + * the same time as this change + */ + basename = "smu"; + break; + default: + return -EINVAL; + } + snprintf(adapter->name, sizeof(adapter->name), "%s %d", basename, + pmac_i2c_get_channel(bus)); + of_node_put(parent); + + platform_set_drvdata(dev, adapter); + adapter->algo = &i2c_powermac_algorithm; + i2c_set_adapdata(adapter, bus); + adapter->dev.parent = &dev->dev; + rc = i2c_add_adapter(adapter); + if (rc) { + printk(KERN_ERR "i2c-powermac: Adapter %s registration " + "failed\n", adapter->name); + memset(adapter, 0, sizeof(*adapter)); + } + + printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name); + + if (!strncmp(basename, "uni-n", 5)) { + struct device_node *np; + const u32 *prop; + struct i2c_board_info info; + + /* Instantiate I2C motion sensor if present */ + np = of_find_node_by_name(NULL, "accelerometer"); + if (np && of_device_is_compatible(np, "AAPL,accelerometer_1") && + (prop = of_get_property(np, "reg", NULL))) { + int i2c_bus; + const char *tmp_bus; + + /* look for bus either using "reg" or by path */ + tmp_bus = strstr(np->full_name, "/i2c-bus@"); + if (tmp_bus) + i2c_bus = *(tmp_bus + 9) - '0'; + else + i2c_bus = ((*prop) >> 8) & 0x0f; + + if (pmac_i2c_get_channel(bus) == i2c_bus) { + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = ((*prop) & 0xff) >> 1; + strlcpy(info.type, "ams", I2C_NAME_SIZE); + i2c_new_device(adapter, &info); + } + } + } + + return rc; +} + + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:i2c-powermac"); + +static struct platform_driver i2c_powermac_driver = { + .probe = i2c_powermac_probe, + .remove = __devexit_p(i2c_powermac_remove), + .driver = { + .name = "i2c-powermac", + .bus = &platform_bus_type, + }, +}; + +static int __init i2c_powermac_init(void) +{ + platform_driver_register(&i2c_powermac_driver); + return 0; +} + + +static void __exit i2c_powermac_cleanup(void) +{ + platform_driver_unregister(&i2c_powermac_driver); +} + +module_init(i2c_powermac_init); +module_exit(i2c_powermac_cleanup); diff --git a/drivers/i2c/busses/i2c-puv3.c b/drivers/i2c/busses/i2c-puv3.c new file mode 100644 index 00000000..fac67394 --- /dev/null +++ b/drivers/i2c/busses/i2c-puv3.c @@ -0,0 +1,306 @@ +/* + * I2C driver for PKUnity-v3 SoC + * Code specific to PKUnity SoC and UniCore ISA + * + * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> + * Copyright (C) 2001-2010 Guan Xuetao + * + * 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <mach/hardware.h> + +/* + * Poll the i2c status register until the specified bit is set. + * Returns 0 if timed out (100 msec). + */ +static short poll_status(unsigned long bit) +{ + int loop_cntr = 1000; + + if (bit & I2C_STATUS_TFNF) { + do { + udelay(10); + } while (!(readl(I2C_STATUS) & bit) && (--loop_cntr > 0)); + } else { + /* RXRDY handler */ + do { + if (readl(I2C_TAR) == I2C_TAR_EEPROM) + msleep(20); + else + udelay(10); + } while (!(readl(I2C_RXFLR) & 0xf) && (--loop_cntr > 0)); + } + + return (loop_cntr > 0); +} + +static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length) +{ + int i2c_reg = *buf; + + /* Read data */ + while (length--) { + if (!poll_status(I2C_STATUS_TFNF)) { + dev_dbg(&adap->dev, "Tx FIFO Not Full timeout\n"); + return -ETIMEDOUT; + } + + /* send addr */ + writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD); + + /* get ready to next write */ + i2c_reg++; + + /* send read CMD */ + writel(I2C_DATACMD_READ, I2C_DATACMD); + + /* wait until the Rx FIFO have available */ + if (!poll_status(I2C_STATUS_RFNE)) { + dev_dbg(&adap->dev, "RXRDY timeout\n"); + return -ETIMEDOUT; + } + + /* read the data to buf */ + *buf = (readl(I2C_DATACMD) & I2C_DATACMD_DAT_MASK); + buf++; + } + + return 0; +} + +static int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length) +{ + int i2c_reg = *buf; + + /* Do nothing but storing the reg_num to a static variable */ + if (i2c_reg == -1) { + printk(KERN_WARNING "Error i2c reg\n"); + return -ETIMEDOUT; + } + + if (length == 1) + return 0; + + buf++; + length--; + while (length--) { + /* send addr */ + writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD); + + /* send write CMD */ + writel(*buf | I2C_DATACMD_WRITE, I2C_DATACMD); + + /* wait until the Rx FIFO have available */ + msleep(20); + + /* read the data to buf */ + i2c_reg++; + buf++; + } + + return 0; +} + +/* + * Generic i2c master transfer entrypoint. + * + */ +static int puv3_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, + int num) +{ + int i, ret; + unsigned char swap; + + /* Disable i2c */ + writel(I2C_ENABLE_DISABLE, I2C_ENABLE); + + /* Set the work mode and speed*/ + writel(I2C_CON_MASTER | I2C_CON_SPEED_STD | I2C_CON_SLAVEDISABLE, I2C_CON); + + writel(pmsg->addr, I2C_TAR); + + /* Enable i2c */ + writel(I2C_ENABLE_ENABLE, I2C_ENABLE); + + dev_dbg(&adap->dev, "puv3_i2c_xfer: processing %d messages:\n", num); + + for (i = 0; i < num; i++) { + dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i, + pmsg->flags & I2C_M_RD ? "read" : "writ", + pmsg->len, pmsg->len > 1 ? "s" : "", + pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr); + + if (pmsg->len && pmsg->buf) { /* sanity check */ + if (pmsg->flags & I2C_M_RD) + ret = xfer_read(adap, pmsg->buf, pmsg->len); + else + ret = xfer_write(adap, pmsg->buf, pmsg->len); + + if (ret) + return ret; + + } + dev_dbg(&adap->dev, "transfer complete\n"); + pmsg++; /* next message */ + } + + /* XXX: fixup be16_to_cpu in bq27x00_battery.c */ + if (pmsg->addr == I2C_TAR_PWIC) { + swap = pmsg->buf[0]; + pmsg->buf[0] = pmsg->buf[1]; + pmsg->buf[1] = swap; + } + + return i; +} + +/* + * Return list of supported functionality. + */ +static u32 puv3_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm puv3_i2c_algorithm = { + .master_xfer = puv3_i2c_xfer, + .functionality = puv3_i2c_func, +}; + +/* + * Main initialization routine. + */ +static int __devinit puv3_i2c_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adapter; + struct resource *mem; + int rc; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return -ENODEV; + + if (!request_mem_region(mem->start, resource_size(mem), "puv3_i2c")) + return -EBUSY; + + adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (adapter == NULL) { + dev_err(&pdev->dev, "can't allocate inteface!\n"); + rc = -ENOMEM; + goto fail_nomem; + } + snprintf(adapter->name, sizeof(adapter->name), "PUV3-I2C at 0x%08x", + mem->start); + adapter->algo = &puv3_i2c_algorithm; + adapter->class = I2C_CLASS_HWMON; + adapter->dev.parent = &pdev->dev; + + platform_set_drvdata(pdev, adapter); + + adapter->nr = pdev->id; + rc = i2c_add_numbered_adapter(adapter); + if (rc) { + dev_err(&pdev->dev, "Adapter '%s' registration failed\n", + adapter->name); + goto fail_add_adapter; + } + + dev_info(&pdev->dev, "PKUnity v3 i2c bus adapter.\n"); + return 0; + +fail_add_adapter: + platform_set_drvdata(pdev, NULL); + kfree(adapter); +fail_nomem: + release_mem_region(mem->start, resource_size(mem)); + + return rc; +} + +static int __devexit puv3_i2c_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct resource *mem; + int rc; + + rc = i2c_del_adapter(adapter); + if (rc) { + dev_err(&pdev->dev, "Adapter '%s' delete fail\n", + adapter->name); + return rc; + } + + put_device(&pdev->dev); + platform_set_drvdata(pdev, NULL); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + + return rc; +} + +#ifdef CONFIG_PM +static int puv3_i2c_suspend(struct platform_device *dev, pm_message_t state) +{ + int poll_count; + /* Disable the IIC */ + writel(I2C_ENABLE_DISABLE, I2C_ENABLE); + for (poll_count = 0; poll_count < 50; poll_count++) { + if (readl(I2C_ENSTATUS) & I2C_ENSTATUS_ENABLE) + udelay(25); + } + + return 0; +} + +static int puv3_i2c_resume(struct platform_device *dev) +{ + return 0 ; +} +#else +#define puv3_i2c_suspend NULL +#define puv3_i2c_resume NULL +#endif + +MODULE_ALIAS("platform:puv3_i2c"); + +static struct platform_driver puv3_i2c_driver = { + .probe = puv3_i2c_probe, + .remove = __devexit_p(puv3_i2c_remove), + .suspend = puv3_i2c_suspend, + .resume = puv3_i2c_resume, + .driver = { + .name = "PKUnity-v3-I2C", + .owner = THIS_MODULE, + } +}; + +static int __init puv3_i2c_init(void) +{ + return platform_driver_register(&puv3_i2c_driver); +} + +static void __exit puv3_i2c_exit(void) +{ + platform_driver_unregister(&puv3_i2c_driver); +} + +module_init(puv3_i2c_init); +module_exit(puv3_i2c_exit); + +MODULE_DESCRIPTION("PKUnity v3 I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c new file mode 100644 index 00000000..6659d269 --- /dev/null +++ b/drivers/i2c/busses/i2c-pxa-pci.c @@ -0,0 +1,176 @@ +/* + * The CE4100's I2C device is more or less the same one as found on PXA. + * It does not support slave mode, the register slightly moved. This PCI + * device provides three bars, every contains a single I2C controller. + */ +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/i2c/pxa-i2c.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> + +#define CE4100_PCI_I2C_DEVS 3 + +struct ce4100_devices { + struct platform_device *pdev[CE4100_PCI_I2C_DEVS]; +}; + +static struct platform_device *add_i2c_device(struct pci_dev *dev, int bar) +{ + struct platform_device *pdev; + struct i2c_pxa_platform_data pdata; + struct resource res[2]; + struct device_node *child; + static int devnum; + int ret; + + memset(&pdata, 0, sizeof(struct i2c_pxa_platform_data)); + memset(&res, 0, sizeof(res)); + + res[0].flags = IORESOURCE_MEM; + res[0].start = pci_resource_start(dev, bar); + res[0].end = pci_resource_end(dev, bar); + + res[1].flags = IORESOURCE_IRQ; + res[1].start = dev->irq; + res[1].end = dev->irq; + + for_each_child_of_node(dev->dev.of_node, child) { + const void *prop; + struct resource r; + int ret; + + ret = of_address_to_resource(child, 0, &r); + if (ret < 0) + continue; + if (r.start != res[0].start) + continue; + if (r.end != res[0].end) + continue; + if (r.flags != res[0].flags) + continue; + + prop = of_get_property(child, "fast-mode", NULL); + if (prop) + pdata.fast_mode = 1; + + break; + } + + if (!child) { + dev_err(&dev->dev, "failed to match a DT node for bar %d.\n", + bar); + ret = -EINVAL; + goto out; + } + + pdev = platform_device_alloc("ce4100-i2c", devnum); + if (!pdev) { + of_node_put(child); + ret = -ENOMEM; + goto out; + } + pdev->dev.parent = &dev->dev; + pdev->dev.of_node = child; + + ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res)); + if (ret) + goto err; + + ret = platform_device_add_data(pdev, &pdata, sizeof(pdata)); + if (ret) + goto err; + + ret = platform_device_add(pdev); + if (ret) + goto err; + devnum++; + return pdev; +err: + platform_device_put(pdev); +out: + return ERR_PTR(ret); +} + +static int __devinit ce4100_i2c_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + int ret; + int i; + struct ce4100_devices *sds; + + ret = pci_enable_device_mem(dev); + if (ret) + return ret; + + if (!dev->dev.of_node) { + dev_err(&dev->dev, "Missing device tree node.\n"); + return -EINVAL; + } + sds = kzalloc(sizeof(*sds), GFP_KERNEL); + if (!sds) + goto err_mem; + + for (i = 0; i < ARRAY_SIZE(sds->pdev); i++) { + sds->pdev[i] = add_i2c_device(dev, i); + if (IS_ERR(sds->pdev[i])) { + while (--i >= 0) + platform_device_unregister(sds->pdev[i]); + goto err_dev_add; + } + } + pci_set_drvdata(dev, sds); + return 0; + +err_dev_add: + pci_set_drvdata(dev, NULL); + kfree(sds); +err_mem: + pci_disable_device(dev); + return ret; +} + +static void __devexit ce4100_i2c_remove(struct pci_dev *dev) +{ + struct ce4100_devices *sds; + unsigned int i; + + sds = pci_get_drvdata(dev); + pci_set_drvdata(dev, NULL); + + for (i = 0; i < ARRAY_SIZE(sds->pdev); i++) + platform_device_unregister(sds->pdev[i]); + + pci_disable_device(dev); + kfree(sds); +} + +static struct pci_device_id ce4100_i2c_devices[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)}, + { }, +}; +MODULE_DEVICE_TABLE(pci, ce4100_i2c_devices); + +static struct pci_driver ce4100_i2c_driver = { + .name = "ce4100_i2c", + .id_table = ce4100_i2c_devices, + .probe = ce4100_i2c_probe, + .remove = __devexit_p(ce4100_i2c_remove), +}; + +static int __init ce4100_i2c_init(void) +{ + return pci_register_driver(&ce4100_i2c_driver); +} +module_init(ce4100_i2c_init); + +static void __exit ce4100_i2c_exit(void) +{ + pci_unregister_driver(&ce4100_i2c_driver); +} +module_exit(ce4100_i2c_exit); + +MODULE_DESCRIPTION("CE4100 PCI-I2C glue code for PXA's driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>"); diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c new file mode 100644 index 00000000..f59224a5 --- /dev/null +++ b/drivers/i2c/busses/i2c-pxa.c @@ -0,0 +1,1258 @@ +/* + * i2c_adap_pxa.c + * + * I2C adapter for the PXA I2C bus access. + * + * Copyright (C) 2002 Intrinsyc Software Inc. + * Copyright (C) 2004-2005 Deep Blue Solutions 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. + * + * History: + * Apr 2002: Initial version [CS] + * Jun 2002: Properly separated algo/adap [FB] + * Jan 2003: Fixed several bugs concerning interrupt handling [Kai-Uwe Bloem] + * Jan 2003: added limited signal handling [Kai-Uwe Bloem] + * Sep 2004: Major rework to ensure efficient bus handling [RMK] + * Dec 2004: Added support for PXA27x and slave device probing [Liam Girdwood] + * Feb 2005: Rework slave mode handling [RMK] + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/i2c-pxa.h> +#include <linux/of_i2c.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/i2c/pxa-i2c.h> + +#include <asm/irq.h> + +#ifndef CONFIG_HAVE_CLK +#define clk_get(dev, id) NULL +#define clk_put(clk) do { } while (0) +#define clk_disable(clk) do { } while (0) +#define clk_enable(clk) do { } while (0) +#endif + +struct pxa_reg_layout { + u32 ibmr; + u32 idbr; + u32 icr; + u32 isr; + u32 isar; +}; + +enum pxa_i2c_types { + REGS_PXA2XX, + REGS_PXA3XX, + REGS_CE4100, +}; + +/* + * I2C registers definitions + */ +static struct pxa_reg_layout pxa_reg_layout[] = { + [REGS_PXA2XX] = { + .ibmr = 0x00, + .idbr = 0x08, + .icr = 0x10, + .isr = 0x18, + .isar = 0x20, + }, + [REGS_PXA3XX] = { + .ibmr = 0x00, + .idbr = 0x04, + .icr = 0x08, + .isr = 0x0c, + .isar = 0x10, + }, + [REGS_CE4100] = { + .ibmr = 0x14, + .idbr = 0x0c, + .icr = 0x00, + .isr = 0x04, + /* no isar register */ + }, +}; + +static const struct platform_device_id i2c_pxa_id_table[] = { + { "pxa2xx-i2c", REGS_PXA2XX }, + { "pxa3xx-pwri2c", REGS_PXA3XX }, + { "ce4100-i2c", REGS_CE4100 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table); + +/* + * I2C bit definitions + */ + +#define ICR_START (1 << 0) /* start bit */ +#define ICR_STOP (1 << 1) /* stop bit */ +#define ICR_ACKNAK (1 << 2) /* send ACK(0) or NAK(1) */ +#define ICR_TB (1 << 3) /* transfer byte bit */ +#define ICR_MA (1 << 4) /* master abort */ +#define ICR_SCLE (1 << 5) /* master clock enable */ +#define ICR_IUE (1 << 6) /* unit enable */ +#define ICR_GCD (1 << 7) /* general call disable */ +#define ICR_ITEIE (1 << 8) /* enable tx interrupts */ +#define ICR_IRFIE (1 << 9) /* enable rx interrupts */ +#define ICR_BEIE (1 << 10) /* enable bus error ints */ +#define ICR_SSDIE (1 << 11) /* slave STOP detected int enable */ +#define ICR_ALDIE (1 << 12) /* enable arbitration interrupt */ +#define ICR_SADIE (1 << 13) /* slave address detected int enable */ +#define ICR_UR (1 << 14) /* unit reset */ +#define ICR_FM (1 << 15) /* fast mode */ + +#define ISR_RWM (1 << 0) /* read/write mode */ +#define ISR_ACKNAK (1 << 1) /* ack/nak status */ +#define ISR_UB (1 << 2) /* unit busy */ +#define ISR_IBB (1 << 3) /* bus busy */ +#define ISR_SSD (1 << 4) /* slave stop detected */ +#define ISR_ALD (1 << 5) /* arbitration loss detected */ +#define ISR_ITE (1 << 6) /* tx buffer empty */ +#define ISR_IRF (1 << 7) /* rx buffer full */ +#define ISR_GCAD (1 << 8) /* general call address detected */ +#define ISR_SAD (1 << 9) /* slave address detected */ +#define ISR_BED (1 << 10) /* bus error no ACK/NAK */ + +struct pxa_i2c { + spinlock_t lock; + wait_queue_head_t wait; + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int msg_idx; + unsigned int msg_ptr; + unsigned int slave_addr; + + struct i2c_adapter adap; + struct clk *clk; +#ifdef CONFIG_I2C_PXA_SLAVE + struct i2c_slave_client *slave; +#endif + + unsigned int irqlogidx; + u32 isrlog[32]; + u32 icrlog[32]; + + void __iomem *reg_base; + void __iomem *reg_ibmr; + void __iomem *reg_idbr; + void __iomem *reg_icr; + void __iomem *reg_isr; + void __iomem *reg_isar; + + unsigned long iobase; + unsigned long iosize; + + int irq; + unsigned int use_pio :1; + unsigned int fast_mode :1; +}; + +#define _IBMR(i2c) ((i2c)->reg_ibmr) +#define _IDBR(i2c) ((i2c)->reg_idbr) +#define _ICR(i2c) ((i2c)->reg_icr) +#define _ISR(i2c) ((i2c)->reg_isr) +#define _ISAR(i2c) ((i2c)->reg_isar) + +/* + * I2C Slave mode address + */ +#define I2C_PXA_SLAVE_ADDR 0x1 + +#ifdef DEBUG + +struct bits { + u32 mask; + const char *set; + const char *unset; +}; +#define PXA_BIT(m, s, u) { .mask = m, .set = s, .unset = u } + +static inline void +decode_bits(const char *prefix, const struct bits *bits, int num, u32 val) +{ + printk("%s %08x: ", prefix, val); + while (num--) { + const char *str = val & bits->mask ? bits->set : bits->unset; + if (str) + printk("%s ", str); + bits++; + } +} + +static const struct bits isr_bits[] = { + PXA_BIT(ISR_RWM, "RX", "TX"), + PXA_BIT(ISR_ACKNAK, "NAK", "ACK"), + PXA_BIT(ISR_UB, "Bsy", "Rdy"), + PXA_BIT(ISR_IBB, "BusBsy", "BusRdy"), + PXA_BIT(ISR_SSD, "SlaveStop", NULL), + PXA_BIT(ISR_ALD, "ALD", NULL), + PXA_BIT(ISR_ITE, "TxEmpty", NULL), + PXA_BIT(ISR_IRF, "RxFull", NULL), + PXA_BIT(ISR_GCAD, "GenCall", NULL), + PXA_BIT(ISR_SAD, "SlaveAddr", NULL), + PXA_BIT(ISR_BED, "BusErr", NULL), +}; + +static void decode_ISR(unsigned int val) +{ + decode_bits(KERN_DEBUG "ISR", isr_bits, ARRAY_SIZE(isr_bits), val); + printk("\n"); +} + +static const struct bits icr_bits[] = { + PXA_BIT(ICR_START, "START", NULL), + PXA_BIT(ICR_STOP, "STOP", NULL), + PXA_BIT(ICR_ACKNAK, "ACKNAK", NULL), + PXA_BIT(ICR_TB, "TB", NULL), + PXA_BIT(ICR_MA, "MA", NULL), + PXA_BIT(ICR_SCLE, "SCLE", "scle"), + PXA_BIT(ICR_IUE, "IUE", "iue"), + PXA_BIT(ICR_GCD, "GCD", NULL), + PXA_BIT(ICR_ITEIE, "ITEIE", NULL), + PXA_BIT(ICR_IRFIE, "IRFIE", NULL), + PXA_BIT(ICR_BEIE, "BEIE", NULL), + PXA_BIT(ICR_SSDIE, "SSDIE", NULL), + PXA_BIT(ICR_ALDIE, "ALDIE", NULL), + PXA_BIT(ICR_SADIE, "SADIE", NULL), + PXA_BIT(ICR_UR, "UR", "ur"), +}; + +#ifdef CONFIG_I2C_PXA_SLAVE +static void decode_ICR(unsigned int val) +{ + decode_bits(KERN_DEBUG "ICR", icr_bits, ARRAY_SIZE(icr_bits), val); + printk("\n"); +} +#endif + +static unsigned int i2c_debug = DEBUG; + +static void i2c_pxa_show_state(struct pxa_i2c *i2c, int lno, const char *fname) +{ + dev_dbg(&i2c->adap.dev, "state:%s:%d: ISR=%08x, ICR=%08x, IBMR=%02x\n", fname, lno, + readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); +} + +#define show_state(i2c) i2c_pxa_show_state(i2c, __LINE__, __func__) + +static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) +{ + unsigned int i; + printk(KERN_ERR "i2c: error: %s\n", why); + printk(KERN_ERR "i2c: msg_num: %d msg_idx: %d msg_ptr: %d\n", + i2c->msg_num, i2c->msg_idx, i2c->msg_ptr); + printk(KERN_ERR "i2c: ICR: %08x ISR: %08x\n", + readl(_ICR(i2c)), readl(_ISR(i2c))); + printk(KERN_DEBUG "i2c: log: "); + for (i = 0; i < i2c->irqlogidx; i++) + printk("[%08x:%08x] ", i2c->isrlog[i], i2c->icrlog[i]); + printk("\n"); +} + +#else /* ifdef DEBUG */ + +#define i2c_debug 0 + +#define show_state(i2c) do { } while (0) +#define decode_ISR(val) do { } while (0) +#define decode_ICR(val) do { } while (0) +#define i2c_pxa_scream_blue_murder(i2c, why) do { } while (0) + +#endif /* ifdef DEBUG / else */ + +static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret); +static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id); + +static inline int i2c_pxa_is_slavemode(struct pxa_i2c *i2c) +{ + return !(readl(_ICR(i2c)) & ICR_SCLE); +} + +static void i2c_pxa_abort(struct pxa_i2c *i2c) +{ + int i = 250; + + if (i2c_pxa_is_slavemode(i2c)) { + dev_dbg(&i2c->adap.dev, "%s: called in slave mode\n", __func__); + return; + } + + while ((i > 0) && (readl(_IBMR(i2c)) & 0x1) == 0) { + unsigned long icr = readl(_ICR(i2c)); + + icr &= ~ICR_START; + icr |= ICR_ACKNAK | ICR_STOP | ICR_TB; + + writel(icr, _ICR(i2c)); + + show_state(i2c); + + mdelay(1); + i --; + } + + writel(readl(_ICR(i2c)) & ~(ICR_MA | ICR_START | ICR_STOP), + _ICR(i2c)); +} + +static int i2c_pxa_wait_bus_not_busy(struct pxa_i2c *i2c) +{ + int timeout = DEF_TIMEOUT; + + while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB)) { + if ((readl(_ISR(i2c)) & ISR_SAD) != 0) + timeout += 4; + + msleep(2); + show_state(i2c); + } + + if (timeout < 0) + show_state(i2c); + + return timeout < 0 ? I2C_RETRY : 0; +} + +static int i2c_pxa_wait_master(struct pxa_i2c *i2c) +{ + unsigned long timeout = jiffies + HZ*4; + + while (time_before(jiffies, timeout)) { + if (i2c_debug > 1) + dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", + __func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); + + if (readl(_ISR(i2c)) & ISR_SAD) { + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "%s: Slave detected\n", __func__); + goto out; + } + + /* wait for unit and bus being not busy, and we also do a + * quick check of the i2c lines themselves to ensure they've + * gone high... + */ + if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) == 0 && readl(_IBMR(i2c)) == 3) { + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "%s: done\n", __func__); + return 1; + } + + msleep(1); + } + + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "%s: did not free\n", __func__); + out: + return 0; +} + +static int i2c_pxa_set_master(struct pxa_i2c *i2c) +{ + if (i2c_debug) + dev_dbg(&i2c->adap.dev, "setting to bus master\n"); + + if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) != 0) { + dev_dbg(&i2c->adap.dev, "%s: unit is busy\n", __func__); + if (!i2c_pxa_wait_master(i2c)) { + dev_dbg(&i2c->adap.dev, "%s: error: unit busy\n", __func__); + return I2C_RETRY; + } + } + + writel(readl(_ICR(i2c)) | ICR_SCLE, _ICR(i2c)); + return 0; +} + +#ifdef CONFIG_I2C_PXA_SLAVE +static int i2c_pxa_wait_slave(struct pxa_i2c *i2c) +{ + unsigned long timeout = jiffies + HZ*1; + + /* wait for stop */ + + show_state(i2c); + + while (time_before(jiffies, timeout)) { + if (i2c_debug > 1) + dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", + __func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); + + if ((readl(_ISR(i2c)) & (ISR_UB|ISR_IBB)) == 0 || + (readl(_ISR(i2c)) & ISR_SAD) != 0 || + (readl(_ICR(i2c)) & ICR_SCLE) == 0) { + if (i2c_debug > 1) + dev_dbg(&i2c->adap.dev, "%s: done\n", __func__); + return 1; + } + + msleep(1); + } + + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "%s: did not free\n", __func__); + return 0; +} + +/* + * clear the hold on the bus, and take of anything else + * that has been configured + */ +static void i2c_pxa_set_slave(struct pxa_i2c *i2c, int errcode) +{ + show_state(i2c); + + if (errcode < 0) { + udelay(100); /* simple delay */ + } else { + /* we need to wait for the stop condition to end */ + + /* if we where in stop, then clear... */ + if (readl(_ICR(i2c)) & ICR_STOP) { + udelay(100); + writel(readl(_ICR(i2c)) & ~ICR_STOP, _ICR(i2c)); + } + + if (!i2c_pxa_wait_slave(i2c)) { + dev_err(&i2c->adap.dev, "%s: wait timedout\n", + __func__); + return; + } + } + + writel(readl(_ICR(i2c)) & ~(ICR_STOP|ICR_ACKNAK|ICR_MA), _ICR(i2c)); + writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c)); + + if (i2c_debug) { + dev_dbg(&i2c->adap.dev, "ICR now %08x, ISR %08x\n", readl(_ICR(i2c)), readl(_ISR(i2c))); + decode_ICR(readl(_ICR(i2c))); + } +} +#else +#define i2c_pxa_set_slave(i2c, err) do { } while (0) +#endif + +static void i2c_pxa_reset(struct pxa_i2c *i2c) +{ + pr_debug("Resetting I2C Controller Unit\n"); + + /* abort any transfer currently under way */ + i2c_pxa_abort(i2c); + + /* reset according to 9.8 */ + writel(ICR_UR, _ICR(i2c)); + writel(I2C_ISR_INIT, _ISR(i2c)); + writel(readl(_ICR(i2c)) & ~ICR_UR, _ICR(i2c)); + + if (i2c->reg_isar) + writel(i2c->slave_addr, _ISAR(i2c)); + + /* set control register values */ + writel(I2C_ICR_INIT | (i2c->fast_mode ? ICR_FM : 0), _ICR(i2c)); + +#ifdef CONFIG_I2C_PXA_SLAVE + dev_info(&i2c->adap.dev, "Enabling slave mode\n"); + writel(readl(_ICR(i2c)) | ICR_SADIE | ICR_ALDIE | ICR_SSDIE, _ICR(i2c)); +#endif + + i2c_pxa_set_slave(i2c, 0); + + /* enable unit */ + writel(readl(_ICR(i2c)) | ICR_IUE, _ICR(i2c)); + udelay(100); +} + + +#ifdef CONFIG_I2C_PXA_SLAVE +/* + * PXA I2C Slave mode + */ + +static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr) +{ + if (isr & ISR_BED) { + /* what should we do here? */ + } else { + int ret = 0; + + if (i2c->slave != NULL) + ret = i2c->slave->read(i2c->slave->data); + + writel(ret, _IDBR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); /* allow next byte */ + } +} + +static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr) +{ + unsigned int byte = readl(_IDBR(i2c)); + + if (i2c->slave != NULL) + i2c->slave->write(i2c->slave->data, byte); + + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); +} + +static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) +{ + int timeout; + + if (i2c_debug > 0) + dev_dbg(&i2c->adap.dev, "SAD, mode is slave-%cx\n", + (isr & ISR_RWM) ? 'r' : 't'); + + if (i2c->slave != NULL) + i2c->slave->event(i2c->slave->data, + (isr & ISR_RWM) ? I2C_SLAVE_EVENT_START_READ : I2C_SLAVE_EVENT_START_WRITE); + + /* + * slave could interrupt in the middle of us generating a + * start condition... if this happens, we'd better back off + * and stop holding the poor thing up + */ + writel(readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP), _ICR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); + + timeout = 0x10000; + + while (1) { + if ((readl(_IBMR(i2c)) & 2) == 2) + break; + + timeout--; + + if (timeout <= 0) { + dev_err(&i2c->adap.dev, "timeout waiting for SCL high\n"); + break; + } + } + + writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c)); +} + +static void i2c_pxa_slave_stop(struct pxa_i2c *i2c) +{ + if (i2c_debug > 2) + dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop)\n"); + + if (i2c->slave != NULL) + i2c->slave->event(i2c->slave->data, I2C_SLAVE_EVENT_STOP); + + if (i2c_debug > 2) + dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop) acked\n"); + + /* + * If we have a master-mode message waiting, + * kick it off now that the slave has completed. + */ + if (i2c->msg) + i2c_pxa_master_complete(i2c, I2C_RETRY); +} +#else +static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr) +{ + if (isr & ISR_BED) { + /* what should we do here? */ + } else { + writel(0, _IDBR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); + } +} + +static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr) +{ + writel(readl(_ICR(i2c)) | ICR_TB | ICR_ACKNAK, _ICR(i2c)); +} + +static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) +{ + int timeout; + + /* + * slave could interrupt in the middle of us generating a + * start condition... if this happens, we'd better back off + * and stop holding the poor thing up + */ + writel(readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP), _ICR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB | ICR_ACKNAK, _ICR(i2c)); + + timeout = 0x10000; + + while (1) { + if ((readl(_IBMR(i2c)) & 2) == 2) + break; + + timeout--; + + if (timeout <= 0) { + dev_err(&i2c->adap.dev, "timeout waiting for SCL high\n"); + break; + } + } + + writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c)); +} + +static void i2c_pxa_slave_stop(struct pxa_i2c *i2c) +{ + if (i2c->msg) + i2c_pxa_master_complete(i2c, I2C_RETRY); +} +#endif + +/* + * PXA I2C Master mode + */ + +static inline unsigned int i2c_pxa_addr_byte(struct i2c_msg *msg) +{ + unsigned int addr = (msg->addr & 0x7f) << 1; + + if (msg->flags & I2C_M_RD) + addr |= 1; + + return addr; +} + +static inline void i2c_pxa_start_message(struct pxa_i2c *i2c) +{ + u32 icr; + + /* + * Step 1: target slave address into IDBR + */ + writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c)); + + /* + * Step 2: initiate the write. + */ + icr = readl(_ICR(i2c)) & ~(ICR_STOP | ICR_ALDIE); + writel(icr | ICR_START | ICR_TB, _ICR(i2c)); +} + +static inline void i2c_pxa_stop_message(struct pxa_i2c *i2c) +{ + u32 icr; + + /* + * Clear the STOP and ACK flags + */ + icr = readl(_ICR(i2c)); + icr &= ~(ICR_STOP | ICR_ACKNAK); + writel(icr, _ICR(i2c)); +} + +static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c) +{ + /* make timeout the same as for interrupt based functions */ + long timeout = 2 * DEF_TIMEOUT; + + /* + * Wait for the bus to become free. + */ + while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB)) { + udelay(1000); + show_state(i2c); + } + + if (timeout < 0) { + show_state(i2c); + dev_err(&i2c->adap.dev, + "i2c_pxa: timeout waiting for bus free\n"); + return I2C_RETRY; + } + + /* + * Set master mode. + */ + writel(readl(_ICR(i2c)) | ICR_SCLE, _ICR(i2c)); + + return 0; +} + +static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c, + struct i2c_msg *msg, int num) +{ + unsigned long timeout = 500000; /* 5 seconds */ + int ret = 0; + + ret = i2c_pxa_pio_set_master(i2c); + if (ret) + goto out; + + i2c->msg = msg; + i2c->msg_num = num; + i2c->msg_idx = 0; + i2c->msg_ptr = 0; + i2c->irqlogidx = 0; + + i2c_pxa_start_message(i2c); + + while (i2c->msg_num > 0 && --timeout) { + i2c_pxa_handler(0, i2c); + udelay(10); + } + + i2c_pxa_stop_message(i2c); + + /* + * We place the return code in i2c->msg_idx. + */ + ret = i2c->msg_idx; + +out: + if (timeout == 0) + i2c_pxa_scream_blue_murder(i2c, "timeout"); + + return ret; +} + +/* + * We are protected by the adapter bus mutex. + */ +static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num) +{ + long timeout; + int ret; + + /* + * Wait for the bus to become free. + */ + ret = i2c_pxa_wait_bus_not_busy(i2c); + if (ret) { + dev_err(&i2c->adap.dev, "i2c_pxa: timeout waiting for bus free\n"); + goto out; + } + + /* + * Set master mode. + */ + ret = i2c_pxa_set_master(i2c); + if (ret) { + dev_err(&i2c->adap.dev, "i2c_pxa_set_master: error %d\n", ret); + goto out; + } + + spin_lock_irq(&i2c->lock); + + i2c->msg = msg; + i2c->msg_num = num; + i2c->msg_idx = 0; + i2c->msg_ptr = 0; + i2c->irqlogidx = 0; + + i2c_pxa_start_message(i2c); + + spin_unlock_irq(&i2c->lock); + + /* + * The rest of the processing occurs in the interrupt handler. + */ + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); + i2c_pxa_stop_message(i2c); + + /* + * We place the return code in i2c->msg_idx. + */ + ret = i2c->msg_idx; + + if (!timeout && i2c->msg_num) { + i2c_pxa_scream_blue_murder(i2c, "timeout"); + ret = I2C_RETRY; + } + + out: + return ret; +} + +static int i2c_pxa_pio_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct pxa_i2c *i2c = adap->algo_data; + int ret, i; + + /* If the I2C controller is disabled we need to reset it + (probably due to a suspend/resume destroying state). We do + this here as we can then avoid worrying about resuming the + controller before its users. */ + if (!(readl(_ICR(i2c)) & ICR_IUE)) + i2c_pxa_reset(i2c); + + for (i = adap->retries; i >= 0; i--) { + ret = i2c_pxa_do_pio_xfer(i2c, msgs, num); + if (ret != I2C_RETRY) + goto out; + + if (i2c_debug) + dev_dbg(&adap->dev, "Retrying transmission\n"); + udelay(100); + } + i2c_pxa_scream_blue_murder(i2c, "exhausted retries"); + ret = -EREMOTEIO; + out: + i2c_pxa_set_slave(i2c, ret); + return ret; +} + +/* + * i2c_pxa_master_complete - complete the message and wake up. + */ +static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret) +{ + i2c->msg_ptr = 0; + i2c->msg = NULL; + i2c->msg_idx ++; + i2c->msg_num = 0; + if (ret) + i2c->msg_idx = ret; + if (!i2c->use_pio) + wake_up(&i2c->wait); +} + +static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) +{ + u32 icr = readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); + + again: + /* + * If ISR_ALD is set, we lost arbitration. + */ + if (isr & ISR_ALD) { + /* + * Do we need to do anything here? The PXA docs + * are vague about what happens. + */ + i2c_pxa_scream_blue_murder(i2c, "ALD set"); + + /* + * We ignore this error. We seem to see spurious ALDs + * for seemingly no reason. If we handle them as I think + * they should, we end up causing an I2C error, which + * is painful for some systems. + */ + return; /* ignore */ + } + + if (isr & ISR_BED) { + int ret = BUS_ERROR; + + /* + * I2C bus error - either the device NAK'd us, or + * something more serious happened. If we were NAK'd + * on the initial address phase, we can retry. + */ + if (isr & ISR_ACKNAK) { + if (i2c->msg_ptr == 0 && i2c->msg_idx == 0) + ret = I2C_RETRY; + else + ret = XFER_NAKED; + } + i2c_pxa_master_complete(i2c, ret); + } else if (isr & ISR_RWM) { + /* + * Read mode. We have just sent the address byte, and + * now we must initiate the transfer. + */ + if (i2c->msg_ptr == i2c->msg->len - 1 && + i2c->msg_idx == i2c->msg_num - 1) + icr |= ICR_STOP | ICR_ACKNAK; + + icr |= ICR_ALDIE | ICR_TB; + } else if (i2c->msg_ptr < i2c->msg->len) { + /* + * Write mode. Write the next data byte. + */ + writel(i2c->msg->buf[i2c->msg_ptr++], _IDBR(i2c)); + + icr |= ICR_ALDIE | ICR_TB; + + /* + * If this is the last byte of the last message, send + * a STOP. + */ + if (i2c->msg_ptr == i2c->msg->len && + i2c->msg_idx == i2c->msg_num - 1) + icr |= ICR_STOP; + } else if (i2c->msg_idx < i2c->msg_num - 1) { + /* + * Next segment of the message. + */ + i2c->msg_ptr = 0; + i2c->msg_idx ++; + i2c->msg++; + + /* + * If we aren't doing a repeated start and address, + * go back and try to send the next byte. Note that + * we do not support switching the R/W direction here. + */ + if (i2c->msg->flags & I2C_M_NOSTART) + goto again; + + /* + * Write the next address. + */ + writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c)); + + /* + * And trigger a repeated start, and send the byte. + */ + icr &= ~ICR_ALDIE; + icr |= ICR_START | ICR_TB; + } else { + if (i2c->msg->len == 0) { + /* + * Device probes have a message length of zero + * and need the bus to be reset before it can + * be used again. + */ + i2c_pxa_reset(i2c); + } + i2c_pxa_master_complete(i2c, 0); + } + + i2c->icrlog[i2c->irqlogidx-1] = icr; + + writel(icr, _ICR(i2c)); + show_state(i2c); +} + +static void i2c_pxa_irq_rxfull(struct pxa_i2c *i2c, u32 isr) +{ + u32 icr = readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); + + /* + * Read the byte. + */ + i2c->msg->buf[i2c->msg_ptr++] = readl(_IDBR(i2c)); + + if (i2c->msg_ptr < i2c->msg->len) { + /* + * If this is the last byte of the last + * message, send a STOP. + */ + if (i2c->msg_ptr == i2c->msg->len - 1) + icr |= ICR_STOP | ICR_ACKNAK; + + icr |= ICR_ALDIE | ICR_TB; + } else { + i2c_pxa_master_complete(i2c, 0); + } + + i2c->icrlog[i2c->irqlogidx-1] = icr; + + writel(icr, _ICR(i2c)); +} + +#define VALID_INT_SOURCE (ISR_SSD | ISR_ALD | ISR_ITE | ISR_IRF | \ + ISR_SAD | ISR_BED) +static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) +{ + struct pxa_i2c *i2c = dev_id; + u32 isr = readl(_ISR(i2c)); + + if (!(isr & VALID_INT_SOURCE)) + return IRQ_NONE; + + if (i2c_debug > 2 && 0) { + dev_dbg(&i2c->adap.dev, "%s: ISR=%08x, ICR=%08x, IBMR=%02x\n", + __func__, isr, readl(_ICR(i2c)), readl(_IBMR(i2c))); + decode_ISR(isr); + } + + if (i2c->irqlogidx < ARRAY_SIZE(i2c->isrlog)) + i2c->isrlog[i2c->irqlogidx++] = isr; + + show_state(i2c); + + /* + * Always clear all pending IRQs. + */ + writel(isr & VALID_INT_SOURCE, _ISR(i2c)); + + if (isr & ISR_SAD) + i2c_pxa_slave_start(i2c, isr); + if (isr & ISR_SSD) + i2c_pxa_slave_stop(i2c); + + if (i2c_pxa_is_slavemode(i2c)) { + if (isr & ISR_ITE) + i2c_pxa_slave_txempty(i2c, isr); + if (isr & ISR_IRF) + i2c_pxa_slave_rxfull(i2c, isr); + } else if (i2c->msg) { + if (isr & ISR_ITE) + i2c_pxa_irq_txempty(i2c, isr); + if (isr & ISR_IRF) + i2c_pxa_irq_rxfull(i2c, isr); + } else { + i2c_pxa_scream_blue_murder(i2c, "spurious irq"); + } + + return IRQ_HANDLED; +} + + +static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct pxa_i2c *i2c = adap->algo_data; + int ret, i; + + for (i = adap->retries; i >= 0; i--) { + ret = i2c_pxa_do_xfer(i2c, msgs, num); + if (ret != I2C_RETRY) + goto out; + + if (i2c_debug) + dev_dbg(&adap->dev, "Retrying transmission\n"); + udelay(100); + } + i2c_pxa_scream_blue_murder(i2c, "exhausted retries"); + ret = -EREMOTEIO; + out: + i2c_pxa_set_slave(i2c, ret); + return ret; +} + +static u32 i2c_pxa_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm i2c_pxa_algorithm = { + .master_xfer = i2c_pxa_xfer, + .functionality = i2c_pxa_functionality, +}; + +static const struct i2c_algorithm i2c_pxa_pio_algorithm = { + .master_xfer = i2c_pxa_pio_xfer, + .functionality = i2c_pxa_functionality, +}; + +static int i2c_pxa_probe(struct platform_device *dev) +{ + struct pxa_i2c *i2c; + struct resource *res; + struct i2c_pxa_platform_data *plat = dev->dev.platform_data; + const struct platform_device_id *id = platform_get_device_id(dev); + enum pxa_i2c_types i2c_type = id->driver_data; + int ret; + int irq; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + irq = platform_get_irq(dev, 0); + if (res == NULL || irq < 0) + return -ENODEV; + + if (!request_mem_region(res->start, resource_size(res), res->name)) + return -ENOMEM; + + i2c = kzalloc(sizeof(struct pxa_i2c), GFP_KERNEL); + if (!i2c) { + ret = -ENOMEM; + goto emalloc; + } + + i2c->adap.owner = THIS_MODULE; + i2c->adap.retries = 5; + + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + + /* + * If "dev->id" is negative we consider it as zero. + * The reason to do so is to avoid sysfs names that only make + * sense when there are multiple adapters. + */ + i2c->adap.nr = dev->id != -1 ? dev->id : 0; + snprintf(i2c->adap.name, sizeof(i2c->adap.name), "pxa_i2c-i2c.%u", + i2c->adap.nr); + + i2c->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(i2c->clk)) { + ret = PTR_ERR(i2c->clk); + goto eclk; + } + + i2c->reg_base = ioremap(res->start, resource_size(res)); + if (!i2c->reg_base) { + ret = -EIO; + goto eremap; + } + + i2c->reg_ibmr = i2c->reg_base + pxa_reg_layout[i2c_type].ibmr; + i2c->reg_idbr = i2c->reg_base + pxa_reg_layout[i2c_type].idbr; + i2c->reg_icr = i2c->reg_base + pxa_reg_layout[i2c_type].icr; + i2c->reg_isr = i2c->reg_base + pxa_reg_layout[i2c_type].isr; + if (i2c_type != REGS_CE4100) + i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar; + + i2c->iobase = res->start; + i2c->iosize = resource_size(res); + + i2c->irq = irq; + + i2c->slave_addr = I2C_PXA_SLAVE_ADDR; + +#ifdef CONFIG_I2C_PXA_SLAVE + if (plat) { + i2c->slave_addr = plat->slave_addr; + i2c->slave = plat->slave; + } +#endif + + clk_enable(i2c->clk); + + if (plat) { + i2c->adap.class = plat->class; + i2c->use_pio = plat->use_pio; + i2c->fast_mode = plat->fast_mode; + } + + if (i2c->use_pio) { + i2c->adap.algo = &i2c_pxa_pio_algorithm; + } else { + i2c->adap.algo = &i2c_pxa_algorithm; + ret = request_irq(irq, i2c_pxa_handler, IRQF_SHARED, + i2c->adap.name, i2c); + if (ret) + goto ereqirq; + } + + i2c_pxa_reset(i2c); + + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &dev->dev; +#ifdef CONFIG_OF + i2c->adap.dev.of_node = dev->dev.of_node; +#endif + + if (i2c_type == REGS_CE4100) + ret = i2c_add_adapter(&i2c->adap); + else + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + printk(KERN_INFO "I2C: Failed to add bus\n"); + goto eadapt; + } + of_i2c_register_devices(&i2c->adap); + + platform_set_drvdata(dev, i2c); + +#ifdef CONFIG_I2C_PXA_SLAVE + printk(KERN_INFO "I2C: %s: PXA I2C adapter, slave address %d\n", + dev_name(&i2c->adap.dev), i2c->slave_addr); +#else + printk(KERN_INFO "I2C: %s: PXA I2C adapter\n", + dev_name(&i2c->adap.dev)); +#endif + return 0; + +eadapt: + if (!i2c->use_pio) + free_irq(irq, i2c); +ereqirq: + clk_disable(i2c->clk); + iounmap(i2c->reg_base); +eremap: + clk_put(i2c->clk); +eclk: + kfree(i2c); +emalloc: + release_mem_region(res->start, resource_size(res)); + return ret; +} + +static int __exit i2c_pxa_remove(struct platform_device *dev) +{ + struct pxa_i2c *i2c = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + + i2c_del_adapter(&i2c->adap); + if (!i2c->use_pio) + free_irq(i2c->irq, i2c); + + clk_disable(i2c->clk); + clk_put(i2c->clk); + + iounmap(i2c->reg_base); + release_mem_region(i2c->iobase, i2c->iosize); + kfree(i2c); + + return 0; +} + +#ifdef CONFIG_PM +static int i2c_pxa_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pxa_i2c *i2c = platform_get_drvdata(pdev); + + clk_disable(i2c->clk); + + return 0; +} + +static int i2c_pxa_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pxa_i2c *i2c = platform_get_drvdata(pdev); + + clk_enable(i2c->clk); + i2c_pxa_reset(i2c); + + return 0; +} + +static const struct dev_pm_ops i2c_pxa_dev_pm_ops = { + .suspend_noirq = i2c_pxa_suspend_noirq, + .resume_noirq = i2c_pxa_resume_noirq, +}; + +#define I2C_PXA_DEV_PM_OPS (&i2c_pxa_dev_pm_ops) +#else +#define I2C_PXA_DEV_PM_OPS NULL +#endif + +static struct platform_driver i2c_pxa_driver = { + .probe = i2c_pxa_probe, + .remove = __exit_p(i2c_pxa_remove), + .driver = { + .name = "pxa2xx-i2c", + .owner = THIS_MODULE, + .pm = I2C_PXA_DEV_PM_OPS, + }, + .id_table = i2c_pxa_id_table, +}; + +static int __init i2c_adap_pxa_init(void) +{ + return platform_driver_register(&i2c_pxa_driver); +} + +static void __exit i2c_adap_pxa_exit(void) +{ + platform_driver_unregister(&i2c_pxa_driver); +} + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa2xx-i2c"); + +subsys_initcall(i2c_adap_pxa_init); +module_exit(i2c_adap_pxa_exit); diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c new file mode 100644 index 00000000..f84a63c6 --- /dev/null +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -0,0 +1,1040 @@ +/* linux/drivers/i2c/busses/i2c-s3c2410.c + * + * Copyright (C) 2004,2005,2009 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * S3C2410 I2C Controller + * + * 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/kernel.h> +#include <linux/module.h> + +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/cpufreq.h> +#include <linux/slab.h> +#include <linux/io.h> + +#include <asm/irq.h> + +#include <plat/regs-iic.h> +#include <plat/iic.h> + +/* i2c controller state */ + +enum s3c24xx_i2c_state { + STATE_IDLE, + STATE_START, + STATE_READ, + STATE_WRITE, + STATE_STOP +}; + +enum s3c24xx_i2c_type { + TYPE_S3C2410, + TYPE_S3C2440, +}; + +struct s3c24xx_i2c { + spinlock_t lock; + wait_queue_head_t wait; + unsigned int suspended:1; + + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int msg_idx; + unsigned int msg_ptr; + + unsigned int tx_setup; + unsigned int irq; + + enum s3c24xx_i2c_state state; + unsigned long clkrate; + + void __iomem *regs; + struct clk *clk; + struct device *dev; + struct resource *ioarea; + struct i2c_adapter adap; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif +}; + +/* default platform data removed, dev should always carry data. */ + +/* s3c24xx_i2c_is2440() + * + * return true is this is an s3c2440 +*/ + +static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c) +{ + struct platform_device *pdev = to_platform_device(i2c->dev); + enum s3c24xx_i2c_type type; + + type = platform_get_device_id(pdev)->driver_data; + return type == TYPE_S3C2440; +} + +/* s3c24xx_i2c_master_complete + * + * complete the message and wake up the caller, using the given return code, + * or zero to mean ok. +*/ + +static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) +{ + dev_dbg(i2c->dev, "master_complete %d\n", ret); + + i2c->msg_ptr = 0; + i2c->msg = NULL; + i2c->msg_idx++; + i2c->msg_num = 0; + if (ret) + i2c->msg_idx = ret; + + wake_up(&i2c->wait); +} + +static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + S3C2410_IICCON); + writel(tmp & ~S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON); +} + +static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + S3C2410_IICCON); + writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON); +} + +/* irq enable/disable functions */ + +static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + S3C2410_IICCON); + writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); +} + +static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + S3C2410_IICCON); + writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); +} + + +/* s3c24xx_i2c_message_start + * + * put the start of a message onto the bus +*/ + +static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, + struct i2c_msg *msg) +{ + unsigned int addr = (msg->addr & 0x7f) << 1; + unsigned long stat; + unsigned long iiccon; + + stat = 0; + stat |= S3C2410_IICSTAT_TXRXEN; + + if (msg->flags & I2C_M_RD) { + stat |= S3C2410_IICSTAT_MASTER_RX; + addr |= 1; + } else + stat |= S3C2410_IICSTAT_MASTER_TX; + + if (msg->flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + + /* todo - check for wether ack wanted or not */ + s3c24xx_i2c_enable_ack(i2c); + + iiccon = readl(i2c->regs + S3C2410_IICCON); + writel(stat, i2c->regs + S3C2410_IICSTAT); + + dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); + writeb(addr, i2c->regs + S3C2410_IICDS); + + /* delay here to ensure the data byte has gotten onto the bus + * before the transaction is started */ + + ndelay(i2c->tx_setup); + + dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon); + writel(iiccon, i2c->regs + S3C2410_IICCON); + + stat |= S3C2410_IICSTAT_START; + writel(stat, i2c->regs + S3C2410_IICSTAT); +} + +static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) +{ + unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT); + + dev_dbg(i2c->dev, "STOP\n"); + + /* stop the transfer */ + iicstat &= ~S3C2410_IICSTAT_START; + writel(iicstat, i2c->regs + S3C2410_IICSTAT); + + i2c->state = STATE_STOP; + + s3c24xx_i2c_master_complete(i2c, ret); + s3c24xx_i2c_disable_irq(i2c); +} + +/* helper functions to determine the current state in the set of + * messages we are sending */ + +/* is_lastmsg() + * + * returns TRUE if the current message is the last in the set +*/ + +static inline int is_lastmsg(struct s3c24xx_i2c *i2c) +{ + return i2c->msg_idx >= (i2c->msg_num - 1); +} + +/* is_msglast + * + * returns TRUE if we this is the last byte in the current message +*/ + +static inline int is_msglast(struct s3c24xx_i2c *i2c) +{ + return i2c->msg_ptr == i2c->msg->len-1; +} + +/* is_msgend + * + * returns TRUE if we reached the end of the current message +*/ + +static inline int is_msgend(struct s3c24xx_i2c *i2c) +{ + return i2c->msg_ptr >= i2c->msg->len; +} + +/* i2c_s3c_irq_nextbyte + * + * process an interrupt and work out what to do + */ + +static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) +{ + unsigned long tmp; + unsigned char byte; + int ret = 0; + + switch (i2c->state) { + + case STATE_IDLE: + dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__); + goto out; + + case STATE_STOP: + dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__); + s3c24xx_i2c_disable_irq(i2c); + goto out_ack; + + case STATE_START: + /* last thing we did was send a start condition on the + * bus, or started a new i2c message + */ + + if (iicstat & S3C2410_IICSTAT_LASTBIT && + !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { + /* ack was not received... */ + + dev_dbg(i2c->dev, "ack was not received\n"); + s3c24xx_i2c_stop(i2c, -ENXIO); + goto out_ack; + } + + if (i2c->msg->flags & I2C_M_RD) + i2c->state = STATE_READ; + else + i2c->state = STATE_WRITE; + + /* terminate the transfer if there is nothing to do + * as this is used by the i2c probe to find devices. */ + + if (is_lastmsg(i2c) && i2c->msg->len == 0) { + s3c24xx_i2c_stop(i2c, 0); + goto out_ack; + } + + if (i2c->state == STATE_READ) + goto prepare_read; + + /* fall through to the write state, as we will need to + * send a byte as well */ + + case STATE_WRITE: + /* we are writing data to the device... check for the + * end of the message, and if so, work out what to do + */ + + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { + if (iicstat & S3C2410_IICSTAT_LASTBIT) { + dev_dbg(i2c->dev, "WRITE: No Ack\n"); + + s3c24xx_i2c_stop(i2c, -ECONNREFUSED); + goto out_ack; + } + } + + retry_write: + + if (!is_msgend(i2c)) { + byte = i2c->msg->buf[i2c->msg_ptr++]; + writeb(byte, i2c->regs + S3C2410_IICDS); + + /* delay after writing the byte to allow the + * data setup time on the bus, as writing the + * data to the register causes the first bit + * to appear on SDA, and SCL will change as + * soon as the interrupt is acknowledged */ + + ndelay(i2c->tx_setup); + + } else if (!is_lastmsg(i2c)) { + /* we need to go to the next i2c message */ + + dev_dbg(i2c->dev, "WRITE: Next Message\n"); + + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + + /* check to see if we need to do another message */ + if (i2c->msg->flags & I2C_M_NOSTART) { + + if (i2c->msg->flags & I2C_M_RD) { + /* cannot do this, the controller + * forces us to send a new START + * when we change direction */ + + s3c24xx_i2c_stop(i2c, -EINVAL); + } + + goto retry_write; + } else { + /* send the new start */ + s3c24xx_i2c_message_start(i2c, i2c->msg); + i2c->state = STATE_START; + } + + } else { + /* send stop */ + + s3c24xx_i2c_stop(i2c, 0); + } + break; + + case STATE_READ: + /* we have a byte of data in the data register, do + * something with it, and then work out wether we are + * going to do any more read/write + */ + + byte = readb(i2c->regs + S3C2410_IICDS); + i2c->msg->buf[i2c->msg_ptr++] = byte; + + prepare_read: + if (is_msglast(i2c)) { + /* last byte of buffer */ + + if (is_lastmsg(i2c)) + s3c24xx_i2c_disable_ack(i2c); + + } else if (is_msgend(i2c)) { + /* ok, we've read the entire buffer, see if there + * is anything else we need to do */ + + if (is_lastmsg(i2c)) { + /* last message, send stop and complete */ + dev_dbg(i2c->dev, "READ: Send Stop\n"); + + s3c24xx_i2c_stop(i2c, 0); + } else { + /* go to the next transfer */ + dev_dbg(i2c->dev, "READ: Next Transfer\n"); + + i2c->msg_ptr = 0; + i2c->msg_idx++; + i2c->msg++; + } + } + + break; + } + + /* acknowlegde the IRQ and get back on with the work */ + + out_ack: + tmp = readl(i2c->regs + S3C2410_IICCON); + tmp &= ~S3C2410_IICCON_IRQPEND; + writel(tmp, i2c->regs + S3C2410_IICCON); + out: + return ret; +} + +/* s3c24xx_i2c_irq + * + * top level IRQ servicing routine +*/ + +static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) +{ + struct s3c24xx_i2c *i2c = dev_id; + unsigned long status; + unsigned long tmp; + + status = readl(i2c->regs + S3C2410_IICSTAT); + + if (status & S3C2410_IICSTAT_ARBITR) { + /* deal with arbitration loss */ + dev_err(i2c->dev, "deal with arbitration loss\n"); + } + + if (i2c->state == STATE_IDLE) { + dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n"); + + tmp = readl(i2c->regs + S3C2410_IICCON); + tmp &= ~S3C2410_IICCON_IRQPEND; + writel(tmp, i2c->regs + S3C2410_IICCON); + goto out; + } + + /* pretty much this leaves us with the fact that we've + * transmitted or received whatever byte we last sent */ + + i2c_s3c_irq_nextbyte(i2c, status); + + out: + return IRQ_HANDLED; +} + + +/* s3c24xx_i2c_set_master + * + * get the i2c bus for a master transaction +*/ + +static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) +{ + unsigned long iicstat; + int timeout = 400; + + while (timeout-- > 0) { + iicstat = readl(i2c->regs + S3C2410_IICSTAT); + + if (!(iicstat & S3C2410_IICSTAT_BUSBUSY)) + return 0; + + msleep(1); + } + + return -ETIMEDOUT; +} + +/* s3c24xx_i2c_doxfer + * + * this starts an i2c transfer +*/ + +static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, + struct i2c_msg *msgs, int num) +{ + unsigned long iicstat, timeout; + int spins = 20; + int ret; + + if (i2c->suspended) + return -EIO; + + ret = s3c24xx_i2c_set_master(i2c); + if (ret != 0) { + dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); + ret = -EAGAIN; + goto out; + } + + spin_lock_irq(&i2c->lock); + + i2c->msg = msgs; + i2c->msg_num = num; + i2c->msg_ptr = 0; + i2c->msg_idx = 0; + i2c->state = STATE_START; + + s3c24xx_i2c_enable_irq(i2c); + s3c24xx_i2c_message_start(i2c, msgs); + spin_unlock_irq(&i2c->lock); + + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); + + ret = i2c->msg_idx; + + /* having these next two as dev_err() makes life very + * noisy when doing an i2cdetect */ + + if (timeout == 0) + dev_dbg(i2c->dev, "timeout\n"); + else if (ret != num) + dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + + /* ensure the stop has been through the bus */ + + dev_dbg(i2c->dev, "waiting for bus idle\n"); + + /* first, try busy waiting briefly */ + do { + iicstat = readl(i2c->regs + S3C2410_IICSTAT); + } while ((iicstat & S3C2410_IICSTAT_START) && --spins); + + /* if that timed out sleep */ + if (!spins) { + msleep(1); + iicstat = readl(i2c->regs + S3C2410_IICSTAT); + } + + if (iicstat & S3C2410_IICSTAT_START) + dev_warn(i2c->dev, "timeout waiting for bus idle\n"); + + out: + return ret; +} + +/* s3c24xx_i2c_xfer + * + * first port of call from the i2c bus code when an message needs + * transferring across the i2c bus. +*/ + +static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data; + int retry; + int ret; + + clk_enable(i2c->clk); + + for (retry = 0; retry < adap->retries; retry++) { + + ret = s3c24xx_i2c_doxfer(i2c, msgs, num); + + if (ret != -EAGAIN) { + clk_disable(i2c->clk); + return ret; + } + + dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); + + udelay(100); + } + + clk_disable(i2c->clk); + return -EREMOTEIO; +} + +/* declare our i2c functionality */ +static u32 s3c24xx_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; +} + +/* i2c bus registration info */ + +static const struct i2c_algorithm s3c24xx_i2c_algorithm = { + .master_xfer = s3c24xx_i2c_xfer, + .functionality = s3c24xx_i2c_func, +}; + +/* s3c24xx_i2c_calcdivisor + * + * return the divisor settings for a given frequency +*/ + +static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, + unsigned int *div1, unsigned int *divs) +{ + unsigned int calc_divs = clkin / wanted; + unsigned int calc_div1; + + if (calc_divs > (16*16)) + calc_div1 = 512; + else + calc_div1 = 16; + + calc_divs += calc_div1-1; + calc_divs /= calc_div1; + + if (calc_divs == 0) + calc_divs = 1; + if (calc_divs > 17) + calc_divs = 17; + + *divs = calc_divs; + *div1 = calc_div1; + + return clkin / (calc_divs * calc_div1); +} + +/* s3c24xx_i2c_clockrate + * + * work out a divisor for the user requested frequency setting, + * either by the requested frequency, or scanning the acceptable + * range of frequencies until something is found +*/ + +static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) +{ + struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data; + unsigned long clkin = clk_get_rate(i2c->clk); + unsigned int divs, div1; + unsigned long target_frequency; + u32 iiccon; + int freq; + + i2c->clkrate = clkin; + clkin /= 1000; /* clkin now in KHz */ + + dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency); + + target_frequency = pdata->frequency ? pdata->frequency : 100000; + + target_frequency /= 1000; /* Target frequency now in KHz */ + + freq = s3c24xx_i2c_calcdivisor(clkin, target_frequency, &div1, &divs); + + if (freq > target_frequency) { + dev_err(i2c->dev, + "Unable to achieve desired frequency %luKHz." \ + " Lowest achievable %dKHz\n", target_frequency, freq); + return -EINVAL; + } + + *got = freq; + + iiccon = readl(i2c->regs + S3C2410_IICCON); + iiccon &= ~(S3C2410_IICCON_SCALEMASK | S3C2410_IICCON_TXDIV_512); + iiccon |= (divs-1); + + if (div1 == 512) + iiccon |= S3C2410_IICCON_TXDIV_512; + + writel(iiccon, i2c->regs + S3C2410_IICCON); + + if (s3c24xx_i2c_is2440(i2c)) { + unsigned long sda_delay; + + if (pdata->sda_delay) { + sda_delay = clkin * pdata->sda_delay; + sda_delay = DIV_ROUND_UP(sda_delay, 1000000); + sda_delay = DIV_ROUND_UP(sda_delay, 5); + if (sda_delay > 3) + sda_delay = 3; + sda_delay |= S3C2410_IICLC_FILTER_ON; + } else + sda_delay = 0; + + dev_dbg(i2c->dev, "IICLC=%08lx\n", sda_delay); + writel(sda_delay, i2c->regs + S3C2440_IICLC); + } + + return 0; +} + +#ifdef CONFIG_CPU_FREQ + +#define freq_to_i2c(_n) container_of(_n, struct s3c24xx_i2c, freq_transition) + +static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct s3c24xx_i2c *i2c = freq_to_i2c(nb); + unsigned long flags; + unsigned int got; + int delta_f; + int ret; + + delta_f = clk_get_rate(i2c->clk) - i2c->clkrate; + + /* if we're post-change and the input clock has slowed down + * or at pre-change and the clock is about to speed up, then + * adjust our clock rate. <0 is slow, >0 speedup. + */ + + if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) || + (val == CPUFREQ_PRECHANGE && delta_f > 0)) { + spin_lock_irqsave(&i2c->lock, flags); + ret = s3c24xx_i2c_clockrate(i2c, &got); + spin_unlock_irqrestore(&i2c->lock, flags); + + if (ret < 0) + dev_err(i2c->dev, "cannot find frequency\n"); + else + dev_info(i2c->dev, "setting freq %d\n", got); + } + + return 0; +} + +static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c) +{ + i2c->freq_transition.notifier_call = s3c24xx_i2c_cpufreq_transition; + + return cpufreq_register_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c) +{ + cpufreq_unregister_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c) +{ + return 0; +} + +static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c) +{ +} +#endif + +/* s3c24xx_i2c_init + * + * initialise the controller, set the IO lines and frequency +*/ + +static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) +{ + unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN; + struct s3c2410_platform_i2c *pdata; + unsigned int freq; + + /* get the plafrom data */ + + pdata = i2c->dev->platform_data; + + /* inititalise the gpio */ + + if (pdata->cfg_gpio) + pdata->cfg_gpio(to_platform_device(i2c->dev)); + + /* write slave address */ + + writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD); + + dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); + + writel(iicon, i2c->regs + S3C2410_IICCON); + + /* we need to work out the divisors for the clock... */ + + if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) { + writel(0, i2c->regs + S3C2410_IICCON); + dev_err(i2c->dev, "cannot meet bus frequency required\n"); + return -EINVAL; + } + + /* todo - check that the i2c lines aren't being dragged anywhere */ + + dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq); + dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon); + + return 0; +} + +/* s3c24xx_i2c_probe + * + * called by the bus driver when a suitable device is found +*/ + +static int s3c24xx_i2c_probe(struct platform_device *pdev) +{ + struct s3c24xx_i2c *i2c; + struct s3c2410_platform_i2c *pdata; + struct resource *res; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + + i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); + if (!i2c) { + dev_err(&pdev->dev, "no memory for state\n"); + return -ENOMEM; + } + + strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &s3c24xx_i2c_algorithm; + i2c->adap.retries = 2; + i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + i2c->tx_setup = 50; + + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + + /* find the clock and enable it */ + + i2c->dev = &pdev->dev; + i2c->clk = clk_get(&pdev->dev, "i2c"); + if (IS_ERR(i2c->clk)) { + dev_err(&pdev->dev, "cannot get clock\n"); + ret = -ENOENT; + goto err_noclk; + } + + dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); + + clk_enable(i2c->clk); + + /* map the registers */ + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "cannot find IO resource\n"); + ret = -ENOENT; + goto err_clk; + } + + i2c->ioarea = request_mem_region(res->start, resource_size(res), + pdev->name); + + if (i2c->ioarea == NULL) { + dev_err(&pdev->dev, "cannot request IO\n"); + ret = -ENXIO; + goto err_clk; + } + + i2c->regs = ioremap(res->start, resource_size(res)); + + if (i2c->regs == NULL) { + dev_err(&pdev->dev, "cannot map IO\n"); + ret = -ENXIO; + goto err_ioarea; + } + + dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", + i2c->regs, i2c->ioarea, res); + + /* setup info block for the i2c core */ + + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &pdev->dev; + + /* initialise the i2c controller */ + + ret = s3c24xx_i2c_init(i2c); + if (ret != 0) + goto err_iomap; + + /* find the IRQ for this unit (note, this relies on the init call to + * ensure no current IRQs pending + */ + + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + goto err_iomap; + } + + ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, + dev_name(&pdev->dev), i2c); + + if (ret != 0) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); + goto err_iomap; + } + + ret = s3c24xx_i2c_register_cpufreq(i2c); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); + goto err_irq; + } + + /* Note, previous versions of the driver used i2c_add_adapter() + * to add the bus at any number. We now pass the bus number via + * the platform data, so if unset it will now default to always + * being bus 0. + */ + + i2c->adap.nr = pdata->bus_num; + + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + goto err_cpufreq; + } + + platform_set_drvdata(pdev, i2c); + + dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); + clk_disable(i2c->clk); + return 0; + + err_cpufreq: + s3c24xx_i2c_deregister_cpufreq(i2c); + + err_irq: + free_irq(i2c->irq, i2c); + + err_iomap: + iounmap(i2c->regs); + + err_ioarea: + release_resource(i2c->ioarea); + kfree(i2c->ioarea); + + err_clk: + clk_disable(i2c->clk); + clk_put(i2c->clk); + + err_noclk: + kfree(i2c); + return ret; +} + +/* s3c24xx_i2c_remove + * + * called when device is removed from the bus +*/ + +static int s3c24xx_i2c_remove(struct platform_device *pdev) +{ + struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + + s3c24xx_i2c_deregister_cpufreq(i2c); + + i2c_del_adapter(&i2c->adap); + free_irq(i2c->irq, i2c); + + clk_disable(i2c->clk); + clk_put(i2c->clk); + + iounmap(i2c->regs); + + release_resource(i2c->ioarea); + kfree(i2c->ioarea); + kfree(i2c); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c24xx_i2c_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + + i2c->suspended = 1; + + return 0; +} + +static int s3c24xx_i2c_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + + i2c->suspended = 0; + clk_enable(i2c->clk); + s3c24xx_i2c_init(i2c); + clk_disable(i2c->clk); + + return 0; +} + +static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = { + .suspend_noirq = s3c24xx_i2c_suspend_noirq, + .resume = s3c24xx_i2c_resume, +}; + +#define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops) +#else +#define S3C24XX_DEV_PM_OPS NULL +#endif + +/* device driver for platform bus bits */ + +static struct platform_device_id s3c24xx_driver_ids[] = { + { + .name = "s3c2410-i2c", + .driver_data = TYPE_S3C2410, + }, { + .name = "s3c2440-i2c", + .driver_data = TYPE_S3C2440, + }, { }, +}; +MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); + +static struct platform_driver s3c24xx_i2c_driver = { + .probe = s3c24xx_i2c_probe, + .remove = s3c24xx_i2c_remove, + .id_table = s3c24xx_driver_ids, + .driver = { + .owner = THIS_MODULE, + .name = "s3c-i2c", + .pm = S3C24XX_DEV_PM_OPS, + }, +}; + +static int __init i2c_adap_s3c_init(void) +{ + return platform_driver_register(&s3c24xx_i2c_driver); +} +subsys_initcall(i2c_adap_s3c_init); + +static void __exit i2c_adap_s3c_exit(void) +{ + platform_driver_unregister(&s3c24xx_i2c_driver); +} +module_exit(i2c_adap_s3c_exit); + +MODULE_DESCRIPTION("S3C24XX I2C Bus driver"); +MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-s6000.c b/drivers/i2c/busses/i2c-s6000.c new file mode 100644 index 00000000..cb5d01e2 --- /dev/null +++ b/drivers/i2c/busses/i2c-s6000.c @@ -0,0 +1,407 @@ +/* + * drivers/i2c/busses/i2c-s6000.c + * + * Description: Driver for S6000 Family I2C Interface + * Copyright (c) 2008 emlix GmbH + * Author: Oskar Schirmer <os@emlix.com> + * + * Partially based on i2c-bfin-twi.c driver by <sonic.zhang@analog.com> + * Copyright (c) 2005-2007 Analog Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/i2c/s6000.h> +#include <linux/timer.h> +#include <linux/spinlock.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#include "i2c-s6000.h" + +#define DRV_NAME "i2c-s6000" + +#define POLL_TIMEOUT (2 * HZ) + +struct s6i2c_if { + u8 __iomem *reg; /* memory mapped registers */ + int irq; + spinlock_t lock; + struct i2c_msg *msgs; /* messages currently handled */ + int msgs_num; /* nb of msgs to do */ + int msgs_push; /* nb of msgs read/written */ + int msgs_done; /* nb of msgs finally handled */ + unsigned push; /* nb of bytes read/written in msg */ + unsigned done; /* nb of bytes finally handled */ + int timeout_count; /* timeout retries left */ + struct timer_list timeout_timer; + struct i2c_adapter adap; + struct completion complete; + struct clk *clk; + struct resource *res; +}; + +static inline u16 i2c_rd16(struct s6i2c_if *iface, unsigned n) +{ + return readw(iface->reg + (n)); +} + +static inline void i2c_wr16(struct s6i2c_if *iface, unsigned n, u16 v) +{ + writew(v, iface->reg + (n)); +} + +static inline u32 i2c_rd32(struct s6i2c_if *iface, unsigned n) +{ + return readl(iface->reg + (n)); +} + +static inline void i2c_wr32(struct s6i2c_if *iface, unsigned n, u32 v) +{ + writel(v, iface->reg + (n)); +} + +static struct s6i2c_if s6i2c_if; + +static void s6i2c_handle_interrupt(struct s6i2c_if *iface) +{ + if (i2c_rd16(iface, S6_I2C_INTRSTAT) & (1 << S6_I2C_INTR_TXABRT)) { + i2c_rd16(iface, S6_I2C_CLRTXABRT); + i2c_wr16(iface, S6_I2C_INTRMASK, 0); + complete(&iface->complete); + return; + } + if (iface->msgs_done >= iface->msgs_num) { + dev_err(&iface->adap.dev, "s6i2c: spurious I2C irq: %04x\n", + i2c_rd16(iface, S6_I2C_INTRSTAT)); + i2c_wr16(iface, S6_I2C_INTRMASK, 0); + return; + } + while ((iface->msgs_push < iface->msgs_num) + && (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_TFNF))) { + struct i2c_msg *m = &iface->msgs[iface->msgs_push]; + if (!(m->flags & I2C_M_RD)) + i2c_wr16(iface, S6_I2C_DATACMD, m->buf[iface->push]); + else + i2c_wr16(iface, S6_I2C_DATACMD, + 1 << S6_I2C_DATACMD_READ); + if (++iface->push >= m->len) { + iface->push = 0; + iface->msgs_push += 1; + } + } + do { + struct i2c_msg *m = &iface->msgs[iface->msgs_done]; + if (!(m->flags & I2C_M_RD)) { + if (iface->msgs_done < iface->msgs_push) + iface->msgs_done += 1; + else + break; + } else if (i2c_rd16(iface, S6_I2C_STATUS) + & (1 << S6_I2C_STATUS_RFNE)) { + m->buf[iface->done] = i2c_rd16(iface, S6_I2C_DATACMD); + if (++iface->done >= m->len) { + iface->done = 0; + iface->msgs_done += 1; + } + } else{ + break; + } + } while (iface->msgs_done < iface->msgs_num); + if (iface->msgs_done >= iface->msgs_num) { + i2c_wr16(iface, S6_I2C_INTRMASK, 1 << S6_I2C_INTR_TXABRT); + complete(&iface->complete); + } else if (iface->msgs_push >= iface->msgs_num) { + i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXABRT) | + (1 << S6_I2C_INTR_RXFULL)); + } else { + i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXABRT) | + (1 << S6_I2C_INTR_TXEMPTY) | + (1 << S6_I2C_INTR_RXFULL)); + } +} + +static irqreturn_t s6i2c_interrupt_entry(int irq, void *dev_id) +{ + struct s6i2c_if *iface = dev_id; + if (!(i2c_rd16(iface, S6_I2C_STATUS) & ((1 << S6_I2C_INTR_RXUNDER) + | (1 << S6_I2C_INTR_RXOVER) + | (1 << S6_I2C_INTR_RXFULL) + | (1 << S6_I2C_INTR_TXOVER) + | (1 << S6_I2C_INTR_TXEMPTY) + | (1 << S6_I2C_INTR_RDREQ) + | (1 << S6_I2C_INTR_TXABRT) + | (1 << S6_I2C_INTR_RXDONE) + | (1 << S6_I2C_INTR_ACTIVITY) + | (1 << S6_I2C_INTR_STOPDET) + | (1 << S6_I2C_INTR_STARTDET) + | (1 << S6_I2C_INTR_GENCALL)))) + return IRQ_NONE; + + spin_lock(&iface->lock); + del_timer(&iface->timeout_timer); + s6i2c_handle_interrupt(iface); + spin_unlock(&iface->lock); + return IRQ_HANDLED; +} + +static void s6i2c_timeout(unsigned long data) +{ + struct s6i2c_if *iface = (struct s6i2c_if *)data; + unsigned long flags; + + spin_lock_irqsave(&iface->lock, flags); + s6i2c_handle_interrupt(iface); + if (--iface->timeout_count > 0) { + iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; + add_timer(&iface->timeout_timer); + } else { + complete(&iface->complete); + i2c_wr16(iface, S6_I2C_INTRMASK, 0); + } + spin_unlock_irqrestore(&iface->lock, flags); +} + +static int s6i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct s6i2c_if *iface = adap->algo_data; + int i; + if (num == 0) + return 0; + if (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_ACTIVITY)) + yield(); + i2c_wr16(iface, S6_I2C_INTRMASK, 0); + i2c_rd16(iface, S6_I2C_CLRINTR); + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_TEN) { + dev_err(&adap->dev, + "s6i2c: 10 bits addr not supported\n"); + return -EINVAL; + } + if (msgs[i].len == 0) { + dev_err(&adap->dev, + "s6i2c: zero length message not supported\n"); + return -EINVAL; + } + if (msgs[i].addr != msgs[0].addr) { + dev_err(&adap->dev, + "s6i2c: multiple xfer cannot change target\n"); + return -EINVAL; + } + } + + iface->msgs = msgs; + iface->msgs_num = num; + iface->msgs_push = 0; + iface->msgs_done = 0; + iface->push = 0; + iface->done = 0; + iface->timeout_count = 10; + i2c_wr16(iface, S6_I2C_TAR, msgs[0].addr); + i2c_wr16(iface, S6_I2C_ENABLE, 1); + i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXEMPTY) | + (1 << S6_I2C_INTR_TXABRT)); + + iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; + add_timer(&iface->timeout_timer); + wait_for_completion(&iface->complete); + del_timer_sync(&iface->timeout_timer); + while (i2c_rd32(iface, S6_I2C_TXFLR) > 0) + schedule(); + while (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_ACTIVITY)) + schedule(); + + i2c_wr16(iface, S6_I2C_INTRMASK, 0); + i2c_wr16(iface, S6_I2C_ENABLE, 0); + return iface->msgs_done; +} + +static u32 s6i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm s6i2c_algorithm = { + .master_xfer = s6i2c_master_xfer, + .functionality = s6i2c_functionality, +}; + +static u16 __devinit nanoseconds_on_clk(struct s6i2c_if *iface, u32 ns) +{ + u32 dividend = ((clk_get_rate(iface->clk) / 1000) * ns) / 1000000; + if (dividend > 0xffff) + return 0xffff; + return dividend; +} + +static int __devinit s6i2c_probe(struct platform_device *dev) +{ + struct s6i2c_if *iface = &s6i2c_if; + struct i2c_adapter *p_adap; + const char *clock; + int bus_num, rc; + spin_lock_init(&iface->lock); + init_completion(&iface->complete); + iface->irq = platform_get_irq(dev, 0); + if (iface->irq < 0) { + rc = iface->irq; + goto err_out; + } + iface->res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iface->res) { + rc = -ENXIO; + goto err_out; + } + iface->res = request_mem_region(iface->res->start, + resource_size(iface->res), + dev->dev.bus_id); + if (!iface->res) { + rc = -EBUSY; + goto err_out; + } + iface->reg = ioremap_nocache(iface->res->start, + resource_size(iface->res)); + if (!iface->reg) { + rc = -ENOMEM; + goto err_reg; + } + + clock = 0; + bus_num = -1; + if (dev->dev.platform_data) { + struct s6_i2c_platform_data *pdata = dev->dev.platform_data; + bus_num = pdata->bus_num; + clock = pdata->clock; + } + iface->clk = clk_get(&dev->dev, clock); + if (IS_ERR(iface->clk)) { + rc = PTR_ERR(iface->clk); + goto err_map; + } + rc = clk_enable(iface->clk); + if (rc < 0) + goto err_clk_put; + init_timer(&iface->timeout_timer); + iface->timeout_timer.function = s6i2c_timeout; + iface->timeout_timer.data = (unsigned long)iface; + + p_adap = &iface->adap; + strlcpy(p_adap->name, dev->name, sizeof(p_adap->name)); + p_adap->algo = &s6i2c_algorithm; + p_adap->algo_data = iface; + p_adap->nr = bus_num; + p_adap->class = 0; + p_adap->dev.parent = &dev->dev; + i2c_wr16(iface, S6_I2C_INTRMASK, 0); + rc = request_irq(iface->irq, s6i2c_interrupt_entry, + IRQF_SHARED, dev->name, iface); + if (rc) { + dev_err(&p_adap->dev, "s6i2c: can't get IRQ %d\n", iface->irq); + goto err_clk_dis; + } + + i2c_wr16(iface, S6_I2C_ENABLE, 0); + udelay(1); + i2c_wr32(iface, S6_I2C_SRESET, 1 << S6_I2C_SRESET_IC_SRST); + i2c_wr16(iface, S6_I2C_CLRTXABRT, 1); + i2c_wr16(iface, S6_I2C_CON, + (1 << S6_I2C_CON_MASTER) | + (S6_I2C_CON_SPEED_NORMAL << S6_I2C_CON_SPEED) | + (0 << S6_I2C_CON_10BITSLAVE) | + (0 << S6_I2C_CON_10BITMASTER) | + (1 << S6_I2C_CON_RESTARTENA) | + (1 << S6_I2C_CON_SLAVEDISABLE)); + i2c_wr16(iface, S6_I2C_SSHCNT, nanoseconds_on_clk(iface, 4000)); + i2c_wr16(iface, S6_I2C_SSLCNT, nanoseconds_on_clk(iface, 4700)); + i2c_wr16(iface, S6_I2C_FSHCNT, nanoseconds_on_clk(iface, 600)); + i2c_wr16(iface, S6_I2C_FSLCNT, nanoseconds_on_clk(iface, 1300)); + i2c_wr16(iface, S6_I2C_RXTL, 0); + i2c_wr16(iface, S6_I2C_TXTL, 0); + + platform_set_drvdata(dev, iface); + if (bus_num < 0) + rc = i2c_add_adapter(p_adap); + else + rc = i2c_add_numbered_adapter(p_adap); + if (rc) + goto err_irq_free; + return 0; + +err_irq_free: + free_irq(iface->irq, iface); +err_clk_dis: + clk_disable(iface->clk); +err_clk_put: + clk_put(iface->clk); +err_map: + iounmap(iface->reg); +err_reg: + release_mem_region(iface->res->start, + resource_size(iface->res)); +err_out: + return rc; +} + +static int __devexit s6i2c_remove(struct platform_device *pdev) +{ + struct s6i2c_if *iface = platform_get_drvdata(pdev); + i2c_wr16(iface, S6_I2C_ENABLE, 0); + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&iface->adap); + free_irq(iface->irq, iface); + clk_disable(iface->clk); + clk_put(iface->clk); + iounmap(iface->reg); + release_mem_region(iface->res->start, + resource_size(iface->res)); + return 0; +} + +static struct platform_driver s6i2c_driver = { + .probe = s6i2c_probe, + .remove = __devexit_p(s6i2c_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init s6i2c_init(void) +{ + pr_info("I2C: S6000 I2C driver\n"); + return platform_driver_register(&s6i2c_driver); +} + +static void __exit s6i2c_exit(void) +{ + platform_driver_unregister(&s6i2c_driver); +} + +MODULE_DESCRIPTION("I2C-Bus adapter routines for S6000 I2C"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); + +subsys_initcall(s6i2c_init); +module_exit(s6i2c_exit); diff --git a/drivers/i2c/busses/i2c-s6000.h b/drivers/i2c/busses/i2c-s6000.h new file mode 100644 index 00000000..ff23b81d --- /dev/null +++ b/drivers/i2c/busses/i2c-s6000.h @@ -0,0 +1,79 @@ +/* + * drivers/i2c/busses/i2c-s6000.h + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 Emlix GmbH <info@emlix.com> + * Author: Oskar Schirmer <os@emlix.com> + */ + +#ifndef __DRIVERS_I2C_BUSSES_I2C_S6000_H +#define __DRIVERS_I2C_BUSSES_I2C_S6000_H + +#define S6_I2C_CON 0x000 +#define S6_I2C_CON_MASTER 0 +#define S6_I2C_CON_SPEED 1 +#define S6_I2C_CON_SPEED_NORMAL 1 +#define S6_I2C_CON_SPEED_FAST 2 +#define S6_I2C_CON_SPEED_MASK 3 +#define S6_I2C_CON_10BITSLAVE 3 +#define S6_I2C_CON_10BITMASTER 4 +#define S6_I2C_CON_RESTARTENA 5 +#define S6_I2C_CON_SLAVEDISABLE 6 +#define S6_I2C_TAR 0x004 +#define S6_I2C_TAR_GCORSTART 10 +#define S6_I2C_TAR_SPECIAL 11 +#define S6_I2C_SAR 0x008 +#define S6_I2C_HSMADDR 0x00C +#define S6_I2C_DATACMD 0x010 +#define S6_I2C_DATACMD_READ 8 +#define S6_I2C_SSHCNT 0x014 +#define S6_I2C_SSLCNT 0x018 +#define S6_I2C_FSHCNT 0x01C +#define S6_I2C_FSLCNT 0x020 +#define S6_I2C_INTRSTAT 0x02C +#define S6_I2C_INTRMASK 0x030 +#define S6_I2C_RAWINTR 0x034 +#define S6_I2C_INTR_RXUNDER 0 +#define S6_I2C_INTR_RXOVER 1 +#define S6_I2C_INTR_RXFULL 2 +#define S6_I2C_INTR_TXOVER 3 +#define S6_I2C_INTR_TXEMPTY 4 +#define S6_I2C_INTR_RDREQ 5 +#define S6_I2C_INTR_TXABRT 6 +#define S6_I2C_INTR_RXDONE 7 +#define S6_I2C_INTR_ACTIVITY 8 +#define S6_I2C_INTR_STOPDET 9 +#define S6_I2C_INTR_STARTDET 10 +#define S6_I2C_INTR_GENCALL 11 +#define S6_I2C_RXTL 0x038 +#define S6_I2C_TXTL 0x03C +#define S6_I2C_CLRINTR 0x040 +#define S6_I2C_CLRRXUNDER 0x044 +#define S6_I2C_CLRRXOVER 0x048 +#define S6_I2C_CLRTXOVER 0x04C +#define S6_I2C_CLRRDREQ 0x050 +#define S6_I2C_CLRTXABRT 0x054 +#define S6_I2C_CLRRXDONE 0x058 +#define S6_I2C_CLRACTIVITY 0x05C +#define S6_I2C_CLRSTOPDET 0x060 +#define S6_I2C_CLRSTARTDET 0x064 +#define S6_I2C_CLRGENCALL 0x068 +#define S6_I2C_ENABLE 0x06C +#define S6_I2C_STATUS 0x070 +#define S6_I2C_STATUS_ACTIVITY 0 +#define S6_I2C_STATUS_TFNF 1 +#define S6_I2C_STATUS_TFE 2 +#define S6_I2C_STATUS_RFNE 3 +#define S6_I2C_STATUS_RFF 4 +#define S6_I2C_TXFLR 0x074 +#define S6_I2C_RXFLR 0x078 +#define S6_I2C_SRESET 0x07C +#define S6_I2C_SRESET_IC_SRST 0 +#define S6_I2C_SRESET_IC_MASTER_SRST 1 +#define S6_I2C_SRESET_IC_SLAVE_SRST 2 +#define S6_I2C_TXABRTSOURCE 0x080 + +#endif diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c new file mode 100644 index 00000000..388cbdc9 --- /dev/null +++ b/drivers/i2c/busses/i2c-scmi.c @@ -0,0 +1,445 @@ +/* + * SMBus driver for ACPI SMBus CMI + * + * Copyright (C) 2009 Crane Cai <crane.cai@amd.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. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/acpi.h> + +#define ACPI_SMBUS_HC_CLASS "smbus" +#define ACPI_SMBUS_HC_DEVICE_NAME "cmi" + +ACPI_MODULE_NAME("smbus_cmi"); + +struct smbus_methods_t { + char *mt_info; + char *mt_sbr; + char *mt_sbw; +}; + +struct acpi_smbus_cmi { + acpi_handle handle; + struct i2c_adapter adapter; + u8 cap_info:1; + u8 cap_read:1; + u8 cap_write:1; + struct smbus_methods_t *methods; +}; + +static const struct smbus_methods_t smbus_methods = { + .mt_info = "_SBI", + .mt_sbr = "_SBR", + .mt_sbw = "_SBW", +}; + +/* Some IBM BIOSes omit the leading underscore */ +static const struct smbus_methods_t ibm_smbus_methods = { + .mt_info = "SBI_", + .mt_sbr = "SBR_", + .mt_sbw = "SBW_", +}; + +static const struct acpi_device_id acpi_smbus_cmi_ids[] = { + {"SMBUS01", (kernel_ulong_t)&smbus_methods}, + {ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods}, + {"", 0} +}; +MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids); + +#define ACPI_SMBUS_STATUS_OK 0x00 +#define ACPI_SMBUS_STATUS_FAIL 0x07 +#define ACPI_SMBUS_STATUS_DNAK 0x10 +#define ACPI_SMBUS_STATUS_DERR 0x11 +#define ACPI_SMBUS_STATUS_CMD_DENY 0x12 +#define ACPI_SMBUS_STATUS_UNKNOWN 0x13 +#define ACPI_SMBUS_STATUS_ACC_DENY 0x17 +#define ACPI_SMBUS_STATUS_TIMEOUT 0x18 +#define ACPI_SMBUS_STATUS_NOTSUP 0x19 +#define ACPI_SMBUS_STATUS_BUSY 0x1a +#define ACPI_SMBUS_STATUS_PEC 0x1f + +#define ACPI_SMBUS_PRTCL_WRITE 0x00 +#define ACPI_SMBUS_PRTCL_READ 0x01 +#define ACPI_SMBUS_PRTCL_QUICK 0x02 +#define ACPI_SMBUS_PRTCL_BYTE 0x04 +#define ACPI_SMBUS_PRTCL_BYTE_DATA 0x06 +#define ACPI_SMBUS_PRTCL_WORD_DATA 0x08 +#define ACPI_SMBUS_PRTCL_BLOCK_DATA 0x0a + + +static int +acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + int result = 0; + struct acpi_smbus_cmi *smbus_cmi = adap->algo_data; + unsigned char protocol; + acpi_status status = 0; + struct acpi_object_list input; + union acpi_object mt_params[5]; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + union acpi_object *pkg; + char *method; + int len = 0; + + dev_dbg(&adap->dev, "access size: %d %s\n", size, + (read_write) ? "READ" : "WRITE"); + switch (size) { + case I2C_SMBUS_QUICK: + protocol = ACPI_SMBUS_PRTCL_QUICK; + command = 0; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 0; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = 0; + } + break; + + case I2C_SMBUS_BYTE: + protocol = ACPI_SMBUS_PRTCL_BYTE; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 0; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = 0; + } else { + command = 0; + } + break; + + case I2C_SMBUS_BYTE_DATA: + protocol = ACPI_SMBUS_PRTCL_BYTE_DATA; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 1; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = data->byte; + } + break; + + case I2C_SMBUS_WORD_DATA: + protocol = ACPI_SMBUS_PRTCL_WORD_DATA; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 2; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = data->word; + } + break; + + case I2C_SMBUS_BLOCK_DATA: + protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA; + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = len; + mt_params[4].type = ACPI_TYPE_BUFFER; + mt_params[4].buffer.pointer = data->block + 1; + } + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + if (read_write == I2C_SMBUS_READ) { + protocol |= ACPI_SMBUS_PRTCL_READ; + method = smbus_cmi->methods->mt_sbr; + input.count = 3; + } else { + protocol |= ACPI_SMBUS_PRTCL_WRITE; + method = smbus_cmi->methods->mt_sbw; + input.count = 5; + } + + input.pointer = mt_params; + mt_params[0].type = ACPI_TYPE_INTEGER; + mt_params[0].integer.value = protocol; + mt_params[1].type = ACPI_TYPE_INTEGER; + mt_params[1].integer.value = addr; + mt_params[2].type = ACPI_TYPE_INTEGER; + mt_params[2].integer.value = command; + + status = acpi_evaluate_object(smbus_cmi->handle, method, &input, + &buffer); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Evaluating %s: %i", method, status)); + return -EIO; + } + + pkg = buffer.pointer; + if (pkg && pkg->type == ACPI_TYPE_PACKAGE) + obj = pkg->package.elements; + else { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + + result = obj->integer.value; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s return status: %i\n", + method, result)); + + switch (result) { + case ACPI_SMBUS_STATUS_OK: + result = 0; + break; + case ACPI_SMBUS_STATUS_BUSY: + result = -EBUSY; + goto out; + case ACPI_SMBUS_STATUS_TIMEOUT: + result = -ETIMEDOUT; + goto out; + case ACPI_SMBUS_STATUS_DNAK: + result = -ENXIO; + goto out; + default: + result = -EIO; + goto out; + } + + if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK) + goto out; + + obj = pkg->package.elements + 1; + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + + len = obj->integer.value; + obj = pkg->package.elements + 2; + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + if (len == 2) + data->word = obj->integer.value; + else + data->byte = obj->integer.value; + break; + case I2C_SMBUS_BLOCK_DATA: + if (obj == NULL || obj->type != ACPI_TYPE_BUFFER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + data->block[0] = len; + memcpy(data->block + 1, obj->buffer.pointer, len); + break; + } + +out: + kfree(buffer.pointer); + dev_dbg(&adap->dev, "Transaction status: %i\n", result); + return result; +} + +static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter) +{ + struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data; + u32 ret; + + ret = smbus_cmi->cap_read | smbus_cmi->cap_write ? + I2C_FUNC_SMBUS_QUICK : 0; + + ret |= smbus_cmi->cap_read ? + (I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | + I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0; + + ret |= smbus_cmi->cap_write ? + (I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_WORD_DATA | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0; + + return ret; +} + +static const struct i2c_algorithm acpi_smbus_cmi_algorithm = { + .smbus_xfer = acpi_smbus_cmi_access, + .functionality = acpi_smbus_cmi_func, +}; + + +static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi, + const char *name) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + if (!strcmp(name, smbus_cmi->methods->mt_info)) { + status = acpi_evaluate_object(smbus_cmi->handle, + smbus_cmi->methods->mt_info, + NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Evaluating %s: %i", + smbus_cmi->methods->mt_info, status)); + return -EIO; + } + + obj = buffer.pointer; + if (obj && obj->type == ACPI_TYPE_PACKAGE) + obj = obj->package.elements; + else { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + kfree(buffer.pointer); + return -EIO; + } + + if (obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + kfree(buffer.pointer); + return -EIO; + } else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SMBus CMI Version %x" + "\n", (int)obj->integer.value)); + + kfree(buffer.pointer); + smbus_cmi->cap_info = 1; + } else if (!strcmp(name, smbus_cmi->methods->mt_sbr)) + smbus_cmi->cap_read = 1; + else if (!strcmp(name, smbus_cmi->methods->mt_sbw)) + smbus_cmi->cap_write = 1; + else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n", + name)); + + return 0; +} + +static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level, + void *context, void **return_value) +{ + char node_name[5]; + struct acpi_buffer buffer = { sizeof(node_name), node_name }; + struct acpi_smbus_cmi *smbus_cmi = context; + acpi_status status; + + status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + + if (ACPI_SUCCESS(status)) + acpi_smbus_cmi_add_cap(smbus_cmi, node_name); + + return AE_OK; +} + +static int acpi_smbus_cmi_add(struct acpi_device *device) +{ + struct acpi_smbus_cmi *smbus_cmi; + const struct acpi_device_id *id; + + smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL); + if (!smbus_cmi) + return -ENOMEM; + + smbus_cmi->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS); + device->driver_data = smbus_cmi; + smbus_cmi->cap_info = 0; + smbus_cmi->cap_read = 0; + smbus_cmi->cap_write = 0; + + for (id = acpi_smbus_cmi_ids; id->id[0]; id++) + if (!strcmp(id->id, acpi_device_hid(device))) + smbus_cmi->methods = + (struct smbus_methods_t *) id->driver_data; + + acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1, + acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL); + + if (smbus_cmi->cap_info == 0) + goto err; + + snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name), + "SMBus CMI adapter %s", + acpi_device_name(device)); + smbus_cmi->adapter.owner = THIS_MODULE; + smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm; + smbus_cmi->adapter.algo_data = smbus_cmi; + smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus_cmi->adapter.dev.parent = &device->dev; + + if (i2c_add_adapter(&smbus_cmi->adapter)) { + dev_err(&device->dev, "Couldn't register adapter!\n"); + goto err; + } + + return 0; + +err: + kfree(smbus_cmi); + device->driver_data = NULL; + return -EIO; +} + +static int acpi_smbus_cmi_remove(struct acpi_device *device, int type) +{ + struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device); + + i2c_del_adapter(&smbus_cmi->adapter); + kfree(smbus_cmi); + device->driver_data = NULL; + + return 0; +} + +static struct acpi_driver acpi_smbus_cmi_driver = { + .name = ACPI_SMBUS_HC_DEVICE_NAME, + .class = ACPI_SMBUS_HC_CLASS, + .ids = acpi_smbus_cmi_ids, + .ops = { + .add = acpi_smbus_cmi_add, + .remove = acpi_smbus_cmi_remove, + }, +}; + +static int __init acpi_smbus_cmi_init(void) +{ + return acpi_bus_register_driver(&acpi_smbus_cmi_driver); +} + +static void __exit acpi_smbus_cmi_exit(void) +{ + acpi_bus_unregister_driver(&acpi_smbus_cmi_driver); +} + +module_init(acpi_smbus_cmi_init); +module_exit(acpi_smbus_cmi_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>"); +MODULE_DESCRIPTION("ACPI SMBus CMI driver"); diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c new file mode 100644 index 00000000..3cad8fec --- /dev/null +++ b/drivers/i2c/busses/i2c-sh7760.c @@ -0,0 +1,577 @@ +/* + * I2C bus driver for the SH7760 I2C Interfaces. + * + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com> + * + * licensed under the terms outlined in the file COPYING. + * + */ + +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/io.h> + +#include <asm/clock.h> +#include <asm/i2c-sh7760.h> + +/* register offsets */ +#define I2CSCR 0x0 /* slave ctrl */ +#define I2CMCR 0x4 /* master ctrl */ +#define I2CSSR 0x8 /* slave status */ +#define I2CMSR 0xC /* master status */ +#define I2CSIER 0x10 /* slave irq enable */ +#define I2CMIER 0x14 /* master irq enable */ +#define I2CCCR 0x18 /* clock dividers */ +#define I2CSAR 0x1c /* slave address */ +#define I2CMAR 0x20 /* master address */ +#define I2CRXTX 0x24 /* data port */ +#define I2CFCR 0x28 /* fifo control */ +#define I2CFSR 0x2C /* fifo status */ +#define I2CFIER 0x30 /* fifo irq enable */ +#define I2CRFDR 0x34 /* rx fifo count */ +#define I2CTFDR 0x38 /* tx fifo count */ + +#define REGSIZE 0x3C + +#define MCR_MDBS 0x80 /* non-fifo mode switch */ +#define MCR_FSCL 0x40 /* override SCL pin */ +#define MCR_FSDA 0x20 /* override SDA pin */ +#define MCR_OBPC 0x10 /* override pins */ +#define MCR_MIE 0x08 /* master if enable */ +#define MCR_TSBE 0x04 +#define MCR_FSB 0x02 /* force stop bit */ +#define MCR_ESG 0x01 /* en startbit gen. */ + +#define MSR_MNR 0x40 /* nack received */ +#define MSR_MAL 0x20 /* arbitration lost */ +#define MSR_MST 0x10 /* sent a stop */ +#define MSR_MDE 0x08 +#define MSR_MDT 0x04 +#define MSR_MDR 0x02 +#define MSR_MAT 0x01 /* slave addr xfer done */ + +#define MIE_MNRE 0x40 /* nack irq en */ +#define MIE_MALE 0x20 /* arblos irq en */ +#define MIE_MSTE 0x10 /* stop irq en */ +#define MIE_MDEE 0x08 +#define MIE_MDTE 0x04 +#define MIE_MDRE 0x02 +#define MIE_MATE 0x01 /* address sent irq en */ + +#define FCR_RFRST 0x02 /* reset rx fifo */ +#define FCR_TFRST 0x01 /* reset tx fifo */ + +#define FSR_TEND 0x04 /* last byte sent */ +#define FSR_RDF 0x02 /* rx fifo trigger */ +#define FSR_TDFE 0x01 /* tx fifo empty */ + +#define FIER_TEIE 0x04 /* tx fifo empty irq en */ +#define FIER_RXIE 0x02 /* rx fifo trig irq en */ +#define FIER_TXIE 0x01 /* tx fifo trig irq en */ + +#define FIFO_SIZE 16 + +struct cami2c { + void __iomem *iobase; + struct i2c_adapter adap; + + /* message processing */ + struct i2c_msg *msg; +#define IDF_SEND 1 +#define IDF_RECV 2 +#define IDF_STOP 4 + int flags; + +#define IDS_DONE 1 +#define IDS_ARBLOST 2 +#define IDS_NACK 4 + int status; + struct completion xfer_done; + + int irq; + struct resource *ioarea; +}; + +static inline void OUT32(struct cami2c *cam, int reg, unsigned long val) +{ + __raw_writel(val, (unsigned long)cam->iobase + reg); +} + +static inline unsigned long IN32(struct cami2c *cam, int reg) +{ + return __raw_readl((unsigned long)cam->iobase + reg); +} + +static irqreturn_t sh7760_i2c_irq(int irq, void *ptr) +{ + struct cami2c *id = ptr; + struct i2c_msg *msg = id->msg; + char *data = msg->buf; + unsigned long msr, fsr, fier, len; + + msr = IN32(id, I2CMSR); + fsr = IN32(id, I2CFSR); + + /* arbitration lost */ + if (msr & MSR_MAL) { + OUT32(id, I2CMCR, 0); + OUT32(id, I2CSCR, 0); + OUT32(id, I2CSAR, 0); + id->status |= IDS_DONE | IDS_ARBLOST; + goto out; + } + + if (msr & MSR_MNR) { + /* NACK handling is very screwed up. After receiving a + * NAK IRQ one has to wait a bit before writing to any + * registers, or the ctl will lock up. After that delay + * do a normal i2c stop. Then wait at least 1 ms before + * attempting another transfer or ctl will stop working + */ + udelay(100); /* wait or risk ctl hang */ + OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); + OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); + OUT32(id, I2CFIER, 0); + OUT32(id, I2CMIER, MIE_MSTE); + OUT32(id, I2CSCR, 0); + OUT32(id, I2CSAR, 0); + id->status |= IDS_NACK; + msr &= ~MSR_MAT; + fsr = 0; + /* In some cases the MST bit is also set. */ + } + + /* i2c-stop was sent */ + if (msr & MSR_MST) { + id->status |= IDS_DONE; + goto out; + } + + /* i2c slave addr was sent; set to "normal" operation */ + if (msr & MSR_MAT) + OUT32(id, I2CMCR, MCR_MIE); + + fier = IN32(id, I2CFIER); + + if (fsr & FSR_RDF) { + len = IN32(id, I2CRFDR); + if (msg->len <= len) { + if (id->flags & IDF_STOP) { + OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); + OUT32(id, I2CFIER, 0); + /* manual says: wait >= 0.5 SCL times */ + udelay(5); + /* next int should be MST */ + } else { + id->status |= IDS_DONE; + /* keep the RDF bit: ctrl holds SCL low + * until the setup for the next i2c_msg + * clears this bit. + */ + fsr &= ~FSR_RDF; + } + } + while (msg->len && len) { + *data++ = IN32(id, I2CRXTX); + msg->len--; + len--; + } + + if (msg->len) { + len = (msg->len >= FIFO_SIZE) ? FIFO_SIZE - 1 + : msg->len - 1; + + OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xf) << 4)); + } + + } else if (id->flags & IDF_SEND) { + if ((fsr & FSR_TEND) && (msg->len < 1)) { + if (id->flags & IDF_STOP) { + OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); + } else { + id->status |= IDS_DONE; + /* keep the TEND bit: ctl holds SCL low + * until the setup for the next i2c_msg + * clears this bit. + */ + fsr &= ~FSR_TEND; + } + } + if (fsr & FSR_TDFE) { + while (msg->len && (IN32(id, I2CTFDR) < FIFO_SIZE)) { + OUT32(id, I2CRXTX, *data++); + msg->len--; + } + + if (msg->len < 1) { + fier &= ~FIER_TXIE; + OUT32(id, I2CFIER, fier); + } else { + len = (msg->len >= FIFO_SIZE) ? 2 : 0; + OUT32(id, I2CFCR, + FCR_RFRST | ((len & 3) << 2)); + } + } + } +out: + if (id->status & IDS_DONE) { + OUT32(id, I2CMIER, 0); + OUT32(id, I2CFIER, 0); + id->msg = NULL; + complete(&id->xfer_done); + } + /* clear status flags and ctrl resumes work */ + OUT32(id, I2CMSR, ~msr); + OUT32(id, I2CFSR, ~fsr); + OUT32(id, I2CSSR, 0); + + return IRQ_HANDLED; +} + + +/* prepare and start a master receive operation */ +static void sh7760_i2c_mrecv(struct cami2c *id) +{ + int len; + + id->flags |= IDF_RECV; + + /* set the slave addr reg; otherwise rcv wont work! */ + OUT32(id, I2CSAR, 0xfe); + OUT32(id, I2CMAR, (id->msg->addr << 1) | 1); + + /* adjust rx fifo trigger */ + if (id->msg->len >= FIFO_SIZE) + len = FIFO_SIZE - 1; /* trigger at fifo full */ + else + len = id->msg->len - 1; /* trigger before all received */ + + OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); + OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xF) << 4)); + + OUT32(id, I2CMSR, 0); + OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); + OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); + OUT32(id, I2CFIER, FIER_RXIE); +} + +/* prepare and start a master send operation */ +static void sh7760_i2c_msend(struct cami2c *id) +{ + int len; + + id->flags |= IDF_SEND; + + /* set the slave addr reg; otherwise xmit wont work! */ + OUT32(id, I2CSAR, 0xfe); + OUT32(id, I2CMAR, (id->msg->addr << 1) | 0); + + /* adjust tx fifo trigger */ + if (id->msg->len >= FIFO_SIZE) + len = 2; /* trig: 2 bytes left in TX fifo */ + else + len = 0; /* trig: 8 bytes left in TX fifo */ + + OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); + OUT32(id, I2CFCR, FCR_RFRST | ((len & 3) << 2)); + + while (id->msg->len && IN32(id, I2CTFDR) < FIFO_SIZE) { + OUT32(id, I2CRXTX, *(id->msg->buf)); + (id->msg->len)--; + (id->msg->buf)++; + } + + OUT32(id, I2CMSR, 0); + OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); + OUT32(id, I2CFSR, 0); + OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); + OUT32(id, I2CFIER, FIER_TEIE | (id->msg->len ? FIER_TXIE : 0)); +} + +static inline int sh7760_i2c_busy_check(struct cami2c *id) +{ + return (IN32(id, I2CMCR) & MCR_FSDA); +} + +static int sh7760_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct cami2c *id = adap->algo_data; + int i, retr; + + if (sh7760_i2c_busy_check(id)) { + dev_err(&adap->dev, "sh7760-i2c%d: bus busy!\n", adap->nr); + return -EBUSY; + } + + i = 0; + while (i < num) { + retr = adap->retries; +retry: + id->flags = ((i == (num-1)) ? IDF_STOP : 0); + id->status = 0; + id->msg = msgs; + init_completion(&id->xfer_done); + + if (msgs->flags & I2C_M_RD) + sh7760_i2c_mrecv(id); + else + sh7760_i2c_msend(id); + + wait_for_completion(&id->xfer_done); + + if (id->status == 0) { + num = -EIO; + break; + } + + if (id->status & IDS_NACK) { + /* wait a bit or i2c module stops working */ + mdelay(1); + num = -EREMOTEIO; + break; + } + + if (id->status & IDS_ARBLOST) { + if (retr--) { + mdelay(2); + goto retry; + } + num = -EREMOTEIO; + break; + } + + msgs++; + i++; + } + + id->msg = NULL; + id->flags = 0; + id->status = 0; + + OUT32(id, I2CMCR, 0); + OUT32(id, I2CMSR, 0); + OUT32(id, I2CMIER, 0); + OUT32(id, I2CFIER, 0); + + /* reset slave module registers too: master mode enables slave + * module for receive ops (ack, data). Without this reset, + * eternal bus activity might be reported after NACK / ARBLOST. + */ + OUT32(id, I2CSCR, 0); + OUT32(id, I2CSAR, 0); + OUT32(id, I2CSSR, 0); + + return num; +} + +static u32 sh7760_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm sh7760_i2c_algo = { + .master_xfer = sh7760_i2c_master_xfer, + .functionality = sh7760_i2c_func, +}; + +/* calculate CCR register setting for a desired scl clock. SCL clock is + * derived from I2C module clock (iclk) which in turn is derived from + * peripheral module clock (mclk, usually around 33MHz): + * iclk = mclk/(CDF + 1). iclk must be < 20MHz. + * scl = iclk/(SCGD*8 + 20). + */ +static int __devinit calc_CCR(unsigned long scl_hz) +{ + struct clk *mclk; + unsigned long mck, m1, dff, odff, iclk; + signed char cdf, cdfm; + int scgd, scgdm, scgds; + + mclk = clk_get(NULL, "peripheral_clk"); + if (IS_ERR(mclk)) { + return PTR_ERR(mclk); + } else { + mck = mclk->rate; + clk_put(mclk); + } + + odff = scl_hz; + scgdm = cdfm = m1 = 0; + for (cdf = 3; cdf >= 0; cdf--) { + iclk = mck / (1 + cdf); + if (iclk >= 20000000) + continue; + scgds = ((iclk / scl_hz) - 20) >> 3; + for (scgd = scgds; (scgd < 63) && scgd <= scgds + 1; scgd++) { + m1 = iclk / (20 + (scgd << 3)); + dff = abs(scl_hz - m1); + if (dff < odff) { + odff = dff; + cdfm = cdf; + scgdm = scgd; + } + } + } + /* fail if more than 25% off of requested SCL */ + if (odff > (scl_hz >> 2)) + return -EINVAL; + + /* create a CCR register value */ + return ((scgdm << 2) | cdfm); +} + +static int __devinit sh7760_i2c_probe(struct platform_device *pdev) +{ + struct sh7760_i2c_platdata *pd; + struct resource *res; + struct cami2c *id; + int ret; + + pd = pdev->dev.platform_data; + if (!pd) { + dev_err(&pdev->dev, "no platform_data!\n"); + ret = -ENODEV; + goto out0; + } + + id = kzalloc(sizeof(struct cami2c), GFP_KERNEL); + if (!id) { + dev_err(&pdev->dev, "no mem for private data\n"); + ret = -ENOMEM; + goto out0; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no mmio resources\n"); + ret = -ENODEV; + goto out1; + } + + id->ioarea = request_mem_region(res->start, REGSIZE, pdev->name); + if (!id->ioarea) { + dev_err(&pdev->dev, "mmio already reserved\n"); + ret = -EBUSY; + goto out1; + } + + id->iobase = ioremap(res->start, REGSIZE); + if (!id->iobase) { + dev_err(&pdev->dev, "cannot ioremap\n"); + ret = -ENODEV; + goto out2; + } + + id->irq = platform_get_irq(pdev, 0); + + id->adap.nr = pdev->id; + id->adap.algo = &sh7760_i2c_algo; + id->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + id->adap.retries = 3; + id->adap.algo_data = id; + id->adap.dev.parent = &pdev->dev; + snprintf(id->adap.name, sizeof(id->adap.name), + "SH7760 I2C at %08lx", (unsigned long)res->start); + + OUT32(id, I2CMCR, 0); + OUT32(id, I2CMSR, 0); + OUT32(id, I2CMIER, 0); + OUT32(id, I2CMAR, 0); + OUT32(id, I2CSIER, 0); + OUT32(id, I2CSAR, 0); + OUT32(id, I2CSCR, 0); + OUT32(id, I2CSSR, 0); + OUT32(id, I2CFIER, 0); + OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); + OUT32(id, I2CFSR, 0); + + ret = calc_CCR(pd->speed_khz * 1000); + if (ret < 0) { + dev_err(&pdev->dev, "invalid SCL clock: %dkHz\n", + pd->speed_khz); + goto out3; + } + OUT32(id, I2CCCR, ret); + + if (request_irq(id->irq, sh7760_i2c_irq, IRQF_DISABLED, + SH7760_I2C_DEVNAME, id)) { + dev_err(&pdev->dev, "cannot get irq %d\n", id->irq); + ret = -EBUSY; + goto out3; + } + + ret = i2c_add_numbered_adapter(&id->adap); + if (ret < 0) { + dev_err(&pdev->dev, "reg adap failed: %d\n", ret); + goto out4; + } + + platform_set_drvdata(pdev, id); + + dev_info(&pdev->dev, "%d kHz mmio %08x irq %d\n", + pd->speed_khz, res->start, id->irq); + + return 0; + +out4: + free_irq(id->irq, id); +out3: + iounmap(id->iobase); +out2: + release_resource(id->ioarea); + kfree(id->ioarea); +out1: + kfree(id); +out0: + return ret; +} + +static int __devexit sh7760_i2c_remove(struct platform_device *pdev) +{ + struct cami2c *id = platform_get_drvdata(pdev); + + i2c_del_adapter(&id->adap); + free_irq(id->irq, id); + iounmap(id->iobase); + release_resource(id->ioarea); + kfree(id->ioarea); + kfree(id); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver sh7760_i2c_drv = { + .driver = { + .name = SH7760_I2C_DEVNAME, + .owner = THIS_MODULE, + }, + .probe = sh7760_i2c_probe, + .remove = __devexit_p(sh7760_i2c_remove), +}; + +static int __init sh7760_i2c_init(void) +{ + return platform_driver_register(&sh7760_i2c_drv); +} + +static void __exit sh7760_i2c_exit(void) +{ + platform_driver_unregister(&sh7760_i2c_drv); +} + +module_init(sh7760_i2c_init); +module_exit(sh7760_i2c_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SH7760 I2C bus driver"); +MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c new file mode 100644 index 00000000..f633a53b --- /dev/null +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -0,0 +1,739 @@ +/* + * SuperH Mobile I2C Controller + * + * Copyright (C) 2008 Magnus Damm + * + * Portions of the code based on out-of-tree driver i2c-sh7343.c + * Copyright (c) 2006 Carlos Munoz <carlos@kenati.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 + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/pm_runtime.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/i2c/i2c-sh_mobile.h> + +/* Transmit operation: */ +/* */ +/* 0 byte transmit */ +/* BUS: S A8 ACK P */ +/* IRQ: DTE WAIT */ +/* ICIC: */ +/* ICCR: 0x94 0x90 */ +/* ICDR: A8 */ +/* */ +/* 1 byte transmit */ +/* BUS: S A8 ACK D8(1) ACK P */ +/* IRQ: DTE WAIT WAIT */ +/* ICIC: -DTE */ +/* ICCR: 0x94 0x90 */ +/* ICDR: A8 D8(1) */ +/* */ +/* 2 byte transmit */ +/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P */ +/* IRQ: DTE WAIT WAIT WAIT */ +/* ICIC: -DTE */ +/* ICCR: 0x94 0x90 */ +/* ICDR: A8 D8(1) D8(2) */ +/* */ +/* 3 bytes or more, +---------+ gets repeated */ +/* */ +/* */ +/* Receive operation: */ +/* */ +/* 0 byte receive - not supported since slave may hold SDA low */ +/* */ +/* 1 byte receive [TX] | [RX] */ +/* BUS: S A8 ACK | D8(1) ACK P */ +/* IRQ: DTE WAIT | WAIT DTE */ +/* ICIC: -DTE | +DTE */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) */ +/* */ +/* 2 byte receive [TX]| [RX] */ +/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK P */ +/* IRQ: DTE WAIT | WAIT WAIT DTE */ +/* ICIC: -DTE | +DTE */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) D8(2) */ +/* */ +/* 3 byte receive [TX] | [RX] */ +/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK D8(3) ACK P */ +/* IRQ: DTE WAIT | WAIT WAIT WAIT DTE */ +/* ICIC: -DTE | +DTE */ +/* ICCR: 0x94 0x81 | 0xc0 */ +/* ICDR: A8 | D8(1) D8(2) D8(3) */ +/* */ +/* 4 bytes or more, this part is repeated +---------+ */ +/* */ +/* */ +/* Interrupt order and BUSY flag */ +/* ___ _ */ +/* SDA ___\___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAA___/ */ +/* SCL \_/1\_/2\_/3\_/4\_/5\_/6\_/7\_/8\___/9\_____/ */ +/* */ +/* S D7 D6 D5 D4 D3 D2 D1 D0 P */ +/* ___ */ +/* WAIT IRQ ________________________________/ \___________ */ +/* TACK IRQ ____________________________________/ \_______ */ +/* DTE IRQ __________________________________________/ \_ */ +/* AL IRQ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ +/* _______________________________________________ */ +/* BUSY __/ \_ */ +/* */ + +enum sh_mobile_i2c_op { + OP_START = 0, + OP_TX_FIRST, + OP_TX, + OP_TX_STOP, + OP_TX_TO_RX, + OP_RX, + OP_RX_STOP, + OP_RX_STOP_DATA, +}; + +struct sh_mobile_i2c_data { + struct device *dev; + void __iomem *reg; + struct i2c_adapter adap; + unsigned long bus_speed; + struct clk *clk; + u_int8_t icic; + u_int8_t iccl; + u_int8_t icch; + u_int8_t flags; + + spinlock_t lock; + wait_queue_head_t wait; + struct i2c_msg *msg; + int pos; + int sr; +}; + +#define IIC_FLAG_HAS_ICIC67 (1 << 0) + +#define NORMAL_SPEED 100000 /* FAST_SPEED 400000 */ + +/* Register offsets */ +#define ICDR 0x00 +#define ICCR 0x04 +#define ICSR 0x08 +#define ICIC 0x0c +#define ICCL 0x10 +#define ICCH 0x14 + +/* Register bits */ +#define ICCR_ICE 0x80 +#define ICCR_RACK 0x40 +#define ICCR_TRS 0x10 +#define ICCR_BBSY 0x04 +#define ICCR_SCP 0x01 + +#define ICSR_SCLM 0x80 +#define ICSR_SDAM 0x40 +#define SW_DONE 0x20 +#define ICSR_BUSY 0x10 +#define ICSR_AL 0x08 +#define ICSR_TACK 0x04 +#define ICSR_WAIT 0x02 +#define ICSR_DTE 0x01 + +#define ICIC_ICCLB8 0x80 +#define ICIC_ICCHB8 0x40 +#define ICIC_ALE 0x08 +#define ICIC_TACKE 0x04 +#define ICIC_WAITE 0x02 +#define ICIC_DTEE 0x01 + +static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data) +{ + if (offs == ICIC) + data |= pd->icic; + + iowrite8(data, pd->reg + offs); +} + +static unsigned char iic_rd(struct sh_mobile_i2c_data *pd, int offs) +{ + return ioread8(pd->reg + offs); +} + +static void iic_set_clr(struct sh_mobile_i2c_data *pd, int offs, + unsigned char set, unsigned char clr) +{ + iic_wr(pd, offs, (iic_rd(pd, offs) | set) & ~clr); +} + +static void activate_ch(struct sh_mobile_i2c_data *pd) +{ + unsigned long i2c_clk; + u_int32_t num; + u_int32_t denom; + u_int32_t tmp; + + /* Wake up device and enable clock */ + pm_runtime_get_sync(pd->dev); + clk_enable(pd->clk); + + /* Get clock rate after clock is enabled */ + i2c_clk = clk_get_rate(pd->clk); + + /* Calculate the value for iccl. From the data sheet: + * iccl = (p clock / transfer rate) * (L / (L + H)) + * where L and H are the SCL low/high ratio (5/4 in this case). + * We also round off the result. + */ + num = i2c_clk * 5; + denom = pd->bus_speed * 9; + tmp = num * 10 / denom; + if (tmp % 10 >= 5) + pd->iccl = (u_int8_t)((num/denom) + 1); + else + pd->iccl = (u_int8_t)(num/denom); + + /* one more bit of ICCL in ICIC */ + if (pd->flags & IIC_FLAG_HAS_ICIC67) { + if ((num/denom) > 0xff) + pd->icic |= ICIC_ICCLB8; + else + pd->icic &= ~ICIC_ICCLB8; + } + + /* Calculate the value for icch. From the data sheet: + icch = (p clock / transfer rate) * (H / (L + H)) */ + num = i2c_clk * 4; + tmp = num * 10 / denom; + if (tmp % 10 >= 5) + pd->icch = (u_int8_t)((num/denom) + 1); + else + pd->icch = (u_int8_t)(num/denom); + + /* one more bit of ICCH in ICIC */ + if (pd->flags & IIC_FLAG_HAS_ICIC67) { + if ((num/denom) > 0xff) + pd->icic |= ICIC_ICCHB8; + else + pd->icic &= ~ICIC_ICCHB8; + } + + /* Enable channel and configure rx ack */ + iic_set_clr(pd, ICCR, ICCR_ICE, 0); + + /* Mask all interrupts */ + iic_wr(pd, ICIC, 0); + + /* Set the clock */ + iic_wr(pd, ICCL, pd->iccl); + iic_wr(pd, ICCH, pd->icch); +} + +static void deactivate_ch(struct sh_mobile_i2c_data *pd) +{ + /* Clear/disable interrupts */ + iic_wr(pd, ICSR, 0); + iic_wr(pd, ICIC, 0); + + /* Disable channel */ + iic_set_clr(pd, ICCR, 0, ICCR_ICE); + + /* Disable clock and mark device as idle */ + clk_disable(pd->clk); + pm_runtime_put_sync(pd->dev); +} + +static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, + enum sh_mobile_i2c_op op, unsigned char data) +{ + unsigned char ret = 0; + unsigned long flags; + + dev_dbg(pd->dev, "op %d, data in 0x%02x\n", op, data); + + spin_lock_irqsave(&pd->lock, flags); + + switch (op) { + case OP_START: /* issue start and trigger DTE interrupt */ + iic_wr(pd, ICCR, 0x94); + break; + case OP_TX_FIRST: /* disable DTE interrupt and write data */ + iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE); + iic_wr(pd, ICDR, data); + break; + case OP_TX: /* write data */ + iic_wr(pd, ICDR, data); + break; + case OP_TX_STOP: /* write data and issue a stop afterwards */ + iic_wr(pd, ICDR, data); + iic_wr(pd, ICCR, 0x90); + break; + case OP_TX_TO_RX: /* select read mode */ + iic_wr(pd, ICCR, 0x81); + break; + case OP_RX: /* just read data */ + ret = iic_rd(pd, ICDR); + break; + case OP_RX_STOP: /* enable DTE interrupt, issue stop */ + iic_wr(pd, ICIC, + ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); + iic_wr(pd, ICCR, 0xc0); + break; + case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */ + iic_wr(pd, ICIC, + ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); + ret = iic_rd(pd, ICDR); + iic_wr(pd, ICCR, 0xc0); + break; + } + + spin_unlock_irqrestore(&pd->lock, flags); + + dev_dbg(pd->dev, "op %d, data out 0x%02x\n", op, ret); + return ret; +} + +static int sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) +{ + if (pd->pos == -1) + return 1; + + return 0; +} + +static int sh_mobile_i2c_is_last_byte(struct sh_mobile_i2c_data *pd) +{ + if (pd->pos == (pd->msg->len - 1)) + return 1; + + return 0; +} + +static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd, + unsigned char *buf) +{ + switch (pd->pos) { + case -1: + *buf = (pd->msg->addr & 0x7f) << 1; + *buf |= (pd->msg->flags & I2C_M_RD) ? 1 : 0; + break; + default: + *buf = pd->msg->buf[pd->pos]; + } +} + +static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) +{ + unsigned char data; + + if (pd->pos == pd->msg->len) + return 1; + + sh_mobile_i2c_get_data(pd, &data); + + if (sh_mobile_i2c_is_last_byte(pd)) + i2c_op(pd, OP_TX_STOP, data); + else if (sh_mobile_i2c_is_first_byte(pd)) + i2c_op(pd, OP_TX_FIRST, data); + else + i2c_op(pd, OP_TX, data); + + pd->pos++; + return 0; +} + +static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) +{ + unsigned char data; + int real_pos; + + do { + if (pd->pos <= -1) { + sh_mobile_i2c_get_data(pd, &data); + + if (sh_mobile_i2c_is_first_byte(pd)) + i2c_op(pd, OP_TX_FIRST, data); + else + i2c_op(pd, OP_TX, data); + break; + } + + if (pd->pos == 0) { + i2c_op(pd, OP_TX_TO_RX, 0); + break; + } + + real_pos = pd->pos - 2; + + if (pd->pos == pd->msg->len) { + if (real_pos < 0) { + i2c_op(pd, OP_RX_STOP, 0); + break; + } + data = i2c_op(pd, OP_RX_STOP_DATA, 0); + } else + data = i2c_op(pd, OP_RX, 0); + + if (real_pos >= 0) + pd->msg->buf[real_pos] = data; + } while (0); + + pd->pos++; + return pd->pos == (pd->msg->len + 2); +} + +static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) +{ + struct platform_device *dev = dev_id; + struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev); + unsigned char sr; + int wakeup; + + sr = iic_rd(pd, ICSR); + pd->sr |= sr; /* remember state */ + + dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr, + (pd->msg->flags & I2C_M_RD) ? "read" : "write", + pd->pos, pd->msg->len); + + if (sr & (ICSR_AL | ICSR_TACK)) { + /* don't interrupt transaction - continue to issue stop */ + iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK)); + wakeup = 0; + } else if (pd->msg->flags & I2C_M_RD) + wakeup = sh_mobile_i2c_isr_rx(pd); + else + wakeup = sh_mobile_i2c_isr_tx(pd); + + if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */ + iic_wr(pd, ICSR, sr & ~ICSR_WAIT); + + if (wakeup) { + pd->sr |= SW_DONE; + wake_up(&pd->wait); + } + + return IRQ_HANDLED; +} + +static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg) +{ + if (usr_msg->len == 0 && (usr_msg->flags & I2C_M_RD)) { + dev_err(pd->dev, "Unsupported zero length i2c read\n"); + return -EIO; + } + + /* Initialize channel registers */ + iic_set_clr(pd, ICCR, 0, ICCR_ICE); + + /* Enable channel and configure rx ack */ + iic_set_clr(pd, ICCR, ICCR_ICE, 0); + + /* Set the clock */ + iic_wr(pd, ICCL, pd->iccl); + iic_wr(pd, ICCH, pd->icch); + + pd->msg = usr_msg; + pd->pos = -1; + pd->sr = 0; + + /* Enable all interrupts to begin with */ + iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE); + return 0; +} + +static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, + int num) +{ + struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter); + struct i2c_msg *msg; + int err = 0; + u_int8_t val; + int i, k, retry_count; + + activate_ch(pd); + + /* Process all messages */ + for (i = 0; i < num; i++) { + msg = &msgs[i]; + + err = start_ch(pd, msg); + if (err) + break; + + i2c_op(pd, OP_START, 0); + + /* The interrupt handler takes care of the rest... */ + k = wait_event_timeout(pd->wait, + pd->sr & (ICSR_TACK | SW_DONE), + 5 * HZ); + if (!k) + dev_err(pd->dev, "Transfer request timed out\n"); + + retry_count = 1000; +again: + val = iic_rd(pd, ICSR); + + dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr); + + /* the interrupt handler may wake us up before the + * transfer is finished, so poll the hardware + * until we're done. + */ + if (val & ICSR_BUSY) { + udelay(10); + if (retry_count--) + goto again; + + err = -EIO; + dev_err(pd->dev, "Polling timed out\n"); + break; + } + + /* handle missing acknowledge and arbitration lost */ + if ((val | pd->sr) & (ICSR_TACK | ICSR_AL)) { + err = -EIO; + break; + } + } + + deactivate_ch(pd); + + if (!err) + err = num; + return err; +} + +static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm sh_mobile_i2c_algorithm = { + .functionality = sh_mobile_i2c_func, + .master_xfer = sh_mobile_i2c_xfer, +}; + +static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook) +{ + struct resource *res; + int ret = -ENXIO; + int n, k = 0; + + while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) { + for (n = res->start; hook && n <= res->end; n++) { + if (request_irq(n, sh_mobile_i2c_isr, IRQF_DISABLED, + dev_name(&dev->dev), dev)) { + for (n--; n >= res->start; n--) + free_irq(n, dev); + + goto rollback; + } + } + k++; + } + + if (hook) + return k > 0 ? 0 : -ENOENT; + + ret = 0; + + rollback: + k--; + + while (k >= 0) { + res = platform_get_resource(dev, IORESOURCE_IRQ, k); + for (n = res->start; n <= res->end; n++) + free_irq(n, dev); + + k--; + } + + return ret; +} + +static int sh_mobile_i2c_probe(struct platform_device *dev) +{ + struct i2c_sh_mobile_platform_data *pdata = dev->dev.platform_data; + struct sh_mobile_i2c_data *pd; + struct i2c_adapter *adap; + struct resource *res; + int size; + int ret; + + pd = kzalloc(sizeof(struct sh_mobile_i2c_data), GFP_KERNEL); + if (pd == NULL) { + dev_err(&dev->dev, "cannot allocate private data\n"); + return -ENOMEM; + } + + pd->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(pd->clk)) { + dev_err(&dev->dev, "cannot get clock\n"); + ret = PTR_ERR(pd->clk); + goto err; + } + + ret = sh_mobile_i2c_hook_irqs(dev, 1); + if (ret) { + dev_err(&dev->dev, "cannot request IRQ\n"); + goto err_clk; + } + + pd->dev = &dev->dev; + platform_set_drvdata(dev, pd); + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&dev->dev, "cannot find IO resource\n"); + ret = -ENOENT; + goto err_irq; + } + + size = resource_size(res); + + pd->reg = ioremap(res->start, size); + if (pd->reg == NULL) { + dev_err(&dev->dev, "cannot map IO\n"); + ret = -ENXIO; + goto err_irq; + } + + /* Use platformd data bus speed or NORMAL_SPEED */ + pd->bus_speed = NORMAL_SPEED; + if (pdata && pdata->bus_speed) + pd->bus_speed = pdata->bus_speed; + + /* The IIC blocks on SH-Mobile ARM processors + * come with two new bits in ICIC. + */ + if (size > 0x17) + pd->flags |= IIC_FLAG_HAS_ICIC67; + + /* Enable Runtime PM for this device. + * + * Also tell the Runtime PM core to ignore children + * for this device since it is valid for us to suspend + * this I2C master driver even though the slave devices + * on the I2C bus may not be suspended. + * + * The state of the I2C hardware bus is unaffected by + * the Runtime PM state. + */ + pm_suspend_ignore_children(&dev->dev, true); + pm_runtime_enable(&dev->dev); + + /* setup the private data */ + adap = &pd->adap; + i2c_set_adapdata(adap, pd); + + adap->owner = THIS_MODULE; + adap->algo = &sh_mobile_i2c_algorithm; + adap->dev.parent = &dev->dev; + adap->retries = 5; + adap->nr = dev->id; + + strlcpy(adap->name, dev->name, sizeof(adap->name)); + + spin_lock_init(&pd->lock); + init_waitqueue_head(&pd->wait); + + ret = i2c_add_numbered_adapter(adap); + if (ret < 0) { + dev_err(&dev->dev, "cannot add numbered adapter\n"); + goto err_all; + } + + dev_info(&dev->dev, "I2C adapter %d with bus speed %lu Hz\n", + adap->nr, pd->bus_speed); + return 0; + + err_all: + iounmap(pd->reg); + err_irq: + sh_mobile_i2c_hook_irqs(dev, 0); + err_clk: + clk_put(pd->clk); + err: + kfree(pd); + return ret; +} + +static int sh_mobile_i2c_remove(struct platform_device *dev) +{ + struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev); + + i2c_del_adapter(&pd->adap); + iounmap(pd->reg); + sh_mobile_i2c_hook_irqs(dev, 0); + clk_put(pd->clk); + pm_runtime_disable(&dev->dev); + kfree(pd); + return 0; +} + +static int sh_mobile_i2c_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static const struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = { + .runtime_suspend = sh_mobile_i2c_runtime_nop, + .runtime_resume = sh_mobile_i2c_runtime_nop, +}; + +static struct platform_driver sh_mobile_i2c_driver = { + .driver = { + .name = "i2c-sh_mobile", + .owner = THIS_MODULE, + .pm = &sh_mobile_i2c_dev_pm_ops, + }, + .probe = sh_mobile_i2c_probe, + .remove = sh_mobile_i2c_remove, +}; + +static int __init sh_mobile_i2c_adap_init(void) +{ + return platform_driver_register(&sh_mobile_i2c_driver); +} + +static void __exit sh_mobile_i2c_adap_exit(void) +{ + platform_driver_unregister(&sh_mobile_i2c_driver); +} + +subsys_initcall(sh_mobile_i2c_adap_init); +module_exit(sh_mobile_i2c_adap_exit); + +MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver"); +MODULE_AUTHOR("Magnus Damm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-sh_mobile"); diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c new file mode 100644 index 00000000..0fe505d7 --- /dev/null +++ b/drivers/i2c/busses/i2c-sibyte.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2004 Steven J. Hill + * Copyright (C) 2001,2002,2003 Broadcom Corporation + * Copyright (C) 1995-2000 Simon G. Vogl + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <asm/sibyte/sb1250_regs.h> +#include <asm/sibyte/sb1250_smbus.h> + + +struct i2c_algo_sibyte_data { + void *data; /* private data */ + int bus; /* which bus */ + void *reg_base; /* CSR base */ +}; + +/* ----- global defines ----------------------------------------------- */ +#define SMB_CSR(a,r) ((long)(a->reg_base + r)) + + +static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; + int data_bytes = 0; + int error; + + while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) + ; + + switch (size) { + case I2C_SMBUS_QUICK: + csr_out32((V_SMB_ADDR(addr) | + (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) | + V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START)); + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 1; + } else { + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + case I2C_SMBUS_BYTE_DATA: + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 1; + } else { + csr_out32(V_SMB_LB(data->byte), + SMB_CSR(adap, R_SMB_DATA)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + case I2C_SMBUS_WORD_DATA: + csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD)); + if (read_write == I2C_SMBUS_READ) { + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE), + SMB_CSR(adap, R_SMB_START)); + data_bytes = 2; + } else { + csr_out32(V_SMB_LB(data->word & 0xff), + SMB_CSR(adap, R_SMB_DATA)); + csr_out32(V_SMB_MB(data->word >> 8), + SMB_CSR(adap, R_SMB_DATA)); + csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE), + SMB_CSR(adap, R_SMB_START)); + } + break; + default: + return -EOPNOTSUPP; + } + + while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY) + ; + + error = csr_in32(SMB_CSR(adap, R_SMB_STATUS)); + if (error & M_SMB_ERROR) { + /* Clear error bit by writing a 1 */ + csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS)); + return (error & M_SMB_ERROR_TYPE) ? -EIO : -ENXIO; + } + + if (data_bytes == 1) + data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff; + if (data_bytes == 2) + data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff; + + return 0; +} + +static u32 bit_func(struct i2c_adapter *adap) +{ + return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA); +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static const struct i2c_algorithm i2c_sibyte_algo = { + .smbus_xfer = smbus_xfer, + .functionality = bit_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +static int __init i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed) +{ + struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data; + + /* Register new adapter to i2c module... */ + i2c_adap->algo = &i2c_sibyte_algo; + + /* Set the requested frequency. */ + csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ)); + csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL)); + + return i2c_add_numbered_adapter(i2c_adap); +} + + +static struct i2c_algo_sibyte_data sibyte_board_data[2] = { + { NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) }, + { NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) } +}; + +static struct i2c_adapter sibyte_board_adapter[2] = { + { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = NULL, + .algo_data = &sibyte_board_data[0], + .nr = 0, + .name = "SiByte SMBus 0", + }, + { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = NULL, + .algo_data = &sibyte_board_data[1], + .nr = 1, + .name = "SiByte SMBus 1", + }, +}; + +static int __init i2c_sibyte_init(void) +{ + pr_info("i2c-sibyte: i2c SMBus adapter module for SiByte board\n"); + if (i2c_sibyte_add_bus(&sibyte_board_adapter[0], K_SMB_FREQ_100KHZ) < 0) + return -ENODEV; + if (i2c_sibyte_add_bus(&sibyte_board_adapter[1], + K_SMB_FREQ_400KHZ) < 0) { + i2c_del_adapter(&sibyte_board_adapter[0]); + return -ENODEV; + } + return 0; +} + +static void __exit i2c_sibyte_exit(void) +{ + i2c_del_adapter(&sibyte_board_adapter[0]); + i2c_del_adapter(&sibyte_board_adapter[1]); +} + +module_init(i2c_sibyte_init); +module_exit(i2c_sibyte_exit); + +MODULE_AUTHOR("Kip Walker (Broadcom Corp.), Steven J. Hill <sjhill@realitydiluted.com>"); +MODULE_DESCRIPTION("SMBus adapter routines for SiByte boards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-simtec.c b/drivers/i2c/busses/i2c-simtec.c new file mode 100644 index 00000000..2fc08fbf --- /dev/null +++ b/drivers/i2c/busses/i2c-simtec.c @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Simtec Generic I2C Controller + * + * 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 + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/io.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +struct simtec_i2c_data { + struct resource *ioarea; + void __iomem *reg; + struct i2c_adapter adap; + struct i2c_algo_bit_data bit; +}; + +#define CMD_SET_SDA (1<<2) +#define CMD_SET_SCL (1<<3) + +#define STATE_SDA (1<<0) +#define STATE_SCL (1<<1) + +/* i2c bit-bus functions */ + +static void simtec_i2c_setsda(void *pw, int state) +{ + struct simtec_i2c_data *pd = pw; + writeb(CMD_SET_SDA | (state ? STATE_SDA : 0), pd->reg); +} + +static void simtec_i2c_setscl(void *pw, int state) +{ + struct simtec_i2c_data *pd = pw; + writeb(CMD_SET_SCL | (state ? STATE_SCL : 0), pd->reg); +} + +static int simtec_i2c_getsda(void *pw) +{ + struct simtec_i2c_data *pd = pw; + return readb(pd->reg) & STATE_SDA ? 1 : 0; +} + +static int simtec_i2c_getscl(void *pw) +{ + struct simtec_i2c_data *pd = pw; + return readb(pd->reg) & STATE_SCL ? 1 : 0; +} + +/* device registration */ + +static int simtec_i2c_probe(struct platform_device *dev) +{ + struct simtec_i2c_data *pd; + struct resource *res; + int size; + int ret; + + pd = kzalloc(sizeof(struct simtec_i2c_data), GFP_KERNEL); + if (pd == NULL) { + dev_err(&dev->dev, "cannot allocate private data\n"); + return -ENOMEM; + } + + platform_set_drvdata(dev, pd); + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&dev->dev, "cannot find IO resource\n"); + ret = -ENOENT; + goto err; + } + + size = resource_size(res); + + pd->ioarea = request_mem_region(res->start, size, dev->name); + if (pd->ioarea == NULL) { + dev_err(&dev->dev, "cannot request IO\n"); + ret = -ENXIO; + goto err; + } + + pd->reg = ioremap(res->start, size); + if (pd->reg == NULL) { + dev_err(&dev->dev, "cannot map IO\n"); + ret = -ENXIO; + goto err_res; + } + + /* setup the private data */ + + pd->adap.owner = THIS_MODULE; + pd->adap.algo_data = &pd->bit; + pd->adap.dev.parent = &dev->dev; + + strlcpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name)); + + pd->bit.data = pd; + pd->bit.setsda = simtec_i2c_setsda; + pd->bit.setscl = simtec_i2c_setscl; + pd->bit.getsda = simtec_i2c_getsda; + pd->bit.getscl = simtec_i2c_getscl; + pd->bit.timeout = HZ; + pd->bit.udelay = 20; + + ret = i2c_bit_add_bus(&pd->adap); + if (ret) + goto err_all; + + return 0; + + err_all: + iounmap(pd->reg); + + err_res: + release_resource(pd->ioarea); + kfree(pd->ioarea); + + err: + kfree(pd); + return ret; +} + +static int simtec_i2c_remove(struct platform_device *dev) +{ + struct simtec_i2c_data *pd = platform_get_drvdata(dev); + + i2c_del_adapter(&pd->adap); + + iounmap(pd->reg); + release_resource(pd->ioarea); + kfree(pd->ioarea); + kfree(pd); + + return 0; +} + + +/* device driver */ + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:simtec-i2c"); + +static struct platform_driver simtec_i2c_driver = { + .driver = { + .name = "simtec-i2c", + .owner = THIS_MODULE, + }, + .probe = simtec_i2c_probe, + .remove = simtec_i2c_remove, +}; + +static int __init i2c_adap_simtec_init(void) +{ + return platform_driver_register(&simtec_i2c_driver); +} + +static void __exit i2c_adap_simtec_exit(void) +{ + platform_driver_unregister(&simtec_i2c_driver); +} + +module_init(i2c_adap_simtec_init); +module_exit(i2c_adap_simtec_exit); + +MODULE_DESCRIPTION("Simtec Generic I2C Bus driver"); +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c new file mode 100644 index 00000000..6d60284c --- /dev/null +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -0,0 +1,434 @@ +/* + Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and + Philip Edelbrock <phil@netroedge.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. + + 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. +*/ + +/* Note: we assume there can only be one SIS5595 with one SMBus interface */ + +/* + Note: all have mfr. ID 0x1039. + SUPPORTED PCI ID + 5595 0008 + + Note: these chips contain a 0008 device which is incompatible with the + 5595. We recognize these by the presence of the listed + "blacklist" PCI ID and refuse to load. + + NOT SUPPORTED PCI ID BLACKLIST PCI ID + 540 0008 0540 + 550 0008 0550 + 5513 0008 5511 + 5581 0008 5597 + 5582 0008 5597 + 5597 0008 5597 + 5598 0008 5597/5598 + 630 0008 0630 + 645 0008 0645 + 646 0008 0646 + 648 0008 0648 + 650 0008 0650 + 651 0008 0651 + 730 0008 0730 + 735 0008 0735 + 745 0008 0745 + 746 0008 0746 +*/ + +/* TO DO: + * Add Block Transfers (ugly, but supported by the adapter) + * Add adapter resets + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/acpi.h> +#include <linux/io.h> + +static int blacklist[] = { + PCI_DEVICE_ID_SI_540, + PCI_DEVICE_ID_SI_550, + PCI_DEVICE_ID_SI_630, + PCI_DEVICE_ID_SI_645, + PCI_DEVICE_ID_SI_646, + PCI_DEVICE_ID_SI_648, + PCI_DEVICE_ID_SI_650, + PCI_DEVICE_ID_SI_651, + PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_735, + PCI_DEVICE_ID_SI_745, + PCI_DEVICE_ID_SI_746, + PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but that ID + shows up in other chips so we use the 5511 + ID for recognition */ + PCI_DEVICE_ID_SI_5597, + PCI_DEVICE_ID_SI_5598, + 0, /* terminates the list */ +}; + +/* Length of ISA address segment */ +#define SIS5595_EXTENT 8 +/* SIS5595 SMBus registers */ +#define SMB_STS_LO 0x00 +#define SMB_STS_HI 0x01 +#define SMB_CTL_LO 0x02 +#define SMB_CTL_HI 0x03 +#define SMB_ADDR 0x04 +#define SMB_CMD 0x05 +#define SMB_PCNT 0x06 +#define SMB_CNT 0x07 +#define SMB_BYTE 0x08 +#define SMB_DEV 0x10 +#define SMB_DB0 0x11 +#define SMB_DB1 0x12 +#define SMB_HAA 0x13 + +/* PCI Address Constants */ +#define SMB_INDEX 0x38 +#define SMB_DAT 0x39 +#define SIS5595_ENABLE_REG 0x40 +#define ACPI_BASE 0x90 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SIS5595 constants */ +#define SIS5595_QUICK 0x00 +#define SIS5595_BYTE 0x02 +#define SIS5595_BYTE_DATA 0x04 +#define SIS5595_WORD_DATA 0x06 +#define SIS5595_PROC_CALL 0x08 +#define SIS5595_BLOCK_DATA 0x0A + +/* insmod parameters */ + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static u16 force_addr; +module_param(force_addr, ushort, 0); +MODULE_PARM_DESC(force_addr, "Initialize the base address of the i2c controller"); + +static struct pci_driver sis5595_driver; +static unsigned short sis5595_base; +static struct pci_dev *sis5595_pdev; + +static u8 sis5595_read(u8 reg) +{ + outb(reg, sis5595_base + SMB_INDEX); + return inb(sis5595_base + SMB_DAT); +} + +static void sis5595_write(u8 reg, u8 data) +{ + outb(reg, sis5595_base + SMB_INDEX); + outb(data, sis5595_base + SMB_DAT); +} + +static int __devinit sis5595_setup(struct pci_dev *SIS5595_dev) +{ + u16 a; + u8 val; + int *i; + int retval; + + /* Look for imposters */ + for (i = blacklist; *i != 0; i++) { + struct pci_dev *dev; + dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); + if (dev) { + dev_err(&SIS5595_dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i); + pci_dev_put(dev); + return -ENODEV; + } + } + + /* Determine the address of the SMBus areas */ + pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base); + if (sis5595_base == 0 && force_addr == 0) { + dev_err(&SIS5595_dev->dev, "ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if (force_addr) + sis5595_base = force_addr & ~(SIS5595_EXTENT - 1); + dev_dbg(&SIS5595_dev->dev, "ACPI Base address: %04x\n", sis5595_base); + + /* NB: We grab just the two SMBus registers here, but this may still + * interfere with ACPI :-( */ + retval = acpi_check_region(sis5595_base + SMB_INDEX, 2, + sis5595_driver.name); + if (retval) + return retval; + + if (!request_region(sis5595_base + SMB_INDEX, 2, + sis5595_driver.name)) { + dev_err(&SIS5595_dev->dev, "SMBus registers 0x%04x-0x%04x already in use!\n", + sis5595_base + SMB_INDEX, sis5595_base + SMB_INDEX + 1); + return -ENODEV; + } + + if (force_addr) { + dev_info(&SIS5595_dev->dev, "forcing ISA address 0x%04X\n", sis5595_base); + if (pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base) + != PCIBIOS_SUCCESSFUL) + goto error; + if (pci_read_config_word(SIS5595_dev, ACPI_BASE, &a) + != PCIBIOS_SUCCESSFUL) + goto error; + if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) { + /* doesn't work for some chips! */ + dev_err(&SIS5595_dev->dev, "force address failed - not supported?\n"); + goto error; + } + } + + if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val) + != PCIBIOS_SUCCESSFUL) + goto error; + if ((val & 0x80) == 0) { + dev_info(&SIS5595_dev->dev, "enabling ACPI\n"); + if (pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80) + != PCIBIOS_SUCCESSFUL) + goto error; + if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val) + != PCIBIOS_SUCCESSFUL) + goto error; + if ((val & 0x80) == 0) { + /* doesn't work for some chips? */ + dev_err(&SIS5595_dev->dev, "ACPI enable failed - not supported?\n"); + goto error; + } + } + + /* Everything is happy */ + return 0; + +error: + release_region(sis5595_base + SMB_INDEX, 2); + return -ENODEV; +} + +static int sis5595_transaction(struct i2c_adapter *adap) +{ + int temp; + int result = 0; + int timeout = 0; + + /* Make sure the SMBus host is ready to start transmitting */ + temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); + if (temp != 0x00) { + dev_dbg(&adap->dev, "SMBus busy (%04x). Resetting...\n", temp); + sis5595_write(SMB_STS_LO, temp & 0xff); + sis5595_write(SMB_STS_HI, temp >> 8); + if ((temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) { + dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); + return -EBUSY; + } else { + dev_dbg(&adap->dev, "Successful!\n"); + } + } + + /* start the transaction by setting bit 4 */ + sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10); + + /* We will always wait for a fraction of a second! */ + do { + msleep(1); + temp = sis5595_read(SMB_STS_LO); + } while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "SMBus Timeout!\n"); + result = -ETIMEDOUT; + } + + if (temp & 0x10) { + dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); + result = -ENXIO; + } + + if (temp & 0x20) { + dev_err(&adap->dev, "Bus collision! SMBus may be locked until " + "next hard reset (or not...)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + result = -EIO; + } + + temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); + if (temp != 0x00) { + sis5595_write(SMB_STS_LO, temp & 0xff); + sis5595_write(SMB_STS_HI, temp >> 8); + } + + temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); + if (temp != 0x00) + dev_dbg(&adap->dev, "Failed reset at end of transaction (%02x)\n", temp); + + return result; +} + +/* Return negative errno on error. */ +static s32 sis5595_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS5595_QUICK; + break; + case I2C_SMBUS_BYTE: + sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis5595_write(SMB_CMD, command); + size = SIS5595_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis5595_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis5595_write(SMB_BYTE, data->byte); + size = SIS5595_BYTE_DATA; + break; + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis5595_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis5595_write(SMB_BYTE, data->word & 0xff); + sis5595_write(SMB_BYTE + 1, + (data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : SIS5595_WORD_DATA; + break; + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + sis5595_write(SMB_CTL_LO, ((size & 0x0E))); + + status = sis5595_transaction(adap); + if (status) + return status; + + if ((size != SIS5595_PROC_CALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK))) + return 0; + + + switch (size) { + case SIS5595_BYTE: + case SIS5595_BYTE_DATA: + data->byte = sis5595_read(SMB_BYTE); + break; + case SIS5595_WORD_DATA: + case SIS5595_PROC_CALL: + data->word = sis5595_read(SMB_BYTE) + (sis5595_read(SMB_BYTE + 1) << 8); + break; + } + return 0; +} + +static u32 sis5595_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = sis5595_access, + .functionality = sis5595_func, +}; + +static struct i2c_adapter sis5595_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, +}; + +static const struct pci_device_id sis5595_ids[] __devinitconst = { + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, sis5595_ids); + +static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int err; + + if (sis5595_setup(dev)) { + dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n"); + return -ENODEV; + } + + /* set up the sysfs linkage to our parent device */ + sis5595_adapter.dev.parent = &dev->dev; + + snprintf(sis5595_adapter.name, sizeof(sis5595_adapter.name), + "SMBus SIS5595 adapter at %04x", sis5595_base + SMB_INDEX); + err = i2c_add_adapter(&sis5595_adapter); + if (err) { + release_region(sis5595_base + SMB_INDEX, 2); + return err; + } + + /* Always return failure here. This is to allow other drivers to bind + * to this pci device. We don't really want to have control over the + * pci device, we only wanted to read as few register values from it. + */ + sis5595_pdev = pci_dev_get(dev); + return -ENODEV; +} + +static struct pci_driver sis5595_driver = { + .name = "sis5595_smbus", + .id_table = sis5595_ids, + .probe = sis5595_probe, +}; + +static int __init i2c_sis5595_init(void) +{ + return pci_register_driver(&sis5595_driver); +} + +static void __exit i2c_sis5595_exit(void) +{ + pci_unregister_driver(&sis5595_driver); + if (sis5595_pdev) { + i2c_del_adapter(&sis5595_adapter); + release_region(sis5595_base + SMB_INDEX, 2); + pci_dev_put(sis5595_pdev); + sis5595_pdev = NULL; + } +} + +MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); +MODULE_DESCRIPTION("SIS5595 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_sis5595_init); +module_exit(i2c_sis5595_exit); diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c new file mode 100644 index 00000000..b617fd06 --- /dev/null +++ b/drivers/i2c/busses/i2c-sis630.c @@ -0,0 +1,533 @@ +/* + Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de> + + 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. +*/ + +/* + Changes: + 24.08.2002 + Fixed the typo in sis630_access (Thanks to Mark M. Hoffman) + Changed sis630_transaction.(Thanks to Mark M. Hoffman) + 18.09.2002 + Added SIS730 as supported. + 21.09.2002 + Added high_clock module option.If this option is set + used Host Master Clock 56KHz (default 14KHz).For now we save old Host + Master Clock and after transaction completed restore (otherwise + it's confuse BIOS and hung Machine). + 24.09.2002 + Fixed typo in sis630_access + Fixed logical error by restoring of Host Master Clock + 31.07.2003 + Added block data read/write support. +*/ + +/* + Status: beta + + Supports: + SIS 630 + SIS 730 + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/acpi.h> +#include <linux/io.h> + +/* SIS630 SMBus registers */ +#define SMB_STS 0x80 /* status */ +#define SMB_EN 0x81 /* status enable */ +#define SMB_CNT 0x82 +#define SMBHOST_CNT 0x83 +#define SMB_ADDR 0x84 +#define SMB_CMD 0x85 +#define SMB_PCOUNT 0x86 /* processed count */ +#define SMB_COUNT 0x87 +#define SMB_BYTE 0x88 /* ~0x8F data byte field */ +#define SMBDEV_ADDR 0x90 +#define SMB_DB0 0x91 +#define SMB_DB1 0x92 +#define SMB_SAA 0x93 + +/* register count for request_region */ +#define SIS630_SMB_IOREGION 20 + +/* PCI address constants */ +/* acpi base address register */ +#define SIS630_ACPI_BASE_REG 0x74 +/* bios control register */ +#define SIS630_BIOS_CTL_REG 0x40 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SIS630 constants */ +#define SIS630_QUICK 0x00 +#define SIS630_BYTE 0x01 +#define SIS630_BYTE_DATA 0x02 +#define SIS630_WORD_DATA 0x03 +#define SIS630_PCALL 0x04 +#define SIS630_BLOCK_DATA 0x05 + +static struct pci_driver sis630_driver; + +/* insmod parameters */ +static int high_clock; +static int force; +module_param(high_clock, bool, 0); +MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz)."); +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!"); + +/* acpi base address */ +static unsigned short acpi_base; + +/* supported chips */ +static int supported[] = { + PCI_DEVICE_ID_SI_630, + PCI_DEVICE_ID_SI_730, + 0 /* terminates the list */ +}; + +static inline u8 sis630_read(u8 reg) +{ + return inb(acpi_base + reg); +} + +static inline void sis630_write(u8 reg, u8 data) +{ + outb(data, acpi_base + reg); +} + +static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock) +{ + int temp; + + /* Make sure the SMBus host is ready to start transmitting. */ + if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { + dev_dbg(&adap->dev, "SMBus busy (%02x).Resetting...\n",temp); + /* kill smbus transaction */ + sis630_write(SMBHOST_CNT, 0x20); + + if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { + dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); + return -EBUSY; + } else { + dev_dbg(&adap->dev, "Successful!\n"); + } + } + + /* save old clock, so we can prevent machine for hung */ + *oldclock = sis630_read(SMB_CNT); + + dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock); + + /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */ + if (high_clock) + sis630_write(SMB_CNT, 0x20); + else + sis630_write(SMB_CNT, (*oldclock & ~0x40)); + + /* clear all sticky bits */ + temp = sis630_read(SMB_STS); + sis630_write(SMB_STS, temp & 0x1e); + + /* start the transaction by setting bit 4 and size */ + sis630_write(SMBHOST_CNT,0x10 | (size & 0x07)); + + return 0; +} + +static int sis630_transaction_wait(struct i2c_adapter *adap, int size) +{ + int temp, result = 0, timeout = 0; + + /* We will always wait for a fraction of a second! */ + do { + msleep(1); + temp = sis630_read(SMB_STS); + /* check if block transmitted */ + if (size == SIS630_BLOCK_DATA && (temp & 0x10)) + break; + } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&adap->dev, "SMBus Timeout!\n"); + result = -ETIMEDOUT; + } + + if (temp & 0x02) { + dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); + result = -ENXIO; + } + + if (temp & 0x04) { + dev_err(&adap->dev, "Bus collision!\n"); + result = -EIO; + /* + TBD: Datasheet say: + the software should clear this bit and restart SMBUS operation. + Should we do it or user start request again? + */ + } + + return result; +} + +static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) +{ + int temp = 0; + + /* clear all status "sticky" bits */ + sis630_write(SMB_STS, temp); + + dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); + + /* + * restore old Host Master Clock if high_clock is set + * and oldclock was not 56KHz + */ + if (high_clock && !(oldclock & 0x20)) + sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20)); + + dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); +} + +static int sis630_transaction(struct i2c_adapter *adap, int size) +{ + int result = 0; + u8 oldclock = 0; + + result = sis630_transaction_start(adap, size, &oldclock); + if (!result) { + result = sis630_transaction_wait(adap, size); + sis630_transaction_end(adap, oldclock); + } + + return result; +} + +static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write) +{ + int i, len = 0, rc = 0; + u8 oldclock = 0; + + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + else if (len > 32) + len = 32; + sis630_write(SMB_COUNT, len); + for (i=1; i <= len; i++) { + dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]); + /* set data */ + sis630_write(SMB_BYTE+(i-1)%8, data->block[i]); + if (i==8 || (len<8 && i==len)) { + dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i); + /* first transaction */ + rc = sis630_transaction_start(adap, + SIS630_BLOCK_DATA, &oldclock); + if (rc) + return rc; + } + else if ((i-1)%8 == 7 || i==len) { + dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i); + if (i>8) { + dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); + /* + If this is not first transaction, + we must clear sticky bit. + clear SMBARY_STS + */ + sis630_write(SMB_STS,0x10); + } + rc = sis630_transaction_wait(adap, + SIS630_BLOCK_DATA); + if (rc) { + dev_dbg(&adap->dev, "trans_wait failed\n"); + break; + } + } + } + } + else { + /* read request */ + data->block[0] = len = 0; + rc = sis630_transaction_start(adap, + SIS630_BLOCK_DATA, &oldclock); + if (rc) + return rc; + do { + rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA); + if (rc) { + dev_dbg(&adap->dev, "trans_wait failed\n"); + break; + } + /* if this first transaction then read byte count */ + if (len == 0) + data->block[0] = sis630_read(SMB_COUNT); + + /* just to be sure */ + if (data->block[0] > 32) + data->block[0] = 32; + + dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]); + + for (i=0; i < 8 && len < data->block[0]; i++,len++) { + dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len); + data->block[len+1] = sis630_read(SMB_BYTE+i); + } + + dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); + + /* clear SMBARY_STS */ + sis630_write(SMB_STS,0x10); + } while(len < data->block[0]); + } + + sis630_transaction_end(adap, oldclock); + + return rc; +} + +/* Return negative errno on error. */ +static s32 sis630_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS630_QUICK; + break; + case I2C_SMBUS_BYTE: + sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis630_write(SMB_CMD, command); + size = SIS630_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis630_write(SMB_BYTE, data->byte); + size = SIS630_BYTE_DATA; + break; + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis630_write(SMB_BYTE, data->word & 0xff); + sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA); + break; + case I2C_SMBUS_BLOCK_DATA: + sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + size = SIS630_BLOCK_DATA; + return sis630_block_data(adap, data, read_write); + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", + size); + return -EOPNOTSUPP; + } + + status = sis630_transaction(adap, size); + if (status) + return status; + + if ((size != SIS630_PCALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) { + return 0; + } + + switch(size) { + case SIS630_BYTE: + case SIS630_BYTE_DATA: + data->byte = sis630_read(SMB_BYTE); + break; + case SIS630_PCALL: + case SIS630_WORD_DATA: + data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8); + break; + } + + return 0; +} + +static u32 sis630_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static int __devinit sis630_setup(struct pci_dev *sis630_dev) +{ + unsigned char b; + struct pci_dev *dummy = NULL; + int retval, i; + + /* check for supported SiS devices */ + for (i=0; supported[i] > 0 ; i++) { + if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy))) + break; /* found */ + } + + if (dummy) { + pci_dev_put(dummy); + } + else if (force) { + dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but " + "loading because of force option enabled\n"); + } + else { + return -ENODEV; + } + + /* + Enable ACPI first , so we can accsess reg 74-75 + in acpi io space and read acpi base addr + */ + if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) { + dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n"); + retval = -ENODEV; + goto exit; + } + /* if ACPI already enabled , do nothing */ + if (!(b & 0x80) && + pci_write_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, b | 0x80)) { + dev_err(&sis630_dev->dev, "Error: Can't enable ACPI\n"); + retval = -ENODEV; + goto exit; + } + + /* Determine the ACPI base address */ + if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) { + dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n"); + retval = -ENODEV; + goto exit; + } + + dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base); + + retval = acpi_check_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, + sis630_driver.name); + if (retval) + goto exit; + + /* Everything is happy, let's grab the memory and set things up. */ + if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, + sis630_driver.name)) { + dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already " + "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA); + retval = -EBUSY; + goto exit; + } + + retval = 0; + +exit: + if (retval) + acpi_base = 0; + return retval; +} + + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = sis630_access, + .functionality = sis630_func, +}; + +static struct i2c_adapter sis630_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, +}; + +static const struct pci_device_id sis630_ids[] __devinitconst = { + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, sis630_ids); + +static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + if (sis630_setup(dev)) { + dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n"); + return -ENODEV; + } + + /* set up the sysfs linkage to our parent device */ + sis630_adapter.dev.parent = &dev->dev; + + snprintf(sis630_adapter.name, sizeof(sis630_adapter.name), + "SMBus SIS630 adapter at %04x", acpi_base + SMB_STS); + + return i2c_add_adapter(&sis630_adapter); +} + +static void __devexit sis630_remove(struct pci_dev *dev) +{ + if (acpi_base) { + i2c_del_adapter(&sis630_adapter); + release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION); + acpi_base = 0; + } +} + + +static struct pci_driver sis630_driver = { + .name = "sis630_smbus", + .id_table = sis630_ids, + .probe = sis630_probe, + .remove = __devexit_p(sis630_remove), +}; + +static int __init i2c_sis630_init(void) +{ + return pci_register_driver(&sis630_driver); +} + + +static void __exit i2c_sis630_exit(void) +{ + pci_unregister_driver(&sis630_driver); +} + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>"); +MODULE_DESCRIPTION("SIS630 SMBus driver"); + +module_init(i2c_sis630_init); +module_exit(i2c_sis630_exit); diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c new file mode 100644 index 00000000..86837f0c --- /dev/null +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -0,0 +1,344 @@ +/* + Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.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. + + 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. +*/ + +/* + This module must be considered BETA unless and until + the chipset manufacturer releases a datasheet. + The register definitions are based on the SiS630. + + This module relies on quirk_sis_96x_smbus (drivers/pci/quirks.c) + for just about every machine for which users have reported. + If this module isn't detecting your 96x south bridge, have a + look there. + + We assume there can only be one SiS96x with one SMBus interface. +*/ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/io.h> + +/* base address register in PCI config space */ +#define SIS96x_BAR 0x04 + +/* SiS96x SMBus registers */ +#define SMB_STS 0x00 +#define SMB_EN 0x01 +#define SMB_CNT 0x02 +#define SMB_HOST_CNT 0x03 +#define SMB_ADDR 0x04 +#define SMB_CMD 0x05 +#define SMB_PCOUNT 0x06 +#define SMB_COUNT 0x07 +#define SMB_BYTE 0x08 +#define SMB_DEV_ADDR 0x10 +#define SMB_DB0 0x11 +#define SMB_DB1 0x12 +#define SMB_SAA 0x13 + +/* register count for request_region */ +#define SMB_IOSIZE 0x20 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SiS96x SMBus constants */ +#define SIS96x_QUICK 0x00 +#define SIS96x_BYTE 0x01 +#define SIS96x_BYTE_DATA 0x02 +#define SIS96x_WORD_DATA 0x03 +#define SIS96x_PROC_CALL 0x04 +#define SIS96x_BLOCK_DATA 0x05 + +static struct pci_driver sis96x_driver; +static struct i2c_adapter sis96x_adapter; +static u16 sis96x_smbus_base; + +static inline u8 sis96x_read(u8 reg) +{ + return inb(sis96x_smbus_base + reg) ; +} + +static inline void sis96x_write(u8 reg, u8 data) +{ + outb(data, sis96x_smbus_base + reg) ; +} + +/* Execute a SMBus transaction. + int size is from SIS96x_QUICK to SIS96x_BLOCK_DATA + */ +static int sis96x_transaction(int size) +{ + int temp; + int result = 0; + int timeout = 0; + + dev_dbg(&sis96x_adapter.dev, "SMBus transaction %d\n", size); + + /* Make sure the SMBus host is ready to start transmitting */ + if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) { + + dev_dbg(&sis96x_adapter.dev, "SMBus busy (0x%02x). " + "Resetting...\n", temp); + + /* kill the transaction */ + sis96x_write(SMB_HOST_CNT, 0x20); + + /* check it again */ + if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) { + dev_dbg(&sis96x_adapter.dev, "Failed (0x%02x)\n", temp); + return -EBUSY; + } else { + dev_dbg(&sis96x_adapter.dev, "Successful\n"); + } + } + + /* Turn off timeout interrupts, set fast host clock */ + sis96x_write(SMB_CNT, 0x20); + + /* clear all (sticky) status flags */ + temp = sis96x_read(SMB_STS); + sis96x_write(SMB_STS, temp & 0x1e); + + /* start the transaction by setting bit 4 and size bits */ + sis96x_write(SMB_HOST_CNT, 0x10 | (size & 0x07)); + + /* We will always wait for a fraction of a second! */ + do { + msleep(1); + temp = sis96x_read(SMB_STS); + } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout > MAX_TIMEOUT) { + dev_dbg(&sis96x_adapter.dev, "SMBus Timeout! (0x%02x)\n", temp); + result = -ETIMEDOUT; + } + + /* device error - probably missing ACK */ + if (temp & 0x02) { + dev_dbg(&sis96x_adapter.dev, "Failed bus transaction!\n"); + result = -ENXIO; + } + + /* bus collision */ + if (temp & 0x04) { + dev_dbg(&sis96x_adapter.dev, "Bus collision!\n"); + result = -EIO; + } + + /* Finish up by resetting the bus */ + sis96x_write(SMB_STS, temp); + if ((temp = sis96x_read(SMB_STS))) { + dev_dbg(&sis96x_adapter.dev, "Failed reset at " + "end of transaction! (0x%02x)\n", temp); + } + + return result; +} + +/* Return negative errno on error. */ +static s32 sis96x_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS96x_QUICK; + break; + + case I2C_SMBUS_BYTE: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis96x_write(SMB_CMD, command); + size = SIS96x_BYTE; + break; + + case I2C_SMBUS_BYTE_DATA: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis96x_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis96x_write(SMB_BYTE, data->byte); + size = SIS96x_BYTE_DATA; + break; + + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis96x_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis96x_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis96x_write(SMB_BYTE, data->word & 0xff); + sis96x_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL ? + SIS96x_PROC_CALL : SIS96x_WORD_DATA); + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + status = sis96x_transaction(size); + if (status) + return status; + + if ((size != SIS96x_PROC_CALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS96x_QUICK))) + return 0; + + switch (size) { + case SIS96x_BYTE: + case SIS96x_BYTE_DATA: + data->byte = sis96x_read(SMB_BYTE); + break; + + case SIS96x_WORD_DATA: + case SIS96x_PROC_CALL: + data->word = sis96x_read(SMB_BYTE) + + (sis96x_read(SMB_BYTE + 1) << 8); + break; + } + return 0; +} + +static u32 sis96x_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = sis96x_access, + .functionality = sis96x_func, +}; + +static struct i2c_adapter sis96x_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, +}; + +static const struct pci_device_id sis96x_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, sis96x_ids); + +static int __devinit sis96x_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u16 ww = 0; + int retval; + + if (sis96x_smbus_base) { + dev_err(&dev->dev, "Only one device supported.\n"); + return -EBUSY; + } + + pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww); + if (PCI_CLASS_SERIAL_SMBUS != ww) { + dev_err(&dev->dev, "Unsupported device class 0x%04x!\n", ww); + return -ENODEV; + } + + sis96x_smbus_base = pci_resource_start(dev, SIS96x_BAR); + if (!sis96x_smbus_base) { + dev_err(&dev->dev, "SiS96x SMBus base address " + "not initialized!\n"); + return -EINVAL; + } + dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n", + sis96x_smbus_base); + + retval = acpi_check_resource_conflict(&dev->resource[SIS96x_BAR]); + if (retval) + return -ENODEV; + + /* Everything is happy, let's grab the memory and set things up. */ + if (!request_region(sis96x_smbus_base, SMB_IOSIZE, + sis96x_driver.name)) { + dev_err(&dev->dev, "SMBus registers 0x%04x-0x%04x " + "already in use!\n", sis96x_smbus_base, + sis96x_smbus_base + SMB_IOSIZE - 1); + + sis96x_smbus_base = 0; + return -EINVAL; + } + + /* set up the sysfs linkage to our parent device */ + sis96x_adapter.dev.parent = &dev->dev; + + snprintf(sis96x_adapter.name, sizeof(sis96x_adapter.name), + "SiS96x SMBus adapter at 0x%04x", sis96x_smbus_base); + + if ((retval = i2c_add_adapter(&sis96x_adapter))) { + dev_err(&dev->dev, "Couldn't register adapter!\n"); + release_region(sis96x_smbus_base, SMB_IOSIZE); + sis96x_smbus_base = 0; + } + + return retval; +} + +static void __devexit sis96x_remove(struct pci_dev *dev) +{ + if (sis96x_smbus_base) { + i2c_del_adapter(&sis96x_adapter); + release_region(sis96x_smbus_base, SMB_IOSIZE); + sis96x_smbus_base = 0; + } +} + +static struct pci_driver sis96x_driver = { + .name = "sis96x_smbus", + .id_table = sis96x_ids, + .probe = sis96x_probe, + .remove = __devexit_p(sis96x_remove), +}; + +static int __init i2c_sis96x_init(void) +{ + return pci_register_driver(&sis96x_driver); +} + +static void __exit i2c_sis96x_exit(void) +{ + pci_unregister_driver(&sis96x_driver); +} + +MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); +MODULE_DESCRIPTION("SiS96x SMBus driver"); +MODULE_LICENSE("GPL"); + +/* Register initialization functions using helper macros */ +module_init(i2c_sis96x_init); +module_exit(i2c_sis96x_exit); + diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c new file mode 100644 index 00000000..99879617 --- /dev/null +++ b/drivers/i2c/busses/i2c-stu300.c @@ -0,0 +1,1057 @@ +/* + * Copyright (C) 2007-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * ST DDC I2C master mode driver, used in e.g. U300 series platforms. + * Author: Linus Walleij <linus.walleij@stericsson.com> + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/spinlock.h> +#include <linux/completion.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> + +/* the name of this kernel module */ +#define NAME "stu300" + +/* CR (Control Register) 8bit (R/W) */ +#define I2C_CR (0x00000000) +#define I2C_CR_RESET_VALUE (0x00) +#define I2C_CR_RESET_UMASK (0x00) +#define I2C_CR_DDC1_ENABLE (0x80) +#define I2C_CR_TRANS_ENABLE (0x40) +#define I2C_CR_PERIPHERAL_ENABLE (0x20) +#define I2C_CR_DDC2B_ENABLE (0x10) +#define I2C_CR_START_ENABLE (0x08) +#define I2C_CR_ACK_ENABLE (0x04) +#define I2C_CR_STOP_ENABLE (0x02) +#define I2C_CR_INTERRUPT_ENABLE (0x01) +/* SR1 (Status Register 1) 8bit (R/-) */ +#define I2C_SR1 (0x00000004) +#define I2C_SR1_RESET_VALUE (0x00) +#define I2C_SR1_RESET_UMASK (0x00) +#define I2C_SR1_EVF_IND (0x80) +#define I2C_SR1_ADD10_IND (0x40) +#define I2C_SR1_TRA_IND (0x20) +#define I2C_SR1_BUSY_IND (0x10) +#define I2C_SR1_BTF_IND (0x08) +#define I2C_SR1_ADSL_IND (0x04) +#define I2C_SR1_MSL_IND (0x02) +#define I2C_SR1_SB_IND (0x01) +/* SR2 (Status Register 2) 8bit (R/-) */ +#define I2C_SR2 (0x00000008) +#define I2C_SR2_RESET_VALUE (0x00) +#define I2C_SR2_RESET_UMASK (0x40) +#define I2C_SR2_MASK (0xBF) +#define I2C_SR2_SCLFAL_IND (0x80) +#define I2C_SR2_ENDAD_IND (0x20) +#define I2C_SR2_AF_IND (0x10) +#define I2C_SR2_STOPF_IND (0x08) +#define I2C_SR2_ARLO_IND (0x04) +#define I2C_SR2_BERR_IND (0x02) +#define I2C_SR2_DDC2BF_IND (0x01) +/* CCR (Clock Control Register) 8bit (R/W) */ +#define I2C_CCR (0x0000000C) +#define I2C_CCR_RESET_VALUE (0x00) +#define I2C_CCR_RESET_UMASK (0x00) +#define I2C_CCR_MASK (0xFF) +#define I2C_CCR_FMSM (0x80) +#define I2C_CCR_CC_MASK (0x7F) +/* OAR1 (Own Address Register 1) 8bit (R/W) */ +#define I2C_OAR1 (0x00000010) +#define I2C_OAR1_RESET_VALUE (0x00) +#define I2C_OAR1_RESET_UMASK (0x00) +#define I2C_OAR1_ADD_MASK (0xFF) +/* OAR2 (Own Address Register 2) 8bit (R/W) */ +#define I2C_OAR2 (0x00000014) +#define I2C_OAR2_RESET_VALUE (0x40) +#define I2C_OAR2_RESET_UMASK (0x19) +#define I2C_OAR2_MASK (0xE6) +#define I2C_OAR2_FR_25_10MHZ (0x00) +#define I2C_OAR2_FR_10_1667MHZ (0x20) +#define I2C_OAR2_FR_1667_2667MHZ (0x40) +#define I2C_OAR2_FR_2667_40MHZ (0x60) +#define I2C_OAR2_FR_40_5333MHZ (0x80) +#define I2C_OAR2_FR_5333_66MHZ (0xA0) +#define I2C_OAR2_FR_66_80MHZ (0xC0) +#define I2C_OAR2_FR_80_100MHZ (0xE0) +#define I2C_OAR2_FR_MASK (0xE0) +#define I2C_OAR2_ADD_MASK (0x06) +/* DR (Data Register) 8bit (R/W) */ +#define I2C_DR (0x00000018) +#define I2C_DR_RESET_VALUE (0x00) +#define I2C_DR_RESET_UMASK (0xFF) +#define I2C_DR_D_MASK (0xFF) +/* ECCR (Extended Clock Control Register) 8bit (R/W) */ +#define I2C_ECCR (0x0000001C) +#define I2C_ECCR_RESET_VALUE (0x00) +#define I2C_ECCR_RESET_UMASK (0xE0) +#define I2C_ECCR_MASK (0x1F) +#define I2C_ECCR_CC_MASK (0x1F) + +/* + * These events are more or less responses to commands + * sent into the hardware, presumably reflecting the state + * of an internal state machine. + */ +enum stu300_event { + STU300_EVENT_NONE = 0, + STU300_EVENT_1, + STU300_EVENT_2, + STU300_EVENT_3, + STU300_EVENT_4, + STU300_EVENT_5, + STU300_EVENT_6, + STU300_EVENT_7, + STU300_EVENT_8, + STU300_EVENT_9 +}; + +enum stu300_error { + STU300_ERROR_NONE = 0, + STU300_ERROR_ACKNOWLEDGE_FAILURE, + STU300_ERROR_BUS_ERROR, + STU300_ERROR_ARBITRATION_LOST, + STU300_ERROR_UNKNOWN +}; + +/* timeout waiting for the controller to respond */ +#define STU300_TIMEOUT (msecs_to_jiffies(1000)) + +/* + * The number of address send athemps tried before giving up. + * If the first one failes it seems like 5 to 8 attempts are required. + */ +#define NUM_ADDR_RESEND_ATTEMPTS 12 + +/* I2C clock speed, in Hz 0-400kHz*/ +static unsigned int scl_frequency = 100000; +module_param(scl_frequency, uint, 0644); + +/** + * struct stu300_dev - the stu300 driver state holder + * @pdev: parent platform device + * @adapter: corresponding I2C adapter + * @phybase: location of I/O area in memory + * @physize: size of I/O area in memory + * @clk: hardware block clock + * @irq: assigned interrupt line + * @cmd_issue_lock: this locks the following cmd_ variables + * @cmd_complete: acknowledge completion for an I2C command + * @cmd_event: expected event coming in as a response to a command + * @cmd_err: error code as response to a command + * @speed: current bus speed in Hz + * @msg_index: index of current message + * @msg_len: length of current message + */ + +struct stu300_dev { + struct platform_device *pdev; + struct i2c_adapter adapter; + resource_size_t phybase; + resource_size_t physize; + void __iomem *virtbase; + struct clk *clk; + int irq; + spinlock_t cmd_issue_lock; + struct completion cmd_complete; + enum stu300_event cmd_event; + enum stu300_error cmd_err; + unsigned int speed; + int msg_index; + int msg_len; +}; + +/* Local forward function declarations */ +static int stu300_init_hw(struct stu300_dev *dev); + +/* + * The block needs writes in both MSW and LSW in order + * for all data lines to reach their destination. + */ +static inline void stu300_wr8(u32 value, void __iomem *address) +{ + writel((value << 16) | value, address); +} + +/* + * This merely masks off the duplicates which appear + * in bytes 1-3. You _MUST_ use 32-bit bus access on this + * device, else it will not work. + */ +static inline u32 stu300_r8(void __iomem *address) +{ + return readl(address) & 0x000000FFU; +} + +static void stu300_irq_enable(struct stu300_dev *dev) +{ + u32 val; + val = stu300_r8(dev->virtbase + I2C_CR); + val |= I2C_CR_INTERRUPT_ENABLE; + /* Twice paranoia (possible HW glitch) */ + stu300_wr8(val, dev->virtbase + I2C_CR); + stu300_wr8(val, dev->virtbase + I2C_CR); +} + +static void stu300_irq_disable(struct stu300_dev *dev) +{ + u32 val; + val = stu300_r8(dev->virtbase + I2C_CR); + val &= ~I2C_CR_INTERRUPT_ENABLE; + /* Twice paranoia (possible HW glitch) */ + stu300_wr8(val, dev->virtbase + I2C_CR); + stu300_wr8(val, dev->virtbase + I2C_CR); +} + + +/* + * Tells whether a certain event or events occurred in + * response to a command. The events represent states in + * the internal state machine of the hardware. The events + * are not very well described in the hardware + * documentation and can only be treated as abstract state + * machine states. + * + * @ret 0 = event has not occurred or unknown error, any + * other value means the correct event occurred or an error. + */ + +static int stu300_event_occurred(struct stu300_dev *dev, + enum stu300_event mr_event) { + u32 status1; + u32 status2; + + /* What event happened? */ + status1 = stu300_r8(dev->virtbase + I2C_SR1); + + if (!(status1 & I2C_SR1_EVF_IND)) + /* No event at all */ + return 0; + + status2 = stu300_r8(dev->virtbase + I2C_SR2); + + /* Block any multiple interrupts */ + stu300_irq_disable(dev); + + /* Check for errors first */ + if (status2 & I2C_SR2_AF_IND) { + dev->cmd_err = STU300_ERROR_ACKNOWLEDGE_FAILURE; + return 1; + } else if (status2 & I2C_SR2_BERR_IND) { + dev->cmd_err = STU300_ERROR_BUS_ERROR; + return 1; + } else if (status2 & I2C_SR2_ARLO_IND) { + dev->cmd_err = STU300_ERROR_ARBITRATION_LOST; + return 1; + } + + switch (mr_event) { + case STU300_EVENT_1: + if (status1 & I2C_SR1_ADSL_IND) + return 1; + break; + case STU300_EVENT_2: + case STU300_EVENT_3: + case STU300_EVENT_7: + case STU300_EVENT_8: + if (status1 & I2C_SR1_BTF_IND) { + return 1; + } + break; + case STU300_EVENT_4: + if (status2 & I2C_SR2_STOPF_IND) + return 1; + break; + case STU300_EVENT_5: + if (status1 & I2C_SR1_SB_IND) + /* Clear start bit */ + return 1; + break; + case STU300_EVENT_6: + if (status2 & I2C_SR2_ENDAD_IND) { + /* First check for any errors */ + return 1; + } + break; + case STU300_EVENT_9: + if (status1 & I2C_SR1_ADD10_IND) + return 1; + break; + default: + break; + } + /* If we get here, we're on thin ice. + * Here we are in a status where we have + * gotten a response that does not match + * what we requested. + */ + dev->cmd_err = STU300_ERROR_UNKNOWN; + dev_err(&dev->pdev->dev, + "Unhandled interrupt! %d sr1: 0x%x sr2: 0x%x\n", + mr_event, status1, status2); + return 0; +} + +static irqreturn_t stu300_irh(int irq, void *data) +{ + struct stu300_dev *dev = data; + int res; + + /* Just make sure that the block is clocked */ + clk_enable(dev->clk); + + /* See if this was what we were waiting for */ + spin_lock(&dev->cmd_issue_lock); + + res = stu300_event_occurred(dev, dev->cmd_event); + if (res || dev->cmd_err != STU300_ERROR_NONE) + complete(&dev->cmd_complete); + + spin_unlock(&dev->cmd_issue_lock); + + clk_disable(dev->clk); + + return IRQ_HANDLED; +} + +/* + * Sends a command and then waits for the bits masked by *flagmask* + * to go high or low by IRQ awaiting. + */ +static int stu300_start_and_await_event(struct stu300_dev *dev, + u8 cr_value, + enum stu300_event mr_event) +{ + int ret; + + if (unlikely(irqs_disabled())) { + /* TODO: implement polling for this case if need be. */ + WARN(1, "irqs are disabled, cannot poll for event\n"); + return -EIO; + } + + /* Lock command issue, fill in an event we wait for */ + spin_lock_irq(&dev->cmd_issue_lock); + init_completion(&dev->cmd_complete); + dev->cmd_err = STU300_ERROR_NONE; + dev->cmd_event = mr_event; + spin_unlock_irq(&dev->cmd_issue_lock); + + /* Turn on interrupt, send command and wait. */ + cr_value |= I2C_CR_INTERRUPT_ENABLE; + stu300_wr8(cr_value, dev->virtbase + I2C_CR); + ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, + STU300_TIMEOUT); + if (ret < 0) { + dev_err(&dev->pdev->dev, + "wait_for_completion_interruptible_timeout() " + "returned %d waiting for event %04x\n", ret, mr_event); + return ret; + } + + if (ret == 0) { + dev_err(&dev->pdev->dev, "controller timed out " + "waiting for event %d, reinit hardware\n", mr_event); + (void) stu300_init_hw(dev); + return -ETIMEDOUT; + } + + if (dev->cmd_err != STU300_ERROR_NONE) { + dev_err(&dev->pdev->dev, "controller (start) " + "error %d waiting for event %d, reinit hardware\n", + dev->cmd_err, mr_event); + (void) stu300_init_hw(dev); + return -EIO; + } + + return 0; +} + +/* + * This waits for a flag to be set, if it is not set on entry, an interrupt is + * configured to wait for the flag using a completion. + */ +static int stu300_await_event(struct stu300_dev *dev, + enum stu300_event mr_event) +{ + int ret; + + if (unlikely(irqs_disabled())) { + /* TODO: implement polling for this case if need be. */ + dev_err(&dev->pdev->dev, "irqs are disabled on this " + "system!\n"); + return -EIO; + } + + /* Is it already here? */ + spin_lock_irq(&dev->cmd_issue_lock); + dev->cmd_err = STU300_ERROR_NONE; + dev->cmd_event = mr_event; + + init_completion(&dev->cmd_complete); + + /* Turn on the I2C interrupt for current operation */ + stu300_irq_enable(dev); + + /* Unlock the command block and wait for the event to occur */ + spin_unlock_irq(&dev->cmd_issue_lock); + + ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, + STU300_TIMEOUT); + if (ret < 0) { + dev_err(&dev->pdev->dev, + "wait_for_completion_interruptible_timeout()" + "returned %d waiting for event %04x\n", ret, mr_event); + return ret; + } + + if (ret == 0) { + if (mr_event != STU300_EVENT_6) { + dev_err(&dev->pdev->dev, "controller " + "timed out waiting for event %d, reinit " + "hardware\n", mr_event); + (void) stu300_init_hw(dev); + } + return -ETIMEDOUT; + } + + if (dev->cmd_err != STU300_ERROR_NONE) { + if (mr_event != STU300_EVENT_6) { + dev_err(&dev->pdev->dev, "controller " + "error (await_event) %d waiting for event %d, " + "reinit hardware\n", dev->cmd_err, mr_event); + (void) stu300_init_hw(dev); + } + return -EIO; + } + + return 0; +} + +/* + * Waits for the busy bit to go low by repeated polling. + */ +#define BUSY_RELEASE_ATTEMPTS 10 +static int stu300_wait_while_busy(struct stu300_dev *dev) +{ + unsigned long timeout; + int i; + + for (i = 0; i < BUSY_RELEASE_ATTEMPTS; i++) { + timeout = jiffies + STU300_TIMEOUT; + + while (!time_after(jiffies, timeout)) { + /* Is not busy? */ + if ((stu300_r8(dev->virtbase + I2C_SR1) & + I2C_SR1_BUSY_IND) == 0) + return 0; + msleep(1); + } + + dev_err(&dev->pdev->dev, "transaction timed out " + "waiting for device to be free (not busy). " + "Attempt: %d\n", i+1); + + dev_err(&dev->pdev->dev, "base address = " + "0x%08x, reinit hardware\n", (u32) dev->virtbase); + + (void) stu300_init_hw(dev); + } + + dev_err(&dev->pdev->dev, "giving up after %d attempts " + "to reset the bus.\n", BUSY_RELEASE_ATTEMPTS); + + return -ETIMEDOUT; +} + +struct stu300_clkset { + unsigned long rate; + u32 setting; +}; + +static const struct stu300_clkset stu300_clktable[] = { + { 0, 0xFFU }, + { 2500000, I2C_OAR2_FR_25_10MHZ }, + { 10000000, I2C_OAR2_FR_10_1667MHZ }, + { 16670000, I2C_OAR2_FR_1667_2667MHZ }, + { 26670000, I2C_OAR2_FR_2667_40MHZ }, + { 40000000, I2C_OAR2_FR_40_5333MHZ }, + { 53330000, I2C_OAR2_FR_5333_66MHZ }, + { 66000000, I2C_OAR2_FR_66_80MHZ }, + { 80000000, I2C_OAR2_FR_80_100MHZ }, + { 100000000, 0xFFU }, +}; + + +static int stu300_set_clk(struct stu300_dev *dev, unsigned long clkrate) +{ + + u32 val; + int i = 0; + + /* Locate the appropriate clock setting */ + while (i < ARRAY_SIZE(stu300_clktable) - 1 && + stu300_clktable[i].rate < clkrate) + i++; + + if (stu300_clktable[i].setting == 0xFFU) { + dev_err(&dev->pdev->dev, "too %s clock rate requested " + "(%lu Hz).\n", i ? "high" : "low", clkrate); + return -EINVAL; + } + + stu300_wr8(stu300_clktable[i].setting, + dev->virtbase + I2C_OAR2); + + dev_dbg(&dev->pdev->dev, "Clock rate %lu Hz, I2C bus speed %d Hz " + "virtbase %p\n", clkrate, dev->speed, dev->virtbase); + + if (dev->speed > 100000) + /* Fast Mode I2C */ + val = ((clkrate/dev->speed) - 9)/3 + 1; + else + /* Standard Mode I2C */ + val = ((clkrate/dev->speed) - 7)/2 + 1; + + /* According to spec the divider must be > 2 */ + if (val < 0x002) { + dev_err(&dev->pdev->dev, "too low clock rate (%lu Hz).\n", + clkrate); + return -EINVAL; + } + + /* We have 12 bits clock divider only! */ + if (val & 0xFFFFF000U) { + dev_err(&dev->pdev->dev, "too high clock rate (%lu Hz).\n", + clkrate); + return -EINVAL; + } + + if (dev->speed > 100000) { + /* CC6..CC0 */ + stu300_wr8((val & I2C_CCR_CC_MASK) | I2C_CCR_FMSM, + dev->virtbase + I2C_CCR); + dev_dbg(&dev->pdev->dev, "set clock divider to 0x%08x, " + "Fast Mode I2C\n", val); + } else { + /* CC6..CC0 */ + stu300_wr8((val & I2C_CCR_CC_MASK), + dev->virtbase + I2C_CCR); + dev_dbg(&dev->pdev->dev, "set clock divider to " + "0x%08x, Standard Mode I2C\n", val); + } + + /* CC11..CC7 */ + stu300_wr8(((val >> 7) & 0x1F), + dev->virtbase + I2C_ECCR); + + return 0; +} + + +static int stu300_init_hw(struct stu300_dev *dev) +{ + u32 dummy; + unsigned long clkrate; + int ret; + + /* Disable controller */ + stu300_wr8(0x00, dev->virtbase + I2C_CR); + /* + * Set own address to some default value (0x00). + * We do not support slave mode anyway. + */ + stu300_wr8(0x00, dev->virtbase + I2C_OAR1); + /* + * The I2C controller only operates properly in 26 MHz but we + * program this driver as if we didn't know. This will also set the two + * high bits of the own address to zero as well. + * There is no known hardware issue with running in 13 MHz + * However, speeds over 200 kHz are not used. + */ + clkrate = clk_get_rate(dev->clk); + ret = stu300_set_clk(dev, clkrate); + + if (ret) + return ret; + /* + * Enable block, do it TWICE (hardware glitch) + * Setting bit 7 can enable DDC mode. (Not used currently.) + */ + stu300_wr8(I2C_CR_PERIPHERAL_ENABLE, + dev->virtbase + I2C_CR); + stu300_wr8(I2C_CR_PERIPHERAL_ENABLE, + dev->virtbase + I2C_CR); + /* Make a dummy read of the status register SR1 & SR2 */ + dummy = stu300_r8(dev->virtbase + I2C_SR2); + dummy = stu300_r8(dev->virtbase + I2C_SR1); + + return 0; +} + + + +/* Send slave address. */ +static int stu300_send_address(struct stu300_dev *dev, + struct i2c_msg *msg, int resend) +{ + u32 val; + int ret; + + if (msg->flags & I2C_M_TEN) + /* This is probably how 10 bit addresses look */ + val = (0xf0 | (((u32) msg->addr & 0x300) >> 7)) & + I2C_DR_D_MASK; + else + val = ((msg->addr << 1) & I2C_DR_D_MASK); + + if (msg->flags & I2C_M_RD) { + /* This is the direction bit */ + val |= 0x01; + if (resend) + dev_dbg(&dev->pdev->dev, "read resend\n"); + } else if (resend) + dev_dbg(&dev->pdev->dev, "write resend\n"); + stu300_wr8(val, dev->virtbase + I2C_DR); + + /* For 10bit addressing, await 10bit request (EVENT 9) */ + if (msg->flags & I2C_M_TEN) { + ret = stu300_await_event(dev, STU300_EVENT_9); + /* + * The slave device wants a 10bit address, send the rest + * of the bits (the LSBits) + */ + val = msg->addr & I2C_DR_D_MASK; + /* This clears "event 9" */ + stu300_wr8(val, dev->virtbase + I2C_DR); + if (ret != 0) + return ret; + } + /* FIXME: Why no else here? two events for 10bit? + * Await event 6 (normal) or event 9 (10bit) + */ + + if (resend) + dev_dbg(&dev->pdev->dev, "await event 6\n"); + ret = stu300_await_event(dev, STU300_EVENT_6); + + /* + * Clear any pending EVENT 6 no matter what happened during + * await_event. + */ + val = stu300_r8(dev->virtbase + I2C_CR); + val |= I2C_CR_PERIPHERAL_ENABLE; + stu300_wr8(val, dev->virtbase + I2C_CR); + + return ret; +} + +static int stu300_xfer_msg(struct i2c_adapter *adap, + struct i2c_msg *msg, int stop) +{ + u32 cr; + u32 val; + u32 i; + int ret; + int attempts = 0; + struct stu300_dev *dev = i2c_get_adapdata(adap); + + clk_enable(dev->clk); + + /* Remove this if (0) to trace each and every message. */ + if (0) { + dev_dbg(&dev->pdev->dev, "I2C message to: 0x%04x, len: %d, " + "flags: 0x%04x, stop: %d\n", + msg->addr, msg->len, msg->flags, stop); + } + + /* Zero-length messages are not supported by this hardware */ + if (msg->len == 0) { + ret = -EINVAL; + goto exit_disable; + } + + /* + * For some reason, sending the address sometimes fails when running + * on the 13 MHz clock. No interrupt arrives. This is a work around, + * which tries to restart and send the address up to 10 times before + * really giving up. Usually 5 to 8 attempts are enough. + */ + do { + if (attempts) + dev_dbg(&dev->pdev->dev, "wait while busy\n"); + /* Check that the bus is free, or wait until some timeout */ + ret = stu300_wait_while_busy(dev); + if (ret != 0) + goto exit_disable; + + if (attempts) + dev_dbg(&dev->pdev->dev, "re-int hw\n"); + /* + * According to ST, there is no problem if the clock is + * changed between 13 and 26 MHz during a transfer. + */ + ret = stu300_init_hw(dev); + if (ret) + goto exit_disable; + + /* Send a start condition */ + cr = I2C_CR_PERIPHERAL_ENABLE; + /* Setting the START bit puts the block in master mode */ + if (!(msg->flags & I2C_M_NOSTART)) + cr |= I2C_CR_START_ENABLE; + if ((msg->flags & I2C_M_RD) && (msg->len > 1)) + /* On read more than 1 byte, we need ack. */ + cr |= I2C_CR_ACK_ENABLE; + /* Check that it gets through */ + if (!(msg->flags & I2C_M_NOSTART)) { + if (attempts) + dev_dbg(&dev->pdev->dev, "send start event\n"); + ret = stu300_start_and_await_event(dev, cr, + STU300_EVENT_5); + } + + if (attempts) + dev_dbg(&dev->pdev->dev, "send address\n"); + + if (ret == 0) + /* Send address */ + ret = stu300_send_address(dev, msg, attempts != 0); + + if (ret != 0) { + attempts++; + dev_dbg(&dev->pdev->dev, "failed sending address, " + "retrying. Attempt: %d msg_index: %d/%d\n", + attempts, dev->msg_index, dev->msg_len); + } + + } while (ret != 0 && attempts < NUM_ADDR_RESEND_ATTEMPTS); + + if (attempts < NUM_ADDR_RESEND_ATTEMPTS && attempts > 0) { + dev_dbg(&dev->pdev->dev, "managed to get address " + "through after %d attempts\n", attempts); + } else if (attempts == NUM_ADDR_RESEND_ATTEMPTS) { + dev_dbg(&dev->pdev->dev, "I give up, tried %d times " + "to resend address.\n", + NUM_ADDR_RESEND_ATTEMPTS); + goto exit_disable; + } + + + if (msg->flags & I2C_M_RD) { + /* READ: we read the actual bytes one at a time */ + for (i = 0; i < msg->len; i++) { + if (i == msg->len-1) { + /* + * Disable ACK and set STOP condition before + * reading last byte + */ + val = I2C_CR_PERIPHERAL_ENABLE; + + if (stop) + val |= I2C_CR_STOP_ENABLE; + + stu300_wr8(val, + dev->virtbase + I2C_CR); + } + /* Wait for this byte... */ + ret = stu300_await_event(dev, STU300_EVENT_7); + if (ret != 0) + goto exit_disable; + /* This clears event 7 */ + msg->buf[i] = (u8) stu300_r8(dev->virtbase + I2C_DR); + } + } else { + /* WRITE: we send the actual bytes one at a time */ + for (i = 0; i < msg->len; i++) { + /* Write the byte */ + stu300_wr8(msg->buf[i], + dev->virtbase + I2C_DR); + /* Check status */ + ret = stu300_await_event(dev, STU300_EVENT_8); + /* Next write to DR will clear event 8 */ + if (ret != 0) { + dev_err(&dev->pdev->dev, "error awaiting " + "event 8 (%d)\n", ret); + goto exit_disable; + } + } + /* Check NAK */ + if (!(msg->flags & I2C_M_IGNORE_NAK)) { + if (stu300_r8(dev->virtbase + I2C_SR2) & + I2C_SR2_AF_IND) { + dev_err(&dev->pdev->dev, "I2C payload " + "send returned NAK!\n"); + ret = -EIO; + goto exit_disable; + } + } + if (stop) { + /* Send stop condition */ + val = I2C_CR_PERIPHERAL_ENABLE; + val |= I2C_CR_STOP_ENABLE; + stu300_wr8(val, dev->virtbase + I2C_CR); + } + } + + /* Check that the bus is free, or wait until some timeout occurs */ + ret = stu300_wait_while_busy(dev); + if (ret != 0) { + dev_err(&dev->pdev->dev, "timout waiting for transfer " + "to commence.\n"); + goto exit_disable; + } + + /* Dummy read status registers */ + val = stu300_r8(dev->virtbase + I2C_SR2); + val = stu300_r8(dev->virtbase + I2C_SR1); + ret = 0; + + exit_disable: + /* Disable controller */ + stu300_wr8(0x00, dev->virtbase + I2C_CR); + clk_disable(dev->clk); + return ret; +} + +static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + int ret = -1; + int i; + + struct stu300_dev *dev = i2c_get_adapdata(adap); + dev->msg_len = num; + + for (i = 0; i < num; i++) { + /* + * Another driver appears to send stop for each message, + * here we only do that for the last message. Possibly some + * peripherals require this behaviour, then their drivers + * have to send single messages in order to get "stop" for + * each message. + */ + dev->msg_index = i; + + ret = stu300_xfer_msg(adap, &msgs[i], (i == (num - 1))); + + if (ret != 0) { + num = ret; + break; + } + } + + return num; +} + +static u32 stu300_func(struct i2c_adapter *adap) +{ + /* This is the simplest thing you can think of... */ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm stu300_algo = { + .master_xfer = stu300_xfer, + .functionality = stu300_func, +}; + +static int __init +stu300_probe(struct platform_device *pdev) +{ + struct stu300_dev *dev; + struct i2c_adapter *adap; + struct resource *res; + int bus_nr; + int ret = 0; + char clk_name[] = "I2C0"; + + dev = kzalloc(sizeof(struct stu300_dev), GFP_KERNEL); + if (!dev) { + dev_err(&pdev->dev, "could not allocate device struct\n"); + ret = -ENOMEM; + goto err_no_devmem; + } + + bus_nr = pdev->id; + clk_name[3] += (char)bus_nr; + dev->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(dev->clk)) { + ret = PTR_ERR(dev->clk); + dev_err(&pdev->dev, "could not retrieve i2c bus clock\n"); + goto err_no_clk; + } + + dev->pdev = pdev; + platform_set_drvdata(pdev, dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOENT; + goto err_no_resource; + } + + dev->phybase = res->start; + dev->physize = resource_size(res); + + if (request_mem_region(dev->phybase, dev->physize, + NAME " I/O Area") == NULL) { + ret = -EBUSY; + goto err_no_ioregion; + } + + dev->virtbase = ioremap(dev->phybase, dev->physize); + dev_dbg(&pdev->dev, "initialize bus device I2C%d on virtual " + "base %p\n", bus_nr, dev->virtbase); + if (!dev->virtbase) { + ret = -ENOMEM; + goto err_no_ioremap; + } + + dev->irq = platform_get_irq(pdev, 0); + if (request_irq(dev->irq, stu300_irh, IRQF_DISABLED, + NAME, dev)) { + ret = -EIO; + goto err_no_irq; + } + + dev->speed = scl_frequency; + + clk_enable(dev->clk); + ret = stu300_init_hw(dev); + clk_disable(dev->clk); + + if (ret != 0) { + dev_err(&dev->pdev->dev, "error initializing hardware.\n"); + goto err_init_hw; + } + + /* IRQ event handling initialization */ + spin_lock_init(&dev->cmd_issue_lock); + dev->cmd_event = STU300_EVENT_NONE; + dev->cmd_err = STU300_ERROR_NONE; + + adap = &dev->adapter; + adap->owner = THIS_MODULE; + /* DDC class but actually often used for more generic I2C */ + adap->class = I2C_CLASS_DDC; + strlcpy(adap->name, "ST Microelectronics DDC I2C adapter", + sizeof(adap->name)); + adap->nr = bus_nr; + adap->algo = &stu300_algo; + adap->dev.parent = &pdev->dev; + i2c_set_adapdata(adap, dev); + + /* i2c device drivers may be active on return from add_adapter() */ + ret = i2c_add_numbered_adapter(adap); + if (ret) { + dev_err(&dev->pdev->dev, "failure adding ST Micro DDC " + "I2C adapter\n"); + goto err_add_adapter; + } + return 0; + + err_add_adapter: + err_init_hw: + free_irq(dev->irq, dev); + err_no_irq: + iounmap(dev->virtbase); + err_no_ioremap: + release_mem_region(dev->phybase, dev->physize); + err_no_ioregion: + platform_set_drvdata(pdev, NULL); + err_no_resource: + clk_put(dev->clk); + err_no_clk: + kfree(dev); + err_no_devmem: + dev_err(&pdev->dev, "failed to add " NAME " adapter: %d\n", + pdev->id); + return ret; +} + +#ifdef CONFIG_PM +static int stu300_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct stu300_dev *dev = platform_get_drvdata(pdev); + + /* Turn off everything */ + stu300_wr8(0x00, dev->virtbase + I2C_CR); + return 0; +} + +static int stu300_resume(struct platform_device *pdev) +{ + int ret = 0; + struct stu300_dev *dev = platform_get_drvdata(pdev); + + clk_enable(dev->clk); + ret = stu300_init_hw(dev); + clk_disable(dev->clk); + + if (ret != 0) + dev_err(&pdev->dev, "error re-initializing hardware.\n"); + return ret; +} +#else +#define stu300_suspend NULL +#define stu300_resume NULL +#endif + +static int __exit +stu300_remove(struct platform_device *pdev) +{ + struct stu300_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adapter); + /* Turn off everything */ + stu300_wr8(0x00, dev->virtbase + I2C_CR); + free_irq(dev->irq, dev); + iounmap(dev->virtbase); + release_mem_region(dev->phybase, dev->physize); + clk_put(dev->clk); + platform_set_drvdata(pdev, NULL); + kfree(dev); + return 0; +} + +static struct platform_driver stu300_i2c_driver = { + .driver = { + .name = NAME, + .owner = THIS_MODULE, + }, + .remove = __exit_p(stu300_remove), + .suspend = stu300_suspend, + .resume = stu300_resume, + +}; + +static int __init stu300_init(void) +{ + return platform_driver_probe(&stu300_i2c_driver, stu300_probe); +} + +static void __exit stu300_exit(void) +{ + platform_driver_unregister(&stu300_i2c_driver); +} + +/* + * The systems using this bus often have very basic devices such + * as regulators on the I2C bus, so this needs to be loaded early. + * Therefore it is registered in the subsys_initcall(). + */ +subsys_initcall(stu300_init); +module_exit(stu300_exit); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>"); +MODULE_DESCRIPTION("ST Micro DDC I2C adapter (" NAME ")"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" NAME); diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c new file mode 100644 index 00000000..b1b34479 --- /dev/null +++ b/drivers/i2c/busses/i2c-stub.c @@ -0,0 +1,222 @@ +/* + i2c-stub.c - I2C/SMBus chip emulator + + Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com> + Copyright (C) 2007 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. +*/ + +#define DEBUG 1 + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/i2c.h> + +#define MAX_CHIPS 10 +#define STUB_FUNC (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_I2C_BLOCK) + +static unsigned short chip_addr[MAX_CHIPS]; +module_param_array(chip_addr, ushort, NULL, S_IRUGO); +MODULE_PARM_DESC(chip_addr, + "Chip addresses (up to 10, between 0x03 and 0x77)"); + +static unsigned long functionality = STUB_FUNC; +module_param(functionality, ulong, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(functionality, "Override functionality bitfield"); + +struct stub_chip { + u8 pointer; + u16 words[256]; /* Byte operations use the LSB as per SMBus + specification */ +}; + +static struct stub_chip *stub_chips; + +/* Return negative errno on error. */ +static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, union i2c_smbus_data * data) +{ + s32 ret; + int i, len; + struct stub_chip *chip = NULL; + + /* Search for the right chip */ + for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { + if (addr == chip_addr[i]) { + chip = stub_chips + i; + break; + } + } + if (!chip) + return -ENODEV; + + switch (size) { + + case I2C_SMBUS_QUICK: + dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr); + ret = 0; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + chip->pointer = command; + dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " + "wrote 0x%02x.\n", + addr, command); + } else { + data->byte = chip->words[chip->pointer++] & 0xff; + dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " + "read 0x%02x.\n", + addr, data->byte); + } + + ret = 0; + break; + + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) { + chip->words[command] &= 0xff00; + chip->words[command] |= data->byte; + dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " + "wrote 0x%02x at 0x%02x.\n", + addr, data->byte, command); + } else { + data->byte = chip->words[command] & 0xff; + dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " + "read 0x%02x at 0x%02x.\n", + addr, data->byte, command); + } + chip->pointer = command + 1; + + ret = 0; + break; + + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_WRITE) { + chip->words[command] = data->word; + dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " + "wrote 0x%04x at 0x%02x.\n", + addr, data->word, command); + } else { + data->word = chip->words[command]; + dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " + "read 0x%04x at 0x%02x.\n", + addr, data->word, command); + } + + ret = 0; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = data->block[0]; + if (read_write == I2C_SMBUS_WRITE) { + for (i = 0; i < len; i++) { + chip->words[command + i] &= 0xff00; + chip->words[command + i] |= data->block[1 + i]; + } + dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, " + "wrote %d bytes at 0x%02x.\n", + addr, len, command); + } else { + for (i = 0; i < len; i++) { + data->block[1 + i] = + chip->words[command + i] & 0xff; + } + dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, " + "read %d bytes at 0x%02x.\n", + addr, len, command); + } + + ret = 0; + break; + + default: + dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); + ret = -EOPNOTSUPP; + break; + } /* switch (size) */ + + return ret; +} + +static u32 stub_func(struct i2c_adapter *adapter) +{ + return STUB_FUNC & functionality; +} + +static const struct i2c_algorithm smbus_algorithm = { + .functionality = stub_func, + .smbus_xfer = stub_xfer, +}; + +static struct i2c_adapter stub_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, + .name = "SMBus stub driver", +}; + +static int __init i2c_stub_init(void) +{ + int i, ret; + + if (!chip_addr[0]) { + printk(KERN_ERR "i2c-stub: Please specify a chip address\n"); + return -ENODEV; + } + + for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { + if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) { + printk(KERN_ERR "i2c-stub: Invalid chip address " + "0x%02x\n", chip_addr[i]); + return -EINVAL; + } + + printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n", + chip_addr[i]); + } + + /* Allocate memory for all chips at once */ + stub_chips = kzalloc(i * sizeof(struct stub_chip), GFP_KERNEL); + if (!stub_chips) { + printk(KERN_ERR "i2c-stub: Out of memory\n"); + return -ENOMEM; + } + + ret = i2c_add_adapter(&stub_adapter); + if (ret) + kfree(stub_chips); + return ret; +} + +static void __exit i2c_stub_exit(void) +{ + i2c_del_adapter(&stub_adapter); + kfree(stub_chips); +} + +MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); +MODULE_DESCRIPTION("I2C stub driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_stub_init); +module_exit(i2c_stub_exit); + diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c new file mode 100644 index 00000000..26c352a0 --- /dev/null +++ b/drivers/i2c/busses/i2c-taos-evm.c @@ -0,0 +1,331 @@ +/* + * Driver for the TAOS evaluation modules + * These devices include an I2C master which can be controlled over the + * serial port. + * + * Copyright (C) 2007 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; version 2 of the License. + * + * 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/delay.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> +#include <linux/i2c.h> + +#define TAOS_BUFFER_SIZE 63 + +#define TAOS_STATE_INIT 0 +#define TAOS_STATE_IDLE 1 +#define TAOS_STATE_EOFF 2 +#define TAOS_STATE_RECV 3 + +#define TAOS_CMD_RESET 0x12 +#define TAOS_CMD_ECHO_ON '+' +#define TAOS_CMD_ECHO_OFF '-' + +static DECLARE_WAIT_QUEUE_HEAD(wq); + +struct taos_data { + struct i2c_adapter adapter; + struct i2c_client *client; + int state; + u8 addr; /* last used address */ + unsigned char buffer[TAOS_BUFFER_SIZE]; + unsigned int pos; /* position inside the buffer */ +}; + +/* TAOS TSL2550 EVM */ +static struct i2c_board_info tsl2550_info = { + I2C_BOARD_INFO("tsl2550", 0x39), +}; + +/* Instantiate i2c devices based on the adapter name */ +static struct i2c_client *taos_instantiate_device(struct i2c_adapter *adapter) +{ + if (!strncmp(adapter->name, "TAOS TSL2550 EVM", 16)) { + dev_info(&adapter->dev, "Instantiating device %s at 0x%02x\n", + tsl2550_info.type, tsl2550_info.addr); + return i2c_new_device(adapter, &tsl2550_info); + } + + return NULL; +} + +static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct serio *serio = adapter->algo_data; + struct taos_data *taos = serio_get_drvdata(serio); + char *p; + + /* Encode our transaction. "@" is for the device address, "$" for the + SMBus command and "#" for the data. */ + p = taos->buffer; + + /* The device remembers the last used address, no need to send it + again if it's the same */ + if (addr != taos->addr) + p += sprintf(p, "@%02X", addr); + + switch (size) { + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) + sprintf(p, "$#%02X", command); + else + sprintf(p, "$"); + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) + sprintf(p, "$%02X#%02X", command, data->byte); + else + sprintf(p, "$%02X", command); + break; + default: + dev_warn(&adapter->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + /* Send the transaction to the TAOS EVM */ + dev_dbg(&adapter->dev, "Command buffer: %s\n", taos->buffer); + for (p = taos->buffer; *p; p++) + serio_write(serio, *p); + + taos->addr = addr; + + /* Start the transaction and read the answer */ + taos->pos = 0; + taos->state = TAOS_STATE_RECV; + serio_write(serio, read_write == I2C_SMBUS_WRITE ? '>' : '<'); + wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, + msecs_to_jiffies(150)); + if (taos->state != TAOS_STATE_IDLE + || taos->pos != 5) { + dev_err(&adapter->dev, "Transaction timeout (pos=%d)\n", + taos->pos); + return -EIO; + } + dev_dbg(&adapter->dev, "Answer buffer: %s\n", taos->buffer); + + /* Interpret the returned string */ + p = taos->buffer + 1; + p[3] = '\0'; + if (!strcmp(p, "NAK")) + return -ENODEV; + + if (read_write == I2C_SMBUS_WRITE) { + if (!strcmp(p, "ACK")) + return 0; + } else { + if (p[0] == 'x') { + data->byte = simple_strtol(p + 1, NULL, 16); + return 0; + } + } + + return -EIO; +} + +static u32 taos_smbus_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA; +} + +static const struct i2c_algorithm taos_algorithm = { + .smbus_xfer = taos_smbus_xfer, + .functionality = taos_smbus_func, +}; + +static irqreturn_t taos_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct taos_data *taos = serio_get_drvdata(serio); + + switch (taos->state) { + case TAOS_STATE_INIT: + taos->buffer[taos->pos++] = data; + if (data == ':' + || taos->pos == TAOS_BUFFER_SIZE - 1) { + taos->buffer[taos->pos] = '\0'; + taos->state = TAOS_STATE_IDLE; + wake_up_interruptible(&wq); + } + break; + case TAOS_STATE_EOFF: + taos->state = TAOS_STATE_IDLE; + wake_up_interruptible(&wq); + break; + case TAOS_STATE_RECV: + taos->buffer[taos->pos++] = data; + if (data == ']') { + taos->buffer[taos->pos] = '\0'; + taos->state = TAOS_STATE_IDLE; + wake_up_interruptible(&wq); + } + break; + } + + return IRQ_HANDLED; +} + +/* Extract the adapter name from the buffer received after reset. + The buffer is modified and a pointer inside the buffer is returned. */ +static char *taos_adapter_name(char *buffer) +{ + char *start, *end; + + start = strstr(buffer, "TAOS "); + if (!start) + return NULL; + + end = strchr(start, '\r'); + if (!end) + return NULL; + *end = '\0'; + + return start; +} + +static int taos_connect(struct serio *serio, struct serio_driver *drv) +{ + struct taos_data *taos; + struct i2c_adapter *adapter; + char *name; + int err; + + taos = kzalloc(sizeof(struct taos_data), GFP_KERNEL); + if (!taos) { + err = -ENOMEM; + goto exit; + } + taos->state = TAOS_STATE_INIT; + serio_set_drvdata(serio, taos); + + err = serio_open(serio, drv); + if (err) + goto exit_kfree; + + adapter = &taos->adapter; + adapter->owner = THIS_MODULE; + adapter->algo = &taos_algorithm; + adapter->algo_data = serio; + adapter->dev.parent = &serio->dev; + + /* Reset the TAOS evaluation module to identify it */ + serio_write(serio, TAOS_CMD_RESET); + wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, + msecs_to_jiffies(2000)); + + if (taos->state != TAOS_STATE_IDLE) { + err = -ENODEV; + dev_err(&serio->dev, "TAOS EVM reset failed (state=%d, " + "pos=%d)\n", taos->state, taos->pos); + goto exit_close; + } + + name = taos_adapter_name(taos->buffer); + if (!name) { + err = -ENODEV; + dev_err(&serio->dev, "TAOS EVM identification failed\n"); + goto exit_close; + } + strlcpy(adapter->name, name, sizeof(adapter->name)); + + /* Turn echo off for better performance */ + taos->state = TAOS_STATE_EOFF; + serio_write(serio, TAOS_CMD_ECHO_OFF); + + wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, + msecs_to_jiffies(250)); + if (taos->state != TAOS_STATE_IDLE) { + err = -ENODEV; + dev_err(&serio->dev, "TAOS EVM echo off failed " + "(state=%d)\n", taos->state); + goto exit_close; + } + + err = i2c_add_adapter(adapter); + if (err) + goto exit_close; + dev_info(&serio->dev, "Connected to TAOS EVM\n"); + + taos->client = taos_instantiate_device(adapter); + return 0; + + exit_close: + serio_close(serio); + exit_kfree: + serio_set_drvdata(serio, NULL); + kfree(taos); + exit: + return err; +} + +static void taos_disconnect(struct serio *serio) +{ + struct taos_data *taos = serio_get_drvdata(serio); + + if (taos->client) + i2c_unregister_device(taos->client); + i2c_del_adapter(&taos->adapter); + serio_close(serio); + serio_set_drvdata(serio, NULL); + kfree(taos); + + dev_info(&serio->dev, "Disconnected from TAOS EVM\n"); +} + +static struct serio_device_id taos_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TAOSEVM, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; +MODULE_DEVICE_TABLE(serio, taos_serio_ids); + +static struct serio_driver taos_drv = { + .driver = { + .name = "taos-evm", + }, + .description = "TAOS evaluation module driver", + .id_table = taos_serio_ids, + .connect = taos_connect, + .disconnect = taos_disconnect, + .interrupt = taos_interrupt, +}; + +static int __init taos_init(void) +{ + return serio_register_driver(&taos_drv); +} + +static void __exit taos_exit(void) +{ + serio_unregister_driver(&taos_drv); +} + +MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("TAOS evaluation module driver"); +MODULE_LICENSE("GPL"); + +module_init(taos_init); +module_exit(taos_exit); diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c new file mode 100644 index 00000000..fb3b4f8f --- /dev/null +++ b/drivers/i2c/busses/i2c-tegra.c @@ -0,0 +1,735 @@ +/* + * drivers/i2c/busses/i2c-tegra.c + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross <ccross@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/i2c-tegra.h> + +#include <asm/unaligned.h> + +#include <mach/clk.h> + +#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000)) +#define BYTES_PER_FIFO_WORD 4 + +#define I2C_CNFG 0x000 +#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 +#define I2C_CNFG_PACKET_MODE_EN (1<<10) +#define I2C_CNFG_NEW_MASTER_FSM (1<<11) +#define I2C_STATUS 0x01C +#define I2C_SL_CNFG 0x020 +#define I2C_SL_CNFG_NACK (1<<1) +#define I2C_SL_CNFG_NEWSL (1<<2) +#define I2C_SL_ADDR1 0x02c +#define I2C_SL_ADDR2 0x030 +#define I2C_TX_FIFO 0x050 +#define I2C_RX_FIFO 0x054 +#define I2C_PACKET_TRANSFER_STATUS 0x058 +#define I2C_FIFO_CONTROL 0x05c +#define I2C_FIFO_CONTROL_TX_FLUSH (1<<1) +#define I2C_FIFO_CONTROL_RX_FLUSH (1<<0) +#define I2C_FIFO_CONTROL_TX_TRIG_SHIFT 5 +#define I2C_FIFO_CONTROL_RX_TRIG_SHIFT 2 +#define I2C_FIFO_STATUS 0x060 +#define I2C_FIFO_STATUS_TX_MASK 0xF0 +#define I2C_FIFO_STATUS_TX_SHIFT 4 +#define I2C_FIFO_STATUS_RX_MASK 0x0F +#define I2C_FIFO_STATUS_RX_SHIFT 0 +#define I2C_INT_MASK 0x064 +#define I2C_INT_STATUS 0x068 +#define I2C_INT_PACKET_XFER_COMPLETE (1<<7) +#define I2C_INT_ALL_PACKETS_XFER_COMPLETE (1<<6) +#define I2C_INT_TX_FIFO_OVERFLOW (1<<5) +#define I2C_INT_RX_FIFO_UNDERFLOW (1<<4) +#define I2C_INT_NO_ACK (1<<3) +#define I2C_INT_ARBITRATION_LOST (1<<2) +#define I2C_INT_TX_FIFO_DATA_REQ (1<<1) +#define I2C_INT_RX_FIFO_DATA_REQ (1<<0) +#define I2C_CLK_DIVISOR 0x06c + +#define DVC_CTRL_REG1 0x000 +#define DVC_CTRL_REG1_INTR_EN (1<<10) +#define DVC_CTRL_REG2 0x004 +#define DVC_CTRL_REG3 0x008 +#define DVC_CTRL_REG3_SW_PROG (1<<26) +#define DVC_CTRL_REG3_I2C_DONE_INTR_EN (1<<30) +#define DVC_STATUS 0x00c +#define DVC_STATUS_I2C_DONE_INTR (1<<30) + +#define I2C_ERR_NONE 0x00 +#define I2C_ERR_NO_ACK 0x01 +#define I2C_ERR_ARBITRATION_LOST 0x02 +#define I2C_ERR_UNKNOWN_INTERRUPT 0x04 + +#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 +#define PACKET_HEADER0_PACKET_ID_SHIFT 16 +#define PACKET_HEADER0_CONT_ID_SHIFT 12 +#define PACKET_HEADER0_PROTOCOL_I2C (1<<4) + +#define I2C_HEADER_HIGHSPEED_MODE (1<<22) +#define I2C_HEADER_CONT_ON_NAK (1<<21) +#define I2C_HEADER_SEND_START_BYTE (1<<20) +#define I2C_HEADER_READ (1<<19) +#define I2C_HEADER_10BIT_ADDR (1<<18) +#define I2C_HEADER_IE_ENABLE (1<<17) +#define I2C_HEADER_REPEAT_START (1<<16) +#define I2C_HEADER_MASTER_ADDR_SHIFT 12 +#define I2C_HEADER_SLAVE_ADDR_SHIFT 1 + +/** + * struct tegra_i2c_dev - per device i2c context + * @dev: device reference for power management + * @adapter: core i2c layer adapter information + * @clk: clock reference for i2c controller + * @i2c_clk: clock reference for i2c bus + * @iomem: memory resource for registers + * @base: ioremapped registers cookie + * @cont_id: i2c controller id, used for for packet header + * @irq: irq number of transfer complete interrupt + * @is_dvc: identifies the DVC i2c controller, has a different register layout + * @msg_complete: transfer completion notifier + * @msg_err: error code for completed message + * @msg_buf: pointer to current message data + * @msg_buf_remaining: size of unsent data in the message buffer + * @msg_read: identifies read transfers + * @bus_clk_rate: current i2c bus clock rate + * @is_suspended: prevents i2c controller accesses after suspend is called + */ +struct tegra_i2c_dev { + struct device *dev; + struct i2c_adapter adapter; + struct clk *clk; + struct clk *i2c_clk; + struct resource *iomem; + void __iomem *base; + int cont_id; + int irq; + bool irq_disabled; + int is_dvc; + struct completion msg_complete; + int msg_err; + u8 *msg_buf; + size_t msg_buf_remaining; + int msg_read; + unsigned long bus_clk_rate; + bool is_suspended; +}; + +static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg) +{ + writel(val, i2c_dev->base + reg); +} + +static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg) +{ + return readl(i2c_dev->base + reg); +} + +/* + * i2c_writel and i2c_readl will offset the register if necessary to talk + * to the I2C block inside the DVC block + */ +static unsigned long tegra_i2c_reg_addr(struct tegra_i2c_dev *i2c_dev, + unsigned long reg) +{ + if (i2c_dev->is_dvc) + reg += (reg >= I2C_TX_FIFO) ? 0x10 : 0x40; + return reg; +} + +static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val, + unsigned long reg) +{ + writel(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg)); +} + +static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg) +{ + return readl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg)); +} + +static void i2c_writesl(struct tegra_i2c_dev *i2c_dev, void *data, + unsigned long reg, int len) +{ + writesl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len); +} + +static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data, + unsigned long reg, int len) +{ + readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len); +} + +static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) +{ + u32 int_mask = i2c_readl(i2c_dev, I2C_INT_MASK); + int_mask &= ~mask; + i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); +} + +static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) +{ + u32 int_mask = i2c_readl(i2c_dev, I2C_INT_MASK); + int_mask |= mask; + i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); +} + +static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev) +{ + unsigned long timeout = jiffies + HZ; + u32 val = i2c_readl(i2c_dev, I2C_FIFO_CONTROL); + val |= I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH; + i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); + + while (i2c_readl(i2c_dev, I2C_FIFO_CONTROL) & + (I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH)) { + if (time_after(jiffies, timeout)) { + dev_warn(i2c_dev->dev, "timeout waiting for fifo flush\n"); + return -ETIMEDOUT; + } + msleep(1); + } + return 0; +} + +static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev) +{ + u32 val; + int rx_fifo_avail; + u8 *buf = i2c_dev->msg_buf; + size_t buf_remaining = i2c_dev->msg_buf_remaining; + int words_to_transfer; + + val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); + rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >> + I2C_FIFO_STATUS_RX_SHIFT; + + /* Rounds down to not include partial word at the end of buf */ + words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD; + if (words_to_transfer > rx_fifo_avail) + words_to_transfer = rx_fifo_avail; + + i2c_readsl(i2c_dev, buf, I2C_RX_FIFO, words_to_transfer); + + buf += words_to_transfer * BYTES_PER_FIFO_WORD; + buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD; + rx_fifo_avail -= words_to_transfer; + + /* + * If there is a partial word at the end of buf, handle it manually to + * prevent overwriting past the end of buf + */ + if (rx_fifo_avail > 0 && buf_remaining > 0) { + BUG_ON(buf_remaining > 3); + val = i2c_readl(i2c_dev, I2C_RX_FIFO); + memcpy(buf, &val, buf_remaining); + buf_remaining = 0; + rx_fifo_avail--; + } + + BUG_ON(rx_fifo_avail > 0 && buf_remaining > 0); + i2c_dev->msg_buf_remaining = buf_remaining; + i2c_dev->msg_buf = buf; + return 0; +} + +static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) +{ + u32 val; + int tx_fifo_avail; + u8 *buf = i2c_dev->msg_buf; + size_t buf_remaining = i2c_dev->msg_buf_remaining; + int words_to_transfer; + + val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); + tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >> + I2C_FIFO_STATUS_TX_SHIFT; + + /* Rounds down to not include partial word at the end of buf */ + words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD; + if (words_to_transfer > tx_fifo_avail) + words_to_transfer = tx_fifo_avail; + + i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer); + + buf += words_to_transfer * BYTES_PER_FIFO_WORD; + buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD; + tx_fifo_avail -= words_to_transfer; + + /* + * If there is a partial word at the end of buf, handle it manually to + * prevent reading past the end of buf, which could cross a page + * boundary and fault. + */ + if (tx_fifo_avail > 0 && buf_remaining > 0) { + BUG_ON(buf_remaining > 3); + memcpy(&val, buf, buf_remaining); + i2c_writel(i2c_dev, val, I2C_TX_FIFO); + buf_remaining = 0; + tx_fifo_avail--; + } + + BUG_ON(tx_fifo_avail > 0 && buf_remaining > 0); + i2c_dev->msg_buf_remaining = buf_remaining; + i2c_dev->msg_buf = buf; + return 0; +} + +/* + * One of the Tegra I2C blocks is inside the DVC (Digital Voltage Controller) + * block. This block is identical to the rest of the I2C blocks, except that + * it only supports master mode, it has registers moved around, and it needs + * some extra init to get it into I2C mode. The register moves are handled + * by i2c_readl and i2c_writel + */ +static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev) +{ + u32 val = 0; + val = dvc_readl(i2c_dev, DVC_CTRL_REG3); + val |= DVC_CTRL_REG3_SW_PROG; + val |= DVC_CTRL_REG3_I2C_DONE_INTR_EN; + dvc_writel(i2c_dev, val, DVC_CTRL_REG3); + + val = dvc_readl(i2c_dev, DVC_CTRL_REG1); + val |= DVC_CTRL_REG1_INTR_EN; + dvc_writel(i2c_dev, val, DVC_CTRL_REG1); +} + +static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) +{ + u32 val; + int err = 0; + + clk_enable(i2c_dev->clk); + + tegra_periph_reset_assert(i2c_dev->clk); + udelay(2); + tegra_periph_reset_deassert(i2c_dev->clk); + + if (i2c_dev->is_dvc) + tegra_dvc_init(i2c_dev); + + val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN | + (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); + i2c_writel(i2c_dev, val, I2C_CNFG); + i2c_writel(i2c_dev, 0, I2C_INT_MASK); + clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8); + + if (!i2c_dev->is_dvc) { + u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG); + sl_cfg |= I2C_SL_CNFG_NACK | I2C_SL_CNFG_NEWSL; + i2c_writel(i2c_dev, sl_cfg, I2C_SL_CNFG); + i2c_writel(i2c_dev, 0xfc, I2C_SL_ADDR1); + i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2); + + } + + val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | + 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; + i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); + + if (tegra_i2c_flush_fifos(i2c_dev)) + err = -ETIMEDOUT; + + clk_disable(i2c_dev->clk); + + if (i2c_dev->irq_disabled) { + i2c_dev->irq_disabled = 0; + enable_irq(i2c_dev->irq); + } + + return err; +} + +static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) +{ + u32 status; + const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; + struct tegra_i2c_dev *i2c_dev = dev_id; + + status = i2c_readl(i2c_dev, I2C_INT_STATUS); + + if (status == 0) { + dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n", + i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS), + i2c_readl(i2c_dev, I2C_STATUS), + i2c_readl(i2c_dev, I2C_CNFG)); + i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT; + + if (!i2c_dev->irq_disabled) { + disable_irq_nosync(i2c_dev->irq); + i2c_dev->irq_disabled = 1; + } + + complete(&i2c_dev->msg_complete); + goto err; + } + + if (unlikely(status & status_err)) { + if (status & I2C_INT_NO_ACK) + i2c_dev->msg_err |= I2C_ERR_NO_ACK; + if (status & I2C_INT_ARBITRATION_LOST) + i2c_dev->msg_err |= I2C_ERR_ARBITRATION_LOST; + complete(&i2c_dev->msg_complete); + goto err; + } + + if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_empty_rx_fifo(i2c_dev); + else + BUG(); + } + + if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_fill_tx_fifo(i2c_dev); + else + tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ); + } + + if ((status & I2C_INT_PACKET_XFER_COMPLETE) && + !i2c_dev->msg_buf_remaining) + complete(&i2c_dev->msg_complete); + + i2c_writel(i2c_dev, status, I2C_INT_STATUS); + if (i2c_dev->is_dvc) + dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + return IRQ_HANDLED; +err: + /* An error occurred, mask all interrupts */ + tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | + I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | + I2C_INT_RX_FIFO_DATA_REQ); + i2c_writel(i2c_dev, status, I2C_INT_STATUS); + if (i2c_dev->is_dvc) + dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + return IRQ_HANDLED; +} + +static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, + struct i2c_msg *msg, int stop) +{ + u32 packet_header; + u32 int_mask; + int ret; + + tegra_i2c_flush_fifos(i2c_dev); + i2c_writel(i2c_dev, 0xFF, I2C_INT_STATUS); + + if (msg->len == 0) + return -EINVAL; + + i2c_dev->msg_buf = msg->buf; + i2c_dev->msg_buf_remaining = msg->len; + i2c_dev->msg_err = I2C_ERR_NONE; + i2c_dev->msg_read = (msg->flags & I2C_M_RD); + INIT_COMPLETION(i2c_dev->msg_complete); + + packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | + PACKET_HEADER0_PROTOCOL_I2C | + (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) | + (1 << PACKET_HEADER0_PACKET_ID_SHIFT); + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + + packet_header = msg->len - 1; + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + + packet_header = msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT; + packet_header |= I2C_HEADER_IE_ENABLE; + if (!stop) + packet_header |= I2C_HEADER_REPEAT_START; + if (msg->flags & I2C_M_TEN) + packet_header |= I2C_HEADER_10BIT_ADDR; + if (msg->flags & I2C_M_IGNORE_NAK) + packet_header |= I2C_HEADER_CONT_ON_NAK; + if (msg->flags & I2C_M_RD) + packet_header |= I2C_HEADER_READ; + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + + if (!(msg->flags & I2C_M_RD)) + tegra_i2c_fill_tx_fifo(i2c_dev); + + int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; + if (msg->flags & I2C_M_RD) + int_mask |= I2C_INT_RX_FIFO_DATA_REQ; + else if (i2c_dev->msg_buf_remaining) + int_mask |= I2C_INT_TX_FIFO_DATA_REQ; + tegra_i2c_unmask_irq(i2c_dev, int_mask); + dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n", + i2c_readl(i2c_dev, I2C_INT_MASK)); + + ret = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT); + tegra_i2c_mask_irq(i2c_dev, int_mask); + + if (WARN_ON(ret == 0)) { + dev_err(i2c_dev->dev, "i2c transfer timed out\n"); + + tegra_i2c_init(i2c_dev); + return -ETIMEDOUT; + } + + dev_dbg(i2c_dev->dev, "transfer complete: %d %d %d\n", + ret, completion_done(&i2c_dev->msg_complete), i2c_dev->msg_err); + + if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) + return 0; + + tegra_i2c_init(i2c_dev); + if (i2c_dev->msg_err == I2C_ERR_NO_ACK) { + if (msg->flags & I2C_M_IGNORE_NAK) + return 0; + return -EREMOTEIO; + } + + return -EIO; +} + +static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + int i; + int ret = 0; + + if (i2c_dev->is_suspended) + return -EBUSY; + + clk_enable(i2c_dev->clk); + for (i = 0; i < num; i++) { + int stop = (i == (num - 1)) ? 1 : 0; + ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], stop); + if (ret) + break; + } + clk_disable(i2c_dev->clk); + return ret ?: i; +} + +static u32 tegra_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm tegra_i2c_algo = { + .master_xfer = tegra_i2c_xfer, + .functionality = tegra_i2c_func, +}; + +static int tegra_i2c_probe(struct platform_device *pdev) +{ + struct tegra_i2c_dev *i2c_dev; + struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data; + struct resource *res; + struct resource *iomem; + struct clk *clk; + struct clk *i2c_clk; + void *base; + int irq; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no mem resource\n"); + return -EINVAL; + } + iomem = request_mem_region(res->start, resource_size(res), pdev->name); + if (!iomem) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + base = ioremap(iomem->start, resource_size(iomem)); + if (!base) { + dev_err(&pdev->dev, "Cannot ioremap I2C region\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "no irq resource\n"); + ret = -EINVAL; + goto err_iounmap; + } + irq = res->start; + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "missing controller clock"); + ret = PTR_ERR(clk); + goto err_release_region; + } + + i2c_clk = clk_get(&pdev->dev, "i2c"); + if (IS_ERR(i2c_clk)) { + dev_err(&pdev->dev, "missing bus clock"); + ret = PTR_ERR(i2c_clk); + goto err_clk_put; + } + + i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL); + if (!i2c_dev) { + ret = -ENOMEM; + goto err_i2c_clk_put; + } + + i2c_dev->base = base; + i2c_dev->clk = clk; + i2c_dev->i2c_clk = i2c_clk; + i2c_dev->iomem = iomem; + i2c_dev->adapter.algo = &tegra_i2c_algo; + i2c_dev->irq = irq; + i2c_dev->cont_id = pdev->id; + i2c_dev->dev = &pdev->dev; + i2c_dev->bus_clk_rate = pdata ? pdata->bus_clk_rate : 100000; + + if (pdev->id == 3) + i2c_dev->is_dvc = 1; + init_completion(&i2c_dev->msg_complete); + + platform_set_drvdata(pdev, i2c_dev); + + ret = tegra_i2c_init(i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize i2c controller"); + goto err_free; + } + + ret = request_irq(i2c_dev->irq, tegra_i2c_isr, 0, pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); + goto err_free; + } + + clk_enable(i2c_dev->i2c_clk); + + i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); + i2c_dev->adapter.owner = THIS_MODULE; + i2c_dev->adapter.class = I2C_CLASS_HWMON; + strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter", + sizeof(i2c_dev->adapter.name)); + i2c_dev->adapter.algo = &tegra_i2c_algo; + i2c_dev->adapter.dev.parent = &pdev->dev; + i2c_dev->adapter.nr = pdev->id; + + ret = i2c_add_numbered_adapter(&i2c_dev->adapter); + if (ret) { + dev_err(&pdev->dev, "Failed to add I2C adapter\n"); + goto err_free_irq; + } + + return 0; +err_free_irq: + free_irq(i2c_dev->irq, i2c_dev); +err_free: + kfree(i2c_dev); +err_i2c_clk_put: + clk_put(i2c_clk); +err_clk_put: + clk_put(clk); +err_release_region: + release_mem_region(iomem->start, resource_size(iomem)); +err_iounmap: + iounmap(base); + return ret; +} + +static int tegra_i2c_remove(struct platform_device *pdev) +{ + struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + i2c_del_adapter(&i2c_dev->adapter); + free_irq(i2c_dev->irq, i2c_dev); + clk_put(i2c_dev->i2c_clk); + clk_put(i2c_dev->clk); + release_mem_region(i2c_dev->iomem->start, + resource_size(i2c_dev->iomem)); + iounmap(i2c_dev->base); + kfree(i2c_dev); + return 0; +} + +#ifdef CONFIG_PM +static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + i2c_lock_adapter(&i2c_dev->adapter); + i2c_dev->is_suspended = true; + i2c_unlock_adapter(&i2c_dev->adapter); + + return 0; +} + +static int tegra_i2c_resume(struct platform_device *pdev) +{ + struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + int ret; + + i2c_lock_adapter(&i2c_dev->adapter); + + ret = tegra_i2c_init(i2c_dev); + + if (ret) { + i2c_unlock_adapter(&i2c_dev->adapter); + return ret; + } + + i2c_dev->is_suspended = false; + + i2c_unlock_adapter(&i2c_dev->adapter); + + return 0; +} +#endif + +static struct platform_driver tegra_i2c_driver = { + .probe = tegra_i2c_probe, + .remove = tegra_i2c_remove, +#ifdef CONFIG_PM + .suspend = tegra_i2c_suspend, + .resume = tegra_i2c_resume, +#endif + .driver = { + .name = "tegra-i2c", + .owner = THIS_MODULE, + }, +}; + +static int __init tegra_i2c_init_driver(void) +{ + return platform_driver_register(&tegra_i2c_driver); +} + +static void __exit tegra_i2c_exit_driver(void) +{ + platform_driver_unregister(&tegra_i2c_driver); +} + +subsys_initcall(tegra_i2c_init_driver); +module_exit(tegra_i2c_exit_driver); + +MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver"); +MODULE_AUTHOR("Colin Cross"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c new file mode 100644 index 00000000..d03b0400 --- /dev/null +++ b/drivers/i2c/busses/i2c-tiny-usb.c @@ -0,0 +1,284 @@ +/* + * driver for the i2c-tiny-usb adapter - 1.0 + * http://www.harbaum.org/till/i2c_tiny_usb + * + * Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.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, version 2. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> + +/* include interfaces to usb layer */ +#include <linux/usb.h> + +/* include interface to i2c layer */ +#include <linux/i2c.h> + +/* commands via USB, must match command ids in the firmware */ +#define CMD_ECHO 0 +#define CMD_GET_FUNC 1 +#define CMD_SET_DELAY 2 +#define CMD_GET_STATUS 3 + +#define CMD_I2C_IO 4 +#define CMD_I2C_IO_BEGIN (1<<0) +#define CMD_I2C_IO_END (1<<1) + +/* i2c bit delay, default is 10us -> 100kHz max + (in practice, due to additional delays in the i2c bitbanging + code this results in a i2c clock of about 50kHz) */ +static unsigned short delay = 10; +module_param(delay, ushort, 0); +MODULE_PARM_DESC(delay, "bit delay in microseconds " + "(default is 10us for 100kHz max)"); + +static int usb_read(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len); + +static int usb_write(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len); + +/* ----- begin of i2c layer ---------------------------------------------- */ + +#define STATUS_IDLE 0 +#define STATUS_ADDRESS_ACK 1 +#define STATUS_ADDRESS_NAK 2 + +static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) +{ + unsigned char status; + struct i2c_msg *pmsg; + int i; + + dev_dbg(&adapter->dev, "master xfer %d messages:\n", num); + + for (i = 0 ; i < num ; i++) { + int cmd = CMD_I2C_IO; + + if (i == 0) + cmd |= CMD_I2C_IO_BEGIN; + + if (i == num-1) + cmd |= CMD_I2C_IO_END; + + pmsg = &msgs[i]; + + dev_dbg(&adapter->dev, + " %d: %s (flags %d) %d bytes to 0x%02x\n", + i, pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->flags, pmsg->len, pmsg->addr); + + /* and directly send the message */ + if (pmsg->flags & I2C_M_RD) { + /* read data */ + if (usb_read(adapter, cmd, + pmsg->flags, pmsg->addr, + pmsg->buf, pmsg->len) != pmsg->len) { + dev_err(&adapter->dev, + "failure reading data\n"); + return -EREMOTEIO; + } + } else { + /* write data */ + if (usb_write(adapter, cmd, + pmsg->flags, pmsg->addr, + pmsg->buf, pmsg->len) != pmsg->len) { + dev_err(&adapter->dev, + "failure writing data\n"); + return -EREMOTEIO; + } + } + + /* read status */ + if (usb_read(adapter, CMD_GET_STATUS, 0, 0, &status, 1) != 1) { + dev_err(&adapter->dev, "failure reading status\n"); + return -EREMOTEIO; + } + + dev_dbg(&adapter->dev, " status = %d\n", status); + if (status == STATUS_ADDRESS_NAK) + return -EREMOTEIO; + } + + return i; +} + +static u32 usb_func(struct i2c_adapter *adapter) +{ + __le32 func; + + /* get functionality from adapter */ + if (usb_read(adapter, CMD_GET_FUNC, 0, 0, &func, sizeof(func)) != + sizeof(func)) { + dev_err(&adapter->dev, "failure reading functionality\n"); + return 0; + } + + return le32_to_cpu(func); +} + +/* This is the actual algorithm we define */ +static const struct i2c_algorithm usb_algorithm = { + .master_xfer = usb_xfer, + .functionality = usb_func, +}; + +/* ----- end of i2c layer ------------------------------------------------ */ + +/* ----- begin of usb layer ---------------------------------------------- */ + +/* + * Initially the usb i2c interface uses a vid/pid pair donated by + * Future Technology Devices International Ltd., later a pair was + * bought from EZPrototypes + */ +static const struct usb_device_id i2c_tiny_usb_table[] = { + { USB_DEVICE(0x0403, 0xc631) }, /* FTDI */ + { USB_DEVICE(0x1c40, 0x0534) }, /* EZPrototypes */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, i2c_tiny_usb_table); + +/* Structure to hold all of our device specific stuff */ +struct i2c_tiny_usb { + struct usb_device *usb_dev; /* the usb device for this device */ + struct usb_interface *interface; /* the interface for this device */ + struct i2c_adapter adapter; /* i2c related things */ +}; + +static int usb_read(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len) +{ + struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; + + /* do control transfer */ + return usb_control_msg(dev->usb_dev, usb_rcvctrlpipe(dev->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | + USB_DIR_IN, value, index, data, len, 2000); +} + +static int usb_write(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len) +{ + struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; + + /* do control transfer */ + return usb_control_msg(dev->usb_dev, usb_sndctrlpipe(dev->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, data, len, 2000); +} + +static void i2c_tiny_usb_free(struct i2c_tiny_usb *dev) +{ + usb_put_dev(dev->usb_dev); + kfree(dev); +} + +static int i2c_tiny_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct i2c_tiny_usb *dev; + int retval = -ENOMEM; + u16 version; + + dev_dbg(&interface->dev, "probing usb device\n"); + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&interface->dev, "Out of memory\n"); + goto error; + } + + dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + version = le16_to_cpu(dev->usb_dev->descriptor.bcdDevice); + dev_info(&interface->dev, + "version %x.%02x found at bus %03d address %03d\n", + version >> 8, version & 0xff, + dev->usb_dev->bus->busnum, dev->usb_dev->devnum); + + /* setup i2c adapter description */ + dev->adapter.owner = THIS_MODULE; + dev->adapter.class = I2C_CLASS_HWMON; + dev->adapter.algo = &usb_algorithm; + dev->adapter.algo_data = dev; + snprintf(dev->adapter.name, sizeof(dev->adapter.name), + "i2c-tiny-usb at bus %03d device %03d", + dev->usb_dev->bus->busnum, dev->usb_dev->devnum); + + if (usb_write(&dev->adapter, CMD_SET_DELAY, delay, 0, NULL, 0) != 0) { + dev_err(&dev->adapter.dev, + "failure setting delay to %dus\n", delay); + retval = -EIO; + goto error; + } + + dev->adapter.dev.parent = &dev->interface->dev; + + /* and finally attach to i2c layer */ + i2c_add_adapter(&dev->adapter); + + /* inform user about successful attachment to i2c layer */ + dev_info(&dev->adapter.dev, "connected i2c-tiny-usb device\n"); + + return 0; + + error: + if (dev) + i2c_tiny_usb_free(dev); + + return retval; +} + +static void i2c_tiny_usb_disconnect(struct usb_interface *interface) +{ + struct i2c_tiny_usb *dev = usb_get_intfdata(interface); + + i2c_del_adapter(&dev->adapter); + usb_set_intfdata(interface, NULL); + i2c_tiny_usb_free(dev); + + dev_dbg(&interface->dev, "disconnected\n"); +} + +static struct usb_driver i2c_tiny_usb_driver = { + .name = "i2c-tiny-usb", + .probe = i2c_tiny_usb_probe, + .disconnect = i2c_tiny_usb_disconnect, + .id_table = i2c_tiny_usb_table, +}; + +static int __init usb_i2c_tiny_usb_init(void) +{ + /* register this driver with the USB subsystem */ + return usb_register(&i2c_tiny_usb_driver); +} + +static void __exit usb_i2c_tiny_usb_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&i2c_tiny_usb_driver); +} + +module_init(usb_i2c_tiny_usb_init); +module_exit(usb_i2c_tiny_usb_exit); + +/* ----- end of usb layer ------------------------------------------------ */ + +MODULE_AUTHOR("Till Harbaum <Till@Harbaum.org>"); +MODULE_DESCRIPTION("i2c-tiny-usb driver v1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c new file mode 100644 index 00000000..60556012 --- /dev/null +++ b/drivers/i2c/busses/i2c-versatile.c @@ -0,0 +1,160 @@ +/* + * i2c-versatile.c + * + * Copyright (C) 2006 ARM Ltd. + * written by Russell King, Deep Blue Solutions 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. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/io.h> + +#define I2C_CONTROL 0x00 +#define I2C_CONTROLS 0x00 +#define I2C_CONTROLC 0x04 +#define SCL (1 << 0) +#define SDA (1 << 1) + +struct i2c_versatile { + struct i2c_adapter adap; + struct i2c_algo_bit_data algo; + void __iomem *base; +}; + +static void i2c_versatile_setsda(void *data, int state) +{ + struct i2c_versatile *i2c = data; + + writel(SDA, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC)); +} + +static void i2c_versatile_setscl(void *data, int state) +{ + struct i2c_versatile *i2c = data; + + writel(SCL, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC)); +} + +static int i2c_versatile_getsda(void *data) +{ + struct i2c_versatile *i2c = data; + return !!(readl(i2c->base + I2C_CONTROL) & SDA); +} + +static int i2c_versatile_getscl(void *data) +{ + struct i2c_versatile *i2c = data; + return !!(readl(i2c->base + I2C_CONTROL) & SCL); +} + +static struct i2c_algo_bit_data i2c_versatile_algo = { + .setsda = i2c_versatile_setsda, + .setscl = i2c_versatile_setscl, + .getsda = i2c_versatile_getsda, + .getscl = i2c_versatile_getscl, + .udelay = 30, + .timeout = HZ, +}; + +static int i2c_versatile_probe(struct platform_device *dev) +{ + struct i2c_versatile *i2c; + struct resource *r; + int ret; + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!r) { + ret = -EINVAL; + goto err_out; + } + + if (!request_mem_region(r->start, resource_size(r), "versatile-i2c")) { + ret = -EBUSY; + goto err_out; + } + + i2c = kzalloc(sizeof(struct i2c_versatile), GFP_KERNEL); + if (!i2c) { + ret = -ENOMEM; + goto err_release; + } + + i2c->base = ioremap(r->start, resource_size(r)); + if (!i2c->base) { + ret = -ENOMEM; + goto err_free; + } + + writel(SCL | SDA, i2c->base + I2C_CONTROLS); + + i2c->adap.owner = THIS_MODULE; + strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name)); + i2c->adap.algo_data = &i2c->algo; + i2c->adap.dev.parent = &dev->dev; + i2c->algo = i2c_versatile_algo; + i2c->algo.data = i2c; + + if (dev->id >= 0) { + /* static bus numbering */ + i2c->adap.nr = dev->id; + ret = i2c_bit_add_numbered_bus(&i2c->adap); + } else + /* dynamic bus numbering */ + ret = i2c_bit_add_bus(&i2c->adap); + if (ret >= 0) { + platform_set_drvdata(dev, i2c); + return 0; + } + + iounmap(i2c->base); + err_free: + kfree(i2c); + err_release: + release_mem_region(r->start, resource_size(r)); + err_out: + return ret; +} + +static int i2c_versatile_remove(struct platform_device *dev) +{ + struct i2c_versatile *i2c = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + + i2c_del_adapter(&i2c->adap); + return 0; +} + +static struct platform_driver i2c_versatile_driver = { + .probe = i2c_versatile_probe, + .remove = i2c_versatile_remove, + .driver = { + .name = "versatile-i2c", + .owner = THIS_MODULE, + }, +}; + +static int __init i2c_versatile_init(void) +{ + return platform_driver_register(&i2c_versatile_driver); +} + +static void __exit i2c_versatile_exit(void) +{ + platform_driver_unregister(&i2c_versatile_driver); +} + +subsys_initcall(i2c_versatile_init); +module_exit(i2c_versatile_exit); + +MODULE_DESCRIPTION("ARM Versatile I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:versatile-i2c"); diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c new file mode 100644 index 00000000..7799fe5b --- /dev/null +++ b/drivers/i2c/busses/i2c-via.c @@ -0,0 +1,180 @@ +/* + i2c Support for Via Technologies 82C586B South Bridge + + Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi> + + 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/pci.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/io.h> + +/* Power management registers */ +#define PM_CFG_REVID 0x08 /* silicon revision code */ +#define PM_CFG_IOBASE0 0x20 +#define PM_CFG_IOBASE1 0x48 + +#define I2C_DIR (pm_io_base+0x40) +#define I2C_OUT (pm_io_base+0x42) +#define I2C_IN (pm_io_base+0x44) +#define I2C_SCL 0x02 /* clock bit in DIR/OUT/IN register */ +#define I2C_SDA 0x04 + +/* io-region reservation */ +#define IOSPACE 0x06 + +static struct pci_driver vt586b_driver; +static u16 pm_io_base; + +/* + It does not appear from the datasheet that the GPIO pins are + open drain. So a we set a low value by setting the direction to + output and a high value by setting the direction to input and + relying on the required I2C pullup. The data value is initialized + to 0 in via_init() and never changed. +*/ +static void bit_via_setscl(void *data, int state) +{ + outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL, I2C_DIR); +} + +static void bit_via_setsda(void *data, int state) +{ + outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA, I2C_DIR); +} + +static int bit_via_getscl(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SCL)); +} + +static int bit_via_getsda(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SDA)); +} + + +static struct i2c_algo_bit_data bit_data = { + .setsda = bit_via_setsda, + .setscl = bit_via_setscl, + .getsda = bit_via_getsda, + .getscl = bit_via_getscl, + .udelay = 5, + .timeout = HZ +}; + +static struct i2c_adapter vt586b_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .name = "VIA i2c", + .algo_data = &bit_data, +}; + + +static const struct pci_device_id vt586b_ids[] __devinitconst = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3) }, + { 0, } +}; + +MODULE_DEVICE_TABLE (pci, vt586b_ids); + +static int __devinit vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + u16 base; + u8 rev; + int res; + + if (pm_io_base) { + dev_err(&dev->dev, "i2c-via: Will only support one host\n"); + return -ENODEV; + } + + pci_read_config_byte(dev, PM_CFG_REVID, &rev); + + switch (rev) { + case 0x00: + base = PM_CFG_IOBASE0; + break; + case 0x01: + case 0x10: + base = PM_CFG_IOBASE1; + break; + + default: + base = PM_CFG_IOBASE1; + /* later revision */ + } + + pci_read_config_word(dev, base, &pm_io_base); + pm_io_base &= (0xff << 8); + + if (!request_region(I2C_DIR, IOSPACE, vt586b_driver.name)) { + dev_err(&dev->dev, "IO 0x%x-0x%x already in use\n", I2C_DIR, I2C_DIR + IOSPACE); + return -ENODEV; + } + + outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR); + outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT); + + /* set up the sysfs linkage to our parent device */ + vt586b_adapter.dev.parent = &dev->dev; + + res = i2c_bit_add_bus(&vt586b_adapter); + if ( res < 0 ) { + release_region(I2C_DIR, IOSPACE); + pm_io_base = 0; + return res; + } + return 0; +} + +static void __devexit vt586b_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&vt586b_adapter); + release_region(I2C_DIR, IOSPACE); + pm_io_base = 0; +} + + +static struct pci_driver vt586b_driver = { + .name = "vt586b_smbus", + .id_table = vt586b_ids, + .probe = vt586b_probe, + .remove = __devexit_p(vt586b_remove), +}; + +static int __init i2c_vt586b_init(void) +{ + return pci_register_driver(&vt586b_driver); +} + +static void __exit i2c_vt586b_exit(void) +{ + pci_unregister_driver(&vt586b_driver); +} + + +MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>"); +MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge"); +MODULE_LICENSE("GPL"); + +module_init(i2c_vt586b_init); +module_exit(i2c_vt586b_exit); diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c new file mode 100644 index 00000000..58261d47 --- /dev/null +++ b/drivers/i2c/busses/i2c-viapro.c @@ -0,0 +1,508 @@ +/* + Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, + Philip Edelbrock <phil@netroedge.com>, Kyösti Mälkki <kmalkki@cc.hut.fi>, + Mark D. Studebaker <mdsxyz123@yahoo.com> + Copyright (C) 2005 - 2008 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. +*/ + +/* + Supports the following VIA south bridges: + + Chip name PCI ID REV I2C block + VT82C596A 0x3050 no + VT82C596B 0x3051 no + VT82C686A 0x3057 0x30 no + VT82C686B 0x3057 0x40 yes + VT8231 0x8235 no? + VT8233 0x3074 yes + VT8233A 0x3147 yes? + VT8235 0x3177 yes + VT8237R 0x3227 yes + VT8237A 0x3337 yes + VT8237S 0x3372 yes + VT8251 0x3287 yes + CX700 0x8324 yes + VX800/VX820 0x8353 yes + VX855/VX875 0x8409 yes + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/io.h> + +static struct pci_dev *vt596_pdev; + +#define SMBBA1 0x90 +#define SMBBA2 0x80 +#define SMBBA3 0xD0 + +/* SMBus address offsets */ +static unsigned short vt596_smba; +#define SMBHSTSTS (vt596_smba + 0) +#define SMBHSTCNT (vt596_smba + 2) +#define SMBHSTCMD (vt596_smba + 3) +#define SMBHSTADD (vt596_smba + 4) +#define SMBHSTDAT0 (vt596_smba + 5) +#define SMBHSTDAT1 (vt596_smba + 6) +#define SMBBLKDAT (vt596_smba + 7) + +/* PCI Address Constants */ + +/* SMBus data in configuration space can be found in two places, + We try to select the better one */ + +static unsigned short SMBHSTCFG = 0xD2; + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* VT82C596 constants */ +#define VT596_QUICK 0x00 +#define VT596_BYTE 0x04 +#define VT596_BYTE_DATA 0x08 +#define VT596_WORD_DATA 0x0C +#define VT596_PROC_CALL 0x10 +#define VT596_BLOCK_DATA 0x14 +#define VT596_I2C_BLOCK_DATA 0x34 + + +/* If force is set to anything different from 0, we forcibly enable the + VT596. DANGEROUS! */ +static int force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the VT596 at the given address. VERY DANGEROUS! */ +static u16 force_addr; +module_param(force_addr, ushort, 0); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the SMBus at the given address. " + "EXTREMELY DANGEROUS!"); + + +static struct pci_driver vt596_driver; +static struct i2c_adapter vt596_adapter; + +#define FEATURE_I2CBLOCK (1<<0) +static unsigned int vt596_features; + +#ifdef DEBUG +static void vt596_dump_regs(const char *msg, u8 size) +{ + dev_dbg(&vt596_adapter.dev, "%s: STS=%02x CNT=%02x CMD=%02x ADD=%02x " + "DAT=%02x,%02x\n", msg, inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); + + if (size == VT596_BLOCK_DATA + || size == VT596_I2C_BLOCK_DATA) { + int i; + + dev_dbg(&vt596_adapter.dev, "BLK="); + for (i = 0; i < I2C_SMBUS_BLOCK_MAX / 2; i++) + printk("%02x,", inb_p(SMBBLKDAT)); + printk("\n"); + dev_dbg(&vt596_adapter.dev, " "); + for (; i < I2C_SMBUS_BLOCK_MAX - 1; i++) + printk("%02x,", inb_p(SMBBLKDAT)); + printk("%02x\n", inb_p(SMBBLKDAT)); + } +} +#else +static inline void vt596_dump_regs(const char *msg, u8 size) { } +#endif + +/* Return -1 on error, 0 on success */ +static int vt596_transaction(u8 size) +{ + int temp; + int result = 0; + int timeout = 0; + + vt596_dump_regs("Transaction (pre)", size); + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { + dev_dbg(&vt596_adapter.dev, "SMBus busy (0x%02x). " + "Resetting...\n", temp); + + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { + dev_err(&vt596_adapter.dev, "SMBus reset failed! " + "(0x%02x)\n", temp); + return -EBUSY; + } + } + + /* Start the transaction by setting bit 6 */ + outb_p(0x40 | size, SMBHSTCNT); + + /* We will always wait for a fraction of a second */ + do { + msleep(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (++timeout < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout == MAX_TIMEOUT) { + result = -ETIMEDOUT; + dev_err(&vt596_adapter.dev, "SMBus timeout!\n"); + } + + if (temp & 0x10) { + result = -EIO; + dev_err(&vt596_adapter.dev, "Transaction failed (0x%02x)\n", + size); + } + + if (temp & 0x08) { + result = -EIO; + dev_err(&vt596_adapter.dev, "SMBus collision!\n"); + } + + if (temp & 0x04) { + result = -ENXIO; + dev_dbg(&vt596_adapter.dev, "No response\n"); + } + + /* Resetting status register */ + if (temp & 0x1F) + outb_p(temp, SMBHSTSTS); + + vt596_dump_regs("Transaction (post)", size); + + return result; +} + +/* Return negative errno on error, 0 on success */ +static s32 vt596_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int i; + int status; + + switch (size) { + case I2C_SMBUS_QUICK: + size = VT596_QUICK; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = VT596_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = VT596_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = VT596_WORD_DATA; + break; + case I2C_SMBUS_PROC_CALL: + outb_p(command, SMBHSTCMD); + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + size = VT596_PROC_CALL; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (!(vt596_features & FEATURE_I2CBLOCK)) + goto exit_unsupported; + if (read_write == I2C_SMBUS_READ) + outb_p(data->block[0], SMBHSTDAT0); + /* Fall through */ + case I2C_SMBUS_BLOCK_DATA: + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + u8 len = data->block[0]; + if (len > I2C_SMBUS_BLOCK_MAX) + len = I2C_SMBUS_BLOCK_MAX; + outb_p(len, SMBHSTDAT0); + inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = (size == I2C_SMBUS_I2C_BLOCK_DATA) ? + VT596_I2C_BLOCK_DATA : VT596_BLOCK_DATA; + break; + default: + goto exit_unsupported; + } + + outb_p(((addr & 0x7f) << 1) | read_write, SMBHSTADD); + + status = vt596_transaction(size); + if (status) + return status; + + if (size == VT596_PROC_CALL) + read_write = I2C_SMBUS_READ; + + if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK)) + return 0; + + switch (size) { + case VT596_BYTE: + case VT596_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case VT596_WORD_DATA: + case VT596_PROC_CALL: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case VT596_I2C_BLOCK_DATA: + case VT596_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) + data->block[0] = I2C_SMBUS_BLOCK_MAX; + inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; + +exit_unsupported: + dev_warn(&vt596_adapter.dev, "Unsupported transaction %d\n", + size); + return -EOPNOTSUPP; +} + +static u32 vt596_func(struct i2c_adapter *adapter) +{ + u32 func = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_DATA; + + if (vt596_features & FEATURE_I2CBLOCK) + func |= I2C_FUNC_SMBUS_I2C_BLOCK; + return func; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = vt596_access, + .functionality = vt596_func, +}; + +static struct i2c_adapter vt596_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, +}; + +static int __devinit vt596_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned char temp; + int error; + + /* Determine the address of the SMBus areas */ + if (force_addr) { + vt596_smba = force_addr & 0xfff0; + force = 0; + goto found; + } + + if ((pci_read_config_word(pdev, id->driver_data, &vt596_smba)) || + !(vt596_smba & 0x0001)) { + /* try 2nd address and config reg. for 596 */ + if (id->device == PCI_DEVICE_ID_VIA_82C596_3 && + !pci_read_config_word(pdev, SMBBA2, &vt596_smba) && + (vt596_smba & 0x0001)) { + SMBHSTCFG = 0x84; + } else { + /* no matches at all */ + dev_err(&pdev->dev, "Cannot configure " + "SMBus I/O Base address\n"); + return -ENODEV; + } + } + + vt596_smba &= 0xfff0; + if (vt596_smba == 0) { + dev_err(&pdev->dev, "SMBus base address " + "uninitialized - upgrade BIOS or use " + "force_addr=0xaddr\n"); + return -ENODEV; + } + +found: + error = acpi_check_region(vt596_smba, 8, vt596_driver.name); + if (error) + return -ENODEV; + + if (!request_region(vt596_smba, 8, vt596_driver.name)) { + dev_err(&pdev->dev, "SMBus region 0x%x already in use!\n", + vt596_smba); + return -ENODEV; + } + + pci_read_config_byte(pdev, SMBHSTCFG, &temp); + /* If force_addr is set, we program the new address here. Just to make + sure, we disable the VT596 first. */ + if (force_addr) { + pci_write_config_byte(pdev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(pdev, id->driver_data, vt596_smba); + pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01); + dev_warn(&pdev->dev, "WARNING: SMBus interface set to new " + "address 0x%04x!\n", vt596_smba); + } else if (!(temp & 0x01)) { + if (force) { + /* NOTE: This assumes I/O space and other allocations + * WERE done by the Bios! Don't complain if your + * hardware does weird things after enabling this. + * :') Check for Bios updates before resorting to + * this. + */ + pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01); + dev_info(&pdev->dev, "Enabling SMBus device\n"); + } else { + dev_err(&pdev->dev, "SMBUS: Error: Host SMBus " + "controller not enabled! - upgrade BIOS or " + "use force=1\n"); + error = -ENODEV; + goto release_region; + } + } + + dev_dbg(&pdev->dev, "VT596_smba = 0x%X\n", vt596_smba); + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CX700: + case PCI_DEVICE_ID_VIA_VX800: + case PCI_DEVICE_ID_VIA_VX855: + case PCI_DEVICE_ID_VIA_8251: + case PCI_DEVICE_ID_VIA_8237: + case PCI_DEVICE_ID_VIA_8237A: + case PCI_DEVICE_ID_VIA_8237S: + case PCI_DEVICE_ID_VIA_8235: + case PCI_DEVICE_ID_VIA_8233A: + case PCI_DEVICE_ID_VIA_8233_0: + vt596_features |= FEATURE_I2CBLOCK; + break; + case PCI_DEVICE_ID_VIA_82C686_4: + /* The VT82C686B (rev 0x40) does support I2C block + transactions, but the VT82C686A (rev 0x30) doesn't */ + if (pdev->revision >= 0x40) + vt596_features |= FEATURE_I2CBLOCK; + break; + } + + vt596_adapter.dev.parent = &pdev->dev; + snprintf(vt596_adapter.name, sizeof(vt596_adapter.name), + "SMBus Via Pro adapter at %04x", vt596_smba); + + vt596_pdev = pci_dev_get(pdev); + error = i2c_add_adapter(&vt596_adapter); + if (error) { + pci_dev_put(vt596_pdev); + vt596_pdev = NULL; + goto release_region; + } + + /* Always return failure here. This is to allow other drivers to bind + * to this pci device. We don't really want to have control over the + * pci device, we only wanted to read as few register values from it. + */ + return -ENODEV; + +release_region: + release_region(vt596_smba, 8); + return error; +} + +static const struct pci_device_id vt596_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596_3), + .driver_data = SMBBA1 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596B_3), + .driver_data = SMBBA1 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4), + .driver_data = SMBBA1 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_0), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233A), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237A), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237S), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4), + .driver_data = SMBBA1 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8251), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800), + .driver_data = SMBBA3 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855), + .driver_data = SMBBA3 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, vt596_ids); + +static struct pci_driver vt596_driver = { + .name = "vt596_smbus", + .id_table = vt596_ids, + .probe = vt596_probe, +}; + +static int __init i2c_vt596_init(void) +{ + return pci_register_driver(&vt596_driver); +} + + +static void __exit i2c_vt596_exit(void) +{ + pci_unregister_driver(&vt596_driver); + if (vt596_pdev != NULL) { + i2c_del_adapter(&vt596_adapter); + release_region(vt596_smba, 8); + pci_dev_put(vt596_pdev); + vt596_pdev = NULL; + } +} + +MODULE_AUTHOR("Kyosti Malkki <kmalkki@cc.hut.fi>, " + "Mark D. Studebaker <mdsxyz123@yahoo.com> and " + "Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("vt82c596 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_vt596_init); +module_exit(i2c_vt596_exit); diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c new file mode 100644 index 00000000..4bb68f35 --- /dev/null +++ b/drivers/i2c/busses/i2c-xiic.c @@ -0,0 +1,826 @@ +/* + * i2c-xiic.c + * Copyright (c) 2002-2007 Xilinx Inc. + * Copyright (c) 2009-2010 Intel Corporation + * + * 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. + * + * 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. + * + * + * This code was implemented by Mocean Laboratories AB when porting linux + * to the automotive development board Russellville. The copyright holder + * as seen in the header is Intel corporation. + * Mocean Laboratories forked off the GNU/Linux platform work into a + * separate company called Pelagicore AB, which committed the code to the + * kernel. + */ + +/* Supports: + * Xilinx IIC + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/i2c-xiic.h> +#include <linux/io.h> +#include <linux/slab.h> + +#define DRIVER_NAME "xiic-i2c" + +enum xilinx_i2c_state { + STATE_DONE, + STATE_ERROR, + STATE_START +}; + +/** + * struct xiic_i2c - Internal representation of the XIIC I2C bus + * @base: Memory base of the HW registers + * @wait: Wait queue for callers + * @adap: Kernel adapter representation + * @tx_msg: Messages from above to be sent + * @lock: Mutual exclusion + * @tx_pos: Current pos in TX message + * @nmsgs: Number of messages in tx_msg + * @state: See STATE_ + * @rx_msg: Current RX message + * @rx_pos: Position within current RX message + */ +struct xiic_i2c { + void __iomem *base; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_msg *tx_msg; + spinlock_t lock; + unsigned int tx_pos; + unsigned int nmsgs; + enum xilinx_i2c_state state; + struct i2c_msg *rx_msg; + int rx_pos; +}; + + +#define XIIC_MSB_OFFSET 0 +#define XIIC_REG_OFFSET (0x100+XIIC_MSB_OFFSET) + +/* + * Register offsets in bytes from RegisterBase. Three is added to the + * base offset to access LSB (IBM style) of the word + */ +#define XIIC_CR_REG_OFFSET (0x00+XIIC_REG_OFFSET) /* Control Register */ +#define XIIC_SR_REG_OFFSET (0x04+XIIC_REG_OFFSET) /* Status Register */ +#define XIIC_DTR_REG_OFFSET (0x08+XIIC_REG_OFFSET) /* Data Tx Register */ +#define XIIC_DRR_REG_OFFSET (0x0C+XIIC_REG_OFFSET) /* Data Rx Register */ +#define XIIC_ADR_REG_OFFSET (0x10+XIIC_REG_OFFSET) /* Address Register */ +#define XIIC_TFO_REG_OFFSET (0x14+XIIC_REG_OFFSET) /* Tx FIFO Occupancy */ +#define XIIC_RFO_REG_OFFSET (0x18+XIIC_REG_OFFSET) /* Rx FIFO Occupancy */ +#define XIIC_TBA_REG_OFFSET (0x1C+XIIC_REG_OFFSET) /* 10 Bit Address reg */ +#define XIIC_RFD_REG_OFFSET (0x20+XIIC_REG_OFFSET) /* Rx FIFO Depth reg */ +#define XIIC_GPO_REG_OFFSET (0x24+XIIC_REG_OFFSET) /* Output Register */ + +/* Control Register masks */ +#define XIIC_CR_ENABLE_DEVICE_MASK 0x01 /* Device enable = 1 */ +#define XIIC_CR_TX_FIFO_RESET_MASK 0x02 /* Transmit FIFO reset=1 */ +#define XIIC_CR_MSMS_MASK 0x04 /* Master starts Txing=1 */ +#define XIIC_CR_DIR_IS_TX_MASK 0x08 /* Dir of tx. Txing=1 */ +#define XIIC_CR_NO_ACK_MASK 0x10 /* Tx Ack. NO ack = 1 */ +#define XIIC_CR_REPEATED_START_MASK 0x20 /* Repeated start = 1 */ +#define XIIC_CR_GENERAL_CALL_MASK 0x40 /* Gen Call enabled = 1 */ + +/* Status Register masks */ +#define XIIC_SR_GEN_CALL_MASK 0x01 /* 1=a mstr issued a GC */ +#define XIIC_SR_ADDR_AS_SLAVE_MASK 0x02 /* 1=when addr as slave */ +#define XIIC_SR_BUS_BUSY_MASK 0x04 /* 1 = bus is busy */ +#define XIIC_SR_MSTR_RDING_SLAVE_MASK 0x08 /* 1=Dir: mstr <-- slave */ +#define XIIC_SR_TX_FIFO_FULL_MASK 0x10 /* 1 = Tx FIFO full */ +#define XIIC_SR_RX_FIFO_FULL_MASK 0x20 /* 1 = Rx FIFO full */ +#define XIIC_SR_RX_FIFO_EMPTY_MASK 0x40 /* 1 = Rx FIFO empty */ +#define XIIC_SR_TX_FIFO_EMPTY_MASK 0x80 /* 1 = Tx FIFO empty */ + +/* Interrupt Status Register masks Interrupt occurs when... */ +#define XIIC_INTR_ARB_LOST_MASK 0x01 /* 1 = arbitration lost */ +#define XIIC_INTR_TX_ERROR_MASK 0x02 /* 1=Tx error/msg complete */ +#define XIIC_INTR_TX_EMPTY_MASK 0x04 /* 1 = Tx FIFO/reg empty */ +#define XIIC_INTR_RX_FULL_MASK 0x08 /* 1=Rx FIFO/reg=OCY level */ +#define XIIC_INTR_BNB_MASK 0x10 /* 1 = Bus not busy */ +#define XIIC_INTR_AAS_MASK 0x20 /* 1 = when addr as slave */ +#define XIIC_INTR_NAAS_MASK 0x40 /* 1 = not addr as slave */ +#define XIIC_INTR_TX_HALF_MASK 0x80 /* 1 = TX FIFO half empty */ + +/* The following constants specify the depth of the FIFOs */ +#define IIC_RX_FIFO_DEPTH 16 /* Rx fifo capacity */ +#define IIC_TX_FIFO_DEPTH 16 /* Tx fifo capacity */ + +/* The following constants specify groups of interrupts that are typically + * enabled or disables at the same time + */ +#define XIIC_TX_INTERRUPTS \ +(XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK) + +#define XIIC_TX_RX_INTERRUPTS (XIIC_INTR_RX_FULL_MASK | XIIC_TX_INTERRUPTS) + +/* The following constants are used with the following macros to specify the + * operation, a read or write operation. + */ +#define XIIC_READ_OPERATION 1 +#define XIIC_WRITE_OPERATION 0 + +/* + * Tx Fifo upper bit masks. + */ +#define XIIC_TX_DYN_START_MASK 0x0100 /* 1 = Set dynamic start */ +#define XIIC_TX_DYN_STOP_MASK 0x0200 /* 1 = Set dynamic stop */ + +/* + * The following constants define the register offsets for the Interrupt + * registers. There are some holes in the memory map for reserved addresses + * to allow other registers to be added and still match the memory map of the + * interrupt controller registers + */ +#define XIIC_DGIER_OFFSET 0x1C /* Device Global Interrupt Enable Register */ +#define XIIC_IISR_OFFSET 0x20 /* Interrupt Status Register */ +#define XIIC_IIER_OFFSET 0x28 /* Interrupt Enable Register */ +#define XIIC_RESETR_OFFSET 0x40 /* Reset Register */ + +#define XIIC_RESET_MASK 0xAUL + +/* + * The following constant is used for the device global interrupt enable + * register, to enable all interrupts for the device, this is the only bit + * in the register + */ +#define XIIC_GINTR_ENABLE_MASK 0x80000000UL + +#define xiic_tx_space(i2c) ((i2c)->tx_msg->len - (i2c)->tx_pos) +#define xiic_rx_space(i2c) ((i2c)->rx_msg->len - (i2c)->rx_pos) + +static void xiic_start_xfer(struct xiic_i2c *i2c); +static void __xiic_start_xfer(struct xiic_i2c *i2c); + +static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value) +{ + iowrite8(value, i2c->base + reg); +} + +static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg) +{ + return ioread8(i2c->base + reg); +} + +static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value) +{ + iowrite16(value, i2c->base + reg); +} + +static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value) +{ + iowrite32(value, i2c->base + reg); +} + +static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg) +{ + return ioread32(i2c->base + reg); +} + +static inline void xiic_irq_dis(struct xiic_i2c *i2c, u32 mask) +{ + u32 ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET); + xiic_setreg32(i2c, XIIC_IIER_OFFSET, ier & ~mask); +} + +static inline void xiic_irq_en(struct xiic_i2c *i2c, u32 mask) +{ + u32 ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET); + xiic_setreg32(i2c, XIIC_IIER_OFFSET, ier | mask); +} + +static inline void xiic_irq_clr(struct xiic_i2c *i2c, u32 mask) +{ + u32 isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + xiic_setreg32(i2c, XIIC_IISR_OFFSET, isr & mask); +} + +static inline void xiic_irq_clr_en(struct xiic_i2c *i2c, u32 mask) +{ + xiic_irq_clr(i2c, mask); + xiic_irq_en(i2c, mask); +} + +static void xiic_clear_rx_fifo(struct xiic_i2c *i2c) +{ + u8 sr; + for (sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET); + !(sr & XIIC_SR_RX_FIFO_EMPTY_MASK); + sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)) + xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET); +} + +static void xiic_reinit(struct xiic_i2c *i2c) +{ + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + + /* Set receive Fifo depth to maximum (zero based). */ + xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, IIC_RX_FIFO_DEPTH - 1); + + /* Reset Tx Fifo. */ + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); + + /* Enable IIC Device, remove Tx Fifo reset & disable general call. */ + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_ENABLE_DEVICE_MASK); + + /* make sure RX fifo is empty */ + xiic_clear_rx_fifo(i2c); + + /* Enable interrupts */ + xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK); + + xiic_irq_clr_en(i2c, XIIC_INTR_AAS_MASK | XIIC_INTR_ARB_LOST_MASK); +} + +static void xiic_deinit(struct xiic_i2c *i2c) +{ + u8 cr; + + xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK); + + /* Disable IIC Device. */ + cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET); + xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr & ~XIIC_CR_ENABLE_DEVICE_MASK); +} + +static void xiic_read_rx(struct xiic_i2c *i2c) +{ + u8 bytes_in_fifo; + int i; + + bytes_in_fifo = xiic_getreg8(i2c, XIIC_RFO_REG_OFFSET) + 1; + + dev_dbg(i2c->adap.dev.parent, "%s entry, bytes in fifo: %d, msg: %d" + ", SR: 0x%x, CR: 0x%x\n", + __func__, bytes_in_fifo, xiic_rx_space(i2c), + xiic_getreg8(i2c, XIIC_SR_REG_OFFSET), + xiic_getreg8(i2c, XIIC_CR_REG_OFFSET)); + + if (bytes_in_fifo > xiic_rx_space(i2c)) + bytes_in_fifo = xiic_rx_space(i2c); + + for (i = 0; i < bytes_in_fifo; i++) + i2c->rx_msg->buf[i2c->rx_pos++] = + xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET); + + xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, + (xiic_rx_space(i2c) > IIC_RX_FIFO_DEPTH) ? + IIC_RX_FIFO_DEPTH - 1 : xiic_rx_space(i2c) - 1); +} + +static int xiic_tx_fifo_space(struct xiic_i2c *i2c) +{ + /* return the actual space left in the FIFO */ + return IIC_TX_FIFO_DEPTH - xiic_getreg8(i2c, XIIC_TFO_REG_OFFSET) - 1; +} + +static void xiic_fill_tx_fifo(struct xiic_i2c *i2c) +{ + u8 fifo_space = xiic_tx_fifo_space(i2c); + int len = xiic_tx_space(i2c); + + len = (len > fifo_space) ? fifo_space : len; + + dev_dbg(i2c->adap.dev.parent, "%s entry, len: %d, fifo space: %d\n", + __func__, len, fifo_space); + + while (len--) { + u16 data = i2c->tx_msg->buf[i2c->tx_pos++]; + if ((xiic_tx_space(i2c) == 0) && (i2c->nmsgs == 1)) { + /* last message in transfer -> STOP */ + data |= XIIC_TX_DYN_STOP_MASK; + dev_dbg(i2c->adap.dev.parent, "%s TX STOP\n", __func__); + + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data); + } else + xiic_setreg8(i2c, XIIC_DTR_REG_OFFSET, data); + } +} + +static void xiic_wakeup(struct xiic_i2c *i2c, int code) +{ + i2c->tx_msg = NULL; + i2c->rx_msg = NULL; + i2c->nmsgs = 0; + i2c->state = code; + wake_up(&i2c->wait); +} + +static void xiic_process(struct xiic_i2c *i2c) +{ + u32 pend, isr, ier; + u32 clr = 0; + + /* Get the interrupt Status from the IPIF. There is no clearing of + * interrupts in the IPIF. Interrupts must be cleared at the source. + * To find which interrupts are pending; AND interrupts pending with + * interrupts masked. + */ + isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET); + ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET); + pend = isr & ier; + + dev_dbg(i2c->adap.dev.parent, "%s entry, IER: 0x%x, ISR: 0x%x, " + "pend: 0x%x, SR: 0x%x, msg: %p, nmsgs: %d\n", + __func__, ier, isr, pend, xiic_getreg8(i2c, XIIC_SR_REG_OFFSET), + i2c->tx_msg, i2c->nmsgs); + + /* Do not processes a devices interrupts if the device has no + * interrupts pending + */ + if (!pend) + return; + + /* Service requesting interrupt */ + if ((pend & XIIC_INTR_ARB_LOST_MASK) || + ((pend & XIIC_INTR_TX_ERROR_MASK) && + !(pend & XIIC_INTR_RX_FULL_MASK))) { + /* bus arbritration lost, or... + * Transmit error _OR_ RX completed + * if this happens when RX_FULL is not set + * this is probably a TX error + */ + + dev_dbg(i2c->adap.dev.parent, "%s error\n", __func__); + + /* dynamic mode seem to suffer from problems if we just flushes + * fifos and the next message is a TX with len 0 (only addr) + * reset the IP instead of just flush fifos + */ + xiic_reinit(i2c); + + if (i2c->tx_msg) + xiic_wakeup(i2c, STATE_ERROR); + + } else if (pend & XIIC_INTR_RX_FULL_MASK) { + /* Receive register/FIFO is full */ + + clr = XIIC_INTR_RX_FULL_MASK; + if (!i2c->rx_msg) { + dev_dbg(i2c->adap.dev.parent, + "%s unexpexted RX IRQ\n", __func__); + xiic_clear_rx_fifo(i2c); + goto out; + } + + xiic_read_rx(i2c); + if (xiic_rx_space(i2c) == 0) { + /* this is the last part of the message */ + i2c->rx_msg = NULL; + + /* also clear TX error if there (RX complete) */ + clr |= (isr & XIIC_INTR_TX_ERROR_MASK); + + dev_dbg(i2c->adap.dev.parent, + "%s end of message, nmsgs: %d\n", + __func__, i2c->nmsgs); + + /* send next message if this wasn't the last, + * otherwise the transfer will be finialise when + * receiving the bus not busy interrupt + */ + if (i2c->nmsgs > 1) { + i2c->nmsgs--; + i2c->tx_msg++; + dev_dbg(i2c->adap.dev.parent, + "%s will start next...\n", __func__); + + __xiic_start_xfer(i2c); + } + } + } else if (pend & XIIC_INTR_BNB_MASK) { + /* IIC bus has transitioned to not busy */ + clr = XIIC_INTR_BNB_MASK; + + /* The bus is not busy, disable BusNotBusy interrupt */ + xiic_irq_dis(i2c, XIIC_INTR_BNB_MASK); + + if (!i2c->tx_msg) + goto out; + + if ((i2c->nmsgs == 1) && !i2c->rx_msg && + xiic_tx_space(i2c) == 0) + xiic_wakeup(i2c, STATE_DONE); + else + xiic_wakeup(i2c, STATE_ERROR); + + } else if (pend & (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK)) { + /* Transmit register/FIFO is empty or ½ empty */ + + clr = pend & + (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK); + + if (!i2c->tx_msg) { + dev_dbg(i2c->adap.dev.parent, + "%s unexpexted TX IRQ\n", __func__); + goto out; + } + + xiic_fill_tx_fifo(i2c); + + /* current message sent and there is space in the fifo */ + if (!xiic_tx_space(i2c) && xiic_tx_fifo_space(i2c) >= 2) { + dev_dbg(i2c->adap.dev.parent, + "%s end of message sent, nmsgs: %d\n", + __func__, i2c->nmsgs); + if (i2c->nmsgs > 1) { + i2c->nmsgs--; + i2c->tx_msg++; + __xiic_start_xfer(i2c); + } else { + xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK); + + dev_dbg(i2c->adap.dev.parent, + "%s Got TX IRQ but no more to do...\n", + __func__); + } + } else if (!xiic_tx_space(i2c) && (i2c->nmsgs == 1)) + /* current frame is sent and is last, + * make sure to disable tx half + */ + xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK); + } else { + /* got IRQ which is not acked */ + dev_err(i2c->adap.dev.parent, "%s Got unexpected IRQ\n", + __func__); + clr = pend; + } +out: + dev_dbg(i2c->adap.dev.parent, "%s clr: 0x%x\n", __func__, clr); + + xiic_setreg32(i2c, XIIC_IISR_OFFSET, clr); +} + +static int xiic_bus_busy(struct xiic_i2c *i2c) +{ + u8 sr = xiic_getreg8(i2c, XIIC_SR_REG_OFFSET); + + return (sr & XIIC_SR_BUS_BUSY_MASK) ? -EBUSY : 0; +} + +static int xiic_busy(struct xiic_i2c *i2c) +{ + int tries = 3; + int err; + + if (i2c->tx_msg) + return -EBUSY; + + /* for instance if previous transfer was terminated due to TX error + * it might be that the bus is on it's way to become available + * give it at most 3 ms to wake + */ + err = xiic_bus_busy(i2c); + while (err && tries--) { + mdelay(1); + err = xiic_bus_busy(i2c); + } + + return err; +} + +static void xiic_start_recv(struct xiic_i2c *i2c) +{ + u8 rx_watermark; + struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg; + + /* Clear and enable Rx full interrupt. */ + xiic_irq_clr_en(i2c, XIIC_INTR_RX_FULL_MASK | XIIC_INTR_TX_ERROR_MASK); + + /* we want to get all but last byte, because the TX_ERROR IRQ is used + * to inidicate error ACK on the address, and negative ack on the last + * received byte, so to not mix them receive all but last. + * In the case where there is only one byte to receive + * we can check if ERROR and RX full is set at the same time + */ + rx_watermark = msg->len; + if (rx_watermark > IIC_RX_FIFO_DEPTH) + rx_watermark = IIC_RX_FIFO_DEPTH; + xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rx_watermark - 1); + + if (!(msg->flags & I2C_M_NOSTART)) + /* write the address */ + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, + (msg->addr << 1) | XIIC_READ_OPERATION | + XIIC_TX_DYN_START_MASK); + + xiic_irq_clr_en(i2c, XIIC_INTR_BNB_MASK); + + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, + msg->len | ((i2c->nmsgs == 1) ? XIIC_TX_DYN_STOP_MASK : 0)); + if (i2c->nmsgs == 1) + /* very last, enable bus not busy as well */ + xiic_irq_clr_en(i2c, XIIC_INTR_BNB_MASK); + + /* the message is tx:ed */ + i2c->tx_pos = msg->len; +} + +static void xiic_start_send(struct xiic_i2c *i2c) +{ + struct i2c_msg *msg = i2c->tx_msg; + + xiic_irq_clr(i2c, XIIC_INTR_TX_ERROR_MASK); + + dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, len: %d, " + "ISR: 0x%x, CR: 0x%x\n", + __func__, msg, msg->len, xiic_getreg32(i2c, XIIC_IISR_OFFSET), + xiic_getreg8(i2c, XIIC_CR_REG_OFFSET)); + + if (!(msg->flags & I2C_M_NOSTART)) { + /* write the address */ + u16 data = ((msg->addr << 1) & 0xfe) | XIIC_WRITE_OPERATION | + XIIC_TX_DYN_START_MASK; + if ((i2c->nmsgs == 1) && msg->len == 0) + /* no data and last message -> add STOP */ + data |= XIIC_TX_DYN_STOP_MASK; + + xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data); + } + + xiic_fill_tx_fifo(i2c); + + /* Clear any pending Tx empty, Tx Error and then enable them. */ + xiic_irq_clr_en(i2c, XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_ERROR_MASK | + XIIC_INTR_BNB_MASK); +} + +static irqreturn_t xiic_isr(int irq, void *dev_id) +{ + struct xiic_i2c *i2c = dev_id; + + spin_lock(&i2c->lock); + /* disable interrupts globally */ + xiic_setreg32(i2c, XIIC_DGIER_OFFSET, 0); + + dev_dbg(i2c->adap.dev.parent, "%s entry\n", __func__); + + xiic_process(i2c); + + xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK); + spin_unlock(&i2c->lock); + + return IRQ_HANDLED; +} + +static void __xiic_start_xfer(struct xiic_i2c *i2c) +{ + int first = 1; + int fifo_space = xiic_tx_fifo_space(i2c); + dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, fifos space: %d\n", + __func__, i2c->tx_msg, fifo_space); + + if (!i2c->tx_msg) + return; + + i2c->rx_pos = 0; + i2c->tx_pos = 0; + i2c->state = STATE_START; + while ((fifo_space >= 2) && (first || (i2c->nmsgs > 1))) { + if (!first) { + i2c->nmsgs--; + i2c->tx_msg++; + i2c->tx_pos = 0; + } else + first = 0; + + if (i2c->tx_msg->flags & I2C_M_RD) { + /* we dont date putting several reads in the FIFO */ + xiic_start_recv(i2c); + return; + } else { + xiic_start_send(i2c); + if (xiic_tx_space(i2c) != 0) { + /* the message could not be completely sent */ + break; + } + } + + fifo_space = xiic_tx_fifo_space(i2c); + } + + /* there are more messages or the current one could not be completely + * put into the FIFO, also enable the half empty interrupt + */ + if (i2c->nmsgs > 1 || xiic_tx_space(i2c)) + xiic_irq_clr_en(i2c, XIIC_INTR_TX_HALF_MASK); + +} + +static void xiic_start_xfer(struct xiic_i2c *i2c) +{ + unsigned long flags; + + spin_lock_irqsave(&i2c->lock, flags); + xiic_reinit(i2c); + /* disable interrupts globally */ + xiic_setreg32(i2c, XIIC_DGIER_OFFSET, 0); + spin_unlock_irqrestore(&i2c->lock, flags); + + __xiic_start_xfer(i2c); + xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK); +} + +static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct xiic_i2c *i2c = i2c_get_adapdata(adap); + int err; + + dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__, + xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)); + + err = xiic_busy(i2c); + if (err) + return err; + + i2c->tx_msg = msgs; + i2c->nmsgs = num; + + xiic_start_xfer(i2c); + + if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ)) + return (i2c->state == STATE_DONE) ? num : -EIO; + else { + i2c->tx_msg = NULL; + i2c->rx_msg = NULL; + i2c->nmsgs = 0; + return -ETIMEDOUT; + } +} + +static u32 xiic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm xiic_algorithm = { + .master_xfer = xiic_xfer, + .functionality = xiic_func, +}; + +static struct i2c_adapter xiic_adapter = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &xiic_algorithm, +}; + + +static int __devinit xiic_i2c_probe(struct platform_device *pdev) +{ + struct xiic_i2c *i2c; + struct xiic_i2c_platform_data *pdata; + struct resource *res; + int ret, irq; + u8 i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + goto resource_missing; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + goto resource_missing; + + pdata = (struct xiic_i2c_platform_data *) pdev->dev.platform_data; + if (!pdata) + return -EINVAL; + + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Memory region busy\n"); + ret = -EBUSY; + goto request_mem_failed; + } + + i2c->base = ioremap(res->start, resource_size(res)); + if (!i2c->base) { + dev_err(&pdev->dev, "Unable to map registers\n"); + ret = -EIO; + goto map_failed; + } + + /* hook up driver to tree */ + platform_set_drvdata(pdev, i2c); + i2c->adap = xiic_adapter; + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &pdev->dev; + + xiic_reinit(i2c); + + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + ret = request_irq(irq, xiic_isr, 0, pdev->name, i2c); + if (ret) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto request_irq_failed; + } + + /* add i2c adapter to i2c tree */ + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + dev_err(&pdev->dev, "Failed to add adapter\n"); + goto add_adapter_failed; + } + + /* add in known devices to the bus */ + for (i = 0; i < pdata->num_devices; i++) + i2c_new_device(&i2c->adap, pdata->devices + i); + + return 0; + +add_adapter_failed: + free_irq(irq, i2c); +request_irq_failed: + xiic_deinit(i2c); + iounmap(i2c->base); +map_failed: + release_mem_region(res->start, resource_size(res)); +request_mem_failed: + kfree(i2c); + + return ret; +resource_missing: + dev_err(&pdev->dev, "IRQ or Memory resource is missing\n"); + return -ENOENT; +} + +static int __devexit xiic_i2c_remove(struct platform_device* pdev) +{ + struct xiic_i2c *i2c = platform_get_drvdata(pdev); + struct resource *res; + + /* remove adapter & data */ + i2c_del_adapter(&i2c->adap); + + xiic_deinit(i2c); + + platform_set_drvdata(pdev, NULL); + + free_irq(platform_get_irq(pdev, 0), i2c); + + iounmap(i2c->base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, resource_size(res)); + + kfree(i2c); + + return 0; +} + + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:"DRIVER_NAME); + +static struct platform_driver xiic_i2c_driver = { + .probe = xiic_i2c_probe, + .remove = __devexit_p(xiic_i2c_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +static int __init xiic_i2c_init(void) +{ + return platform_driver_register(&xiic_i2c_driver); +} + +static void __exit xiic_i2c_exit(void) +{ + platform_driver_unregister(&xiic_i2c_driver); +} + +module_init(xiic_i2c_init); +module_exit(xiic_i2c_exit); + +MODULE_AUTHOR("info@mocean-labs.com"); +MODULE_DESCRIPTION("Xilinx I2C bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c new file mode 100644 index 00000000..986e5f62 --- /dev/null +++ b/drivers/i2c/busses/scx200_acb.c @@ -0,0 +1,618 @@ +/* + Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> + + National Semiconductor SCx200 ACCESS.bus support + Also supports the AMD CS5535 and AMD CS5536 + + Based on i2c-keywest.c which is: + Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org> + Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.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. + + 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/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/io.h> + +#include <linux/scx200.h> + +#define NAME "scx200_acb" + +MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>"); +MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver"); +MODULE_ALIAS("platform:cs5535-smb"); +MODULE_LICENSE("GPL"); + +#define MAX_DEVICES 4 +static int base[MAX_DEVICES] = { 0x820, 0x840 }; +module_param_array(base, int, NULL, 0); +MODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers"); + +#define POLL_TIMEOUT (HZ/5) + +enum scx200_acb_state { + state_idle, + state_address, + state_command, + state_repeat_start, + state_quick, + state_read, + state_write, +}; + +static const char *scx200_acb_state_name[] = { + "idle", + "address", + "command", + "repeat_start", + "quick", + "read", + "write", +}; + +/* Physical interface */ +struct scx200_acb_iface { + struct scx200_acb_iface *next; + struct i2c_adapter adapter; + unsigned base; + struct mutex mutex; + + /* State machine data */ + enum scx200_acb_state state; + int result; + u8 address_byte; + u8 command; + u8 *ptr; + char needs_reset; + unsigned len; +}; + +/* Register Definitions */ +#define ACBSDA (iface->base + 0) +#define ACBST (iface->base + 1) +#define ACBST_SDAST 0x40 /* SDA Status */ +#define ACBST_BER 0x20 +#define ACBST_NEGACK 0x10 /* Negative Acknowledge */ +#define ACBST_STASTR 0x08 /* Stall After Start */ +#define ACBST_MASTER 0x02 +#define ACBCST (iface->base + 2) +#define ACBCST_BB 0x02 +#define ACBCTL1 (iface->base + 3) +#define ACBCTL1_STASTRE 0x80 +#define ACBCTL1_NMINTE 0x40 +#define ACBCTL1_ACK 0x10 +#define ACBCTL1_STOP 0x02 +#define ACBCTL1_START 0x01 +#define ACBADDR (iface->base + 4) +#define ACBCTL2 (iface->base + 5) +#define ACBCTL2_ENABLE 0x01 + +/************************************************************************/ + +static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) +{ + const char *errmsg; + + dev_dbg(&iface->adapter.dev, "state %s, status = 0x%02x\n", + scx200_acb_state_name[iface->state], status); + + if (status & ACBST_BER) { + errmsg = "bus error"; + goto error; + } + if (!(status & ACBST_MASTER)) { + errmsg = "not master"; + goto error; + } + if (status & ACBST_NEGACK) { + dev_dbg(&iface->adapter.dev, "negative ack in state %s\n", + scx200_acb_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -ENXIO; + + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + outb(ACBST_STASTR | ACBST_NEGACK, ACBST); + + /* Reset the status register */ + outb(0, ACBST); + return; + } + + switch (iface->state) { + case state_idle: + dev_warn(&iface->adapter.dev, "interrupt in idle state\n"); + break; + + case state_address: + /* Do a pointer write first */ + outb(iface->address_byte & ~1, ACBSDA); + + iface->state = state_command; + break; + + case state_command: + outb(iface->command, ACBSDA); + + if (iface->address_byte & 1) + iface->state = state_repeat_start; + else + iface->state = state_write; + break; + + case state_repeat_start: + outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1); + /* fallthrough */ + + case state_quick: + if (iface->address_byte & 1) { + if (iface->len == 1) + outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1); + else + outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1); + outb(iface->address_byte, ACBSDA); + + iface->state = state_read; + } else { + outb(iface->address_byte, ACBSDA); + + iface->state = state_write; + } + break; + + case state_read: + /* Set ACK if _next_ byte will be the last one */ + if (iface->len == 2) + outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1); + else + outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1); + + if (iface->len == 1) { + iface->result = 0; + iface->state = state_idle; + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + } + + *iface->ptr++ = inb(ACBSDA); + --iface->len; + + break; + + case state_write: + if (iface->len == 0) { + iface->result = 0; + iface->state = state_idle; + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + break; + } + + outb(*iface->ptr++, ACBSDA); + --iface->len; + + break; + } + + return; + + error: + dev_err(&iface->adapter.dev, + "%s in state %s (addr=0x%02x, len=%d, status=0x%02x)\n", errmsg, + scx200_acb_state_name[iface->state], iface->address_byte, + iface->len, status); + + iface->state = state_idle; + iface->result = -EIO; + iface->needs_reset = 1; +} + +static void scx200_acb_poll(struct scx200_acb_iface *iface) +{ + u8 status; + unsigned long timeout; + + timeout = jiffies + POLL_TIMEOUT; + while (1) { + status = inb(ACBST); + + /* Reset the status register to avoid the hang */ + outb(0, ACBST); + + if ((status & (ACBST_SDAST|ACBST_BER|ACBST_NEGACK)) != 0) { + scx200_acb_machine(iface, status); + return; + } + if (time_after(jiffies, timeout)) + break; + cpu_relax(); + cond_resched(); + } + + dev_err(&iface->adapter.dev, "timeout in state %s\n", + scx200_acb_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -EIO; + iface->needs_reset = 1; +} + +static void scx200_acb_reset(struct scx200_acb_iface *iface) +{ + /* Disable the ACCESS.bus device and Configure the SCL + frequency: 16 clock cycles */ + outb(0x70, ACBCTL2); + /* Polling mode */ + outb(0, ACBCTL1); + /* Disable slave address */ + outb(0, ACBADDR); + /* Enable the ACCESS.bus device */ + outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2); + /* Free STALL after START */ + outb(inb(ACBCTL1) & ~(ACBCTL1_STASTRE | ACBCTL1_NMINTE), ACBCTL1); + /* Send a STOP */ + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + /* Clear BER, NEGACK and STASTR bits */ + outb(ACBST_BER | ACBST_NEGACK | ACBST_STASTR, ACBST); + /* Clear BB bit */ + outb(inb(ACBCST) | ACBCST_BB, ACBCST); +} + +static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter, + u16 address, unsigned short flags, + char rw, u8 command, int size, + union i2c_smbus_data *data) +{ + struct scx200_acb_iface *iface = i2c_get_adapdata(adapter); + int len; + u8 *buffer; + u16 cur_word; + int rc; + + switch (size) { + case I2C_SMBUS_QUICK: + len = 0; + buffer = NULL; + break; + + case I2C_SMBUS_BYTE: + len = 1; + buffer = rw ? &data->byte : &command; + break; + + case I2C_SMBUS_BYTE_DATA: + len = 1; + buffer = &data->byte; + break; + + case I2C_SMBUS_WORD_DATA: + len = 2; + cur_word = cpu_to_le16(data->word); + buffer = (u8 *)&cur_word; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + buffer = &data->block[1]; + break; + + default: + return -EINVAL; + } + + dev_dbg(&adapter->dev, + "size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n", + size, address, command, len, rw); + + if (!len && rw == I2C_SMBUS_READ) { + dev_dbg(&adapter->dev, "zero length read\n"); + return -EINVAL; + } + + mutex_lock(&iface->mutex); + + iface->address_byte = (address << 1) | rw; + iface->command = command; + iface->ptr = buffer; + iface->len = len; + iface->result = -EINVAL; + iface->needs_reset = 0; + + outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1); + + if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) + iface->state = state_quick; + else + iface->state = state_address; + + while (iface->state != state_idle) + scx200_acb_poll(iface); + + if (iface->needs_reset) + scx200_acb_reset(iface); + + rc = iface->result; + + mutex_unlock(&iface->mutex); + + if (rc == 0 && size == I2C_SMBUS_WORD_DATA && rw == I2C_SMBUS_READ) + data->word = le16_to_cpu(cur_word); + +#ifdef DEBUG + dev_dbg(&adapter->dev, "transfer done, result: %d", rc); + if (buffer) { + int i; + printk(" data:"); + for (i = 0; i < len; ++i) + printk(" %02x", buffer[i]); + } + printk("\n"); +#endif + + return rc; +} + +static u32 scx200_acb_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +/* For now, we only handle combined mode (smbus) */ +static const struct i2c_algorithm scx200_acb_algorithm = { + .smbus_xfer = scx200_acb_smbus_xfer, + .functionality = scx200_acb_func, +}; + +static struct scx200_acb_iface *scx200_acb_list; +static DEFINE_MUTEX(scx200_acb_list_mutex); + +static __devinit int scx200_acb_probe(struct scx200_acb_iface *iface) +{ + u8 val; + + /* Disable the ACCESS.bus device and Configure the SCL + frequency: 16 clock cycles */ + outb(0x70, ACBCTL2); + + if (inb(ACBCTL2) != 0x70) { + pr_debug(NAME ": ACBCTL2 readback failed\n"); + return -ENXIO; + } + + outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1); + + val = inb(ACBCTL1); + if (val) { + pr_debug(NAME ": disabled, but ACBCTL1=0x%02x\n", + val); + return -ENXIO; + } + + outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2); + + outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1); + + val = inb(ACBCTL1); + if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) { + pr_debug(NAME ": enabled, but NMINTE won't be set, " + "ACBCTL1=0x%02x\n", val); + return -ENXIO; + } + + return 0; +} + +static __devinit struct scx200_acb_iface *scx200_create_iface(const char *text, + struct device *dev, int index) +{ + struct scx200_acb_iface *iface; + struct i2c_adapter *adapter; + + iface = kzalloc(sizeof(*iface), GFP_KERNEL); + if (!iface) { + printk(KERN_ERR NAME ": can't allocate memory\n"); + return NULL; + } + + adapter = &iface->adapter; + i2c_set_adapdata(adapter, iface); + snprintf(adapter->name, sizeof(adapter->name), "%s ACB%d", text, index); + adapter->owner = THIS_MODULE; + adapter->algo = &scx200_acb_algorithm; + adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adapter->dev.parent = dev; + + mutex_init(&iface->mutex); + + return iface; +} + +static int __devinit scx200_acb_create(struct scx200_acb_iface *iface) +{ + struct i2c_adapter *adapter; + int rc; + + adapter = &iface->adapter; + + rc = scx200_acb_probe(iface); + if (rc) { + printk(KERN_WARNING NAME ": probe failed\n"); + return rc; + } + + scx200_acb_reset(iface); + + if (i2c_add_adapter(adapter) < 0) { + printk(KERN_ERR NAME ": failed to register\n"); + return -ENODEV; + } + + if (!adapter->dev.parent) { + /* If there's no dev, we're tracking (ISA) ifaces manually */ + mutex_lock(&scx200_acb_list_mutex); + iface->next = scx200_acb_list; + scx200_acb_list = iface; + mutex_unlock(&scx200_acb_list_mutex); + } + + return 0; +} + +static struct scx200_acb_iface * __devinit scx200_create_dev(const char *text, + unsigned long base, int index, struct device *dev) +{ + struct scx200_acb_iface *iface; + int rc; + + iface = scx200_create_iface(text, dev, index); + + if (iface == NULL) + return NULL; + + if (!request_region(base, 8, iface->adapter.name)) { + printk(KERN_ERR NAME ": can't allocate io 0x%lx-0x%lx\n", + base, base + 8 - 1); + goto errout_free; + } + + iface->base = base; + rc = scx200_acb_create(iface); + + if (rc == 0) + return iface; + + release_region(base, 8); + errout_free: + kfree(iface); + return NULL; +} + +static int __devinit scx200_probe(struct platform_device *pdev) +{ + struct scx200_acb_iface *iface; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "can't fetch device resource info\n"); + return -ENODEV; + } + + iface = scx200_create_dev("CS5535", res->start, 0, &pdev->dev); + if (!iface) + return -EIO; + + dev_info(&pdev->dev, "SCx200 device '%s' registered\n", + iface->adapter.name); + platform_set_drvdata(pdev, iface); + + return 0; +} + +static void __devexit scx200_cleanup_iface(struct scx200_acb_iface *iface) +{ + i2c_del_adapter(&iface->adapter); + release_region(iface->base, 8); + kfree(iface); +} + +static int __devexit scx200_remove(struct platform_device *pdev) +{ + struct scx200_acb_iface *iface; + + iface = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + scx200_cleanup_iface(iface); + + return 0; +} + +static struct platform_driver scx200_pci_drv = { + .driver = { + .name = "cs5535-smb", + .owner = THIS_MODULE, + }, + .probe = scx200_probe, + .remove = __devexit_p(scx200_remove), +}; + +static const struct pci_device_id scx200_isa[] __initconst = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) }, + { 0, } +}; + +static __init void scx200_scan_isa(void) +{ + int i; + + if (!pci_dev_present(scx200_isa)) + return; + + for (i = 0; i < MAX_DEVICES; ++i) { + if (base[i] == 0) + continue; + + /* XXX: should we care about failures? */ + scx200_create_dev("SCx200", base[i], i, NULL); + } +} + +static int __init scx200_acb_init(void) +{ + pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n"); + + /* First scan for ISA-based devices */ + scx200_scan_isa(); /* XXX: should we care about errors? */ + + /* If at least one bus was created, init must succeed */ + if (scx200_acb_list) + return 0; + + /* No ISA devices; register the platform driver for PCI-based devices */ + return platform_driver_register(&scx200_pci_drv); +} + +static void __exit scx200_acb_cleanup(void) +{ + struct scx200_acb_iface *iface; + + platform_driver_unregister(&scx200_pci_drv); + + mutex_lock(&scx200_acb_list_mutex); + while ((iface = scx200_acb_list) != NULL) { + scx200_acb_list = iface->next; + mutex_unlock(&scx200_acb_list_mutex); + + scx200_cleanup_iface(iface); + + mutex_lock(&scx200_acb_list_mutex); + } + mutex_unlock(&scx200_acb_list_mutex); +} + +module_init(scx200_acb_init); +module_exit(scx200_acb_cleanup); diff --git a/drivers/i2c/busses/scx200_i2c.c b/drivers/i2c/busses/scx200_i2c.c new file mode 100644 index 00000000..7ee0d502 --- /dev/null +++ b/drivers/i2c/busses/scx200_i2c.c @@ -0,0 +1,131 @@ +/* linux/drivers/i2c/busses/scx200_i2c.c + + Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> + + National Semiconductor SCx200 I2C bus on GPIO pins + + Based on i2c-velleman.c Copyright (C) 1995-96, 2000 Simon G. Vogl + + 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/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/io.h> + +#include <linux/scx200_gpio.h> + +#define NAME "scx200_i2c" + +MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>"); +MODULE_DESCRIPTION("NatSemi SCx200 I2C Driver"); +MODULE_LICENSE("GPL"); + +static int scl = CONFIG_SCx200_I2C_SCL; +static int sda = CONFIG_SCx200_I2C_SDA; + +module_param(scl, int, 0); +MODULE_PARM_DESC(scl, "GPIO line for SCL"); +module_param(sda, int, 0); +MODULE_PARM_DESC(sda, "GPIO line for SDA"); + +static void scx200_i2c_setscl(void *data, int state) +{ + scx200_gpio_set(scl, state); +} + +static void scx200_i2c_setsda(void *data, int state) +{ + scx200_gpio_set(sda, state); +} + +static int scx200_i2c_getscl(void *data) +{ + return scx200_gpio_get(scl); +} + +static int scx200_i2c_getsda(void *data) +{ + return scx200_gpio_get(sda); +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data scx200_i2c_data = { + .setsda = scx200_i2c_setsda, + .setscl = scx200_i2c_setscl, + .getsda = scx200_i2c_getsda, + .getscl = scx200_i2c_getscl, + .udelay = 10, + .timeout = HZ, +}; + +static struct i2c_adapter scx200_i2c_ops = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo_data = &scx200_i2c_data, + .name = "NatSemi SCx200 I2C", +}; + +static int scx200_i2c_init(void) +{ + pr_debug(NAME ": NatSemi SCx200 I2C Driver\n"); + + if (!scx200_gpio_present()) { + printk(KERN_ERR NAME ": no SCx200 gpio pins available\n"); + return -ENODEV; + } + + pr_debug(NAME ": SCL=GPIO%02u, SDA=GPIO%02u\n", scl, sda); + + if (scl == -1 || sda == -1 || scl == sda) { + printk(KERN_ERR NAME ": scl and sda must be specified\n"); + return -EINVAL; + } + + /* Configure GPIOs as open collector outputs */ + scx200_gpio_configure(scl, ~2, 5); + scx200_gpio_configure(sda, ~2, 5); + + if (i2c_bit_add_bus(&scx200_i2c_ops) < 0) { + printk(KERN_ERR NAME ": adapter %s registration failed\n", + scx200_i2c_ops.name); + return -ENODEV; + } + + return 0; +} + +static void scx200_i2c_cleanup(void) +{ + i2c_del_adapter(&scx200_i2c_ops); +} + +module_init(scx200_i2c_init); +module_exit(scx200_i2c_cleanup); + +/* + Local variables: + compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules" + c-basic-offset: 8 + End: +*/ diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c new file mode 100644 index 00000000..3ca2e012 --- /dev/null +++ b/drivers/i2c/i2c-boardinfo.c @@ -0,0 +1,92 @@ +/* + * i2c-boardinfo.c - collect pre-declarations of I2C devices + * + * 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/i2c.h> +#include <linux/slab.h> +#include <linux/rwsem.h> + +#include "i2c-core.h" + + +/* These symbols are exported ONLY FOR the i2c core. + * No other users will be supported. + */ +DECLARE_RWSEM(__i2c_board_lock); +EXPORT_SYMBOL_GPL(__i2c_board_lock); + +LIST_HEAD(__i2c_board_list); +EXPORT_SYMBOL_GPL(__i2c_board_list); + +int __i2c_first_dynamic_bus_num; +EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num); + + +/** + * i2c_register_board_info - statically declare I2C devices + * @busnum: identifies the bus to which these devices belong + * @info: vector of i2c device descriptors + * @len: how many descriptors in the vector; may be zero to reserve + * the specified bus number. + * + * Systems using the Linux I2C driver stack can declare tables of board info + * while they initialize. This should be done in board-specific init code + * near arch_initcall() time, or equivalent, before any I2C adapter driver is + * registered. For example, mainboard init code could define several devices, + * as could the init code for each daughtercard in a board stack. + * + * The I2C devices will be created later, after the adapter for the relevant + * bus has been registered. After that moment, standard driver model tools + * are used to bind "new style" I2C drivers to the devices. The bus number + * for any device declared using this routine is not available for dynamic + * allocation. + * + * The board info passed can safely be __initdata, but be careful of embedded + * pointers (for platform_data, functions, etc) since that won't be copied. + */ +int __init +i2c_register_board_info(int busnum, + struct i2c_board_info const *info, unsigned len) +{ + int status; + + down_write(&__i2c_board_lock); + + /* dynamic bus numbers will be assigned after the last static one */ + if (busnum >= __i2c_first_dynamic_bus_num) + __i2c_first_dynamic_bus_num = busnum + 1; + + for (status = 0; len; len--, info++) { + struct i2c_devinfo *devinfo; + + devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); + if (!devinfo) { + pr_debug("i2c-core: can't register boardinfo!\n"); + status = -ENOMEM; + break; + } + + devinfo->busnum = busnum; + devinfo->board_info = *info; + list_add_tail(&devinfo->list, &__i2c_board_list); + } + + up_write(&__i2c_board_lock); + + return status; +} diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c new file mode 100644 index 00000000..9a58994f --- /dev/null +++ b/drivers/i2c/i2c-core.c @@ -0,0 +1,2124 @@ +/* i2c-core.c - a device driver for the iic-bus interface */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>. + All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> + SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and + Jean Delvare <khali@linux-fr.org> + Mux support by Rodolfo Giometti <giometti@enneenne.com> and + Michael Lawnick <michael.lawnick.ext@nsn.com> */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/idr.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/completion.h> +#include <linux/hardirq.h> +#include <linux/irqflags.h> +#include <linux/rwsem.h> +#include <linux/pm_runtime.h> +#include <asm/uaccess.h> + +#include "i2c-core.h" + + +/* core_lock protects i2c_adapter_idr, and guarantees + that device detection, deletion of detected devices, and attach_adapter + and detach_adapter calls are serialized */ +static DEFINE_MUTEX(core_lock); +static DEFINE_IDR(i2c_adapter_idr); + +static struct device_type i2c_client_type; +static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); + +/* ------------------------------------------------------------------------- */ + +static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, + const struct i2c_client *client) +{ + while (id->name[0]) { + if (strcmp(client->name, id->name) == 0) + return id; + id++; + } + return NULL; +} + +static int i2c_device_match(struct device *dev, struct device_driver *drv) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + + if (!client) + return 0; + + /* Attempt an OF style match */ + if (of_driver_match_device(dev, drv)) + return 1; + + driver = to_i2c_driver(drv); + /* match on an id table if there is one */ + if (driver->id_table) + return i2c_match_id(driver->id_table, client) != NULL; + + return 0; +} + +#ifdef CONFIG_HOTPLUG + +/* uevent helps with hotplug: modprobe -q $(MODALIAS) */ +static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (add_uevent_var(env, "MODALIAS=%s%s", + I2C_MODULE_PREFIX, client->name)) + return -ENOMEM; + dev_dbg(dev, "uevent\n"); + return 0; +} + +#else +#define i2c_device_uevent NULL +#endif /* CONFIG_HOTPLUG */ + +static int i2c_device_probe(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + int status; + + if (!client) + return 0; + + driver = to_i2c_driver(dev->driver); + if (!driver->probe || !driver->id_table) + return -ENODEV; + client->driver = driver; + if (!device_can_wakeup(&client->dev)) + device_init_wakeup(&client->dev, + client->flags & I2C_CLIENT_WAKE); + dev_dbg(dev, "probe\n"); + + status = driver->probe(client, i2c_match_id(driver->id_table, client)); + if (status) { + client->driver = NULL; + i2c_set_clientdata(client, NULL); + } + return status; +} + +static int i2c_device_remove(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + int status; + + if (!client || !dev->driver) + return 0; + + driver = to_i2c_driver(dev->driver); + if (driver->remove) { + dev_dbg(dev, "remove\n"); + status = driver->remove(client); + } else { + dev->driver = NULL; + status = 0; + } + if (status == 0) { + client->driver = NULL; + i2c_set_clientdata(client, NULL); + } + return status; +} + +static void i2c_device_shutdown(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + + if (!client || !dev->driver) + return; + driver = to_i2c_driver(dev->driver); + if (driver->shutdown) + driver->shutdown(client); +} + +#ifdef CONFIG_PM_SLEEP +static int i2c_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + + if (!client || !dev->driver) + return 0; + driver = to_i2c_driver(dev->driver); + if (!driver->suspend) + return 0; + return driver->suspend(client, mesg); +} + +static int i2c_legacy_resume(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + + if (!client || !dev->driver) + return 0; + driver = to_i2c_driver(dev->driver); + if (!driver->resume) + return 0; + return driver->resume(client); +} + +static int i2c_device_pm_suspend(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_suspend(dev); + else + return i2c_legacy_suspend(dev, PMSG_SUSPEND); +} + +static int i2c_device_pm_resume(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_resume(dev); + else + return i2c_legacy_resume(dev); +} + +static int i2c_device_pm_freeze(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_freeze(dev); + else + return i2c_legacy_suspend(dev, PMSG_FREEZE); +} + +static int i2c_device_pm_thaw(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_thaw(dev); + else + return i2c_legacy_resume(dev); +} + +static int i2c_device_pm_poweroff(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_poweroff(dev); + else + return i2c_legacy_suspend(dev, PMSG_HIBERNATE); +} + +static int i2c_device_pm_restore(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_restore(dev); + else + return i2c_legacy_resume(dev); +} +#else /* !CONFIG_PM_SLEEP */ +#define i2c_device_pm_suspend NULL +#define i2c_device_pm_resume NULL +#define i2c_device_pm_freeze NULL +#define i2c_device_pm_thaw NULL +#define i2c_device_pm_poweroff NULL +#define i2c_device_pm_restore NULL +#endif /* !CONFIG_PM_SLEEP */ + +static void i2c_client_dev_release(struct device *dev) +{ + kfree(to_i2c_client(dev)); +} + +static ssize_t +show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", dev->type == &i2c_client_type ? + to_i2c_client(dev)->name : to_i2c_adapter(dev)->name); +} + +static ssize_t +show_modalias(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name); +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +static struct attribute *i2c_dev_attrs[] = { + &dev_attr_name.attr, + /* modalias helps coldplug: modprobe $(cat .../modalias) */ + &dev_attr_modalias.attr, + NULL +}; + +static struct attribute_group i2c_dev_attr_group = { + .attrs = i2c_dev_attrs, +}; + +static const struct attribute_group *i2c_dev_attr_groups[] = { + &i2c_dev_attr_group, + NULL +}; + +static const struct dev_pm_ops i2c_device_pm_ops = { + .suspend = i2c_device_pm_suspend, + .resume = i2c_device_pm_resume, + .freeze = i2c_device_pm_freeze, + .thaw = i2c_device_pm_thaw, + .poweroff = i2c_device_pm_poweroff, + .restore = i2c_device_pm_restore, + SET_RUNTIME_PM_OPS( + pm_generic_runtime_suspend, + pm_generic_runtime_resume, + pm_generic_runtime_idle + ) +}; + +struct bus_type i2c_bus_type = { + .name = "i2c", + .match = i2c_device_match, + .probe = i2c_device_probe, + .remove = i2c_device_remove, + .shutdown = i2c_device_shutdown, + .pm = &i2c_device_pm_ops, +}; +EXPORT_SYMBOL_GPL(i2c_bus_type); + +static struct device_type i2c_client_type = { + .groups = i2c_dev_attr_groups, + .uevent = i2c_device_uevent, + .release = i2c_client_dev_release, +}; + + +/** + * i2c_verify_client - return parameter as i2c_client, or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-I2C device as an i2c_client. + */ +struct i2c_client *i2c_verify_client(struct device *dev) +{ + return (dev->type == &i2c_client_type) + ? to_i2c_client(dev) + : NULL; +} +EXPORT_SYMBOL(i2c_verify_client); + + +/* This is a permissive address validity check, I2C address map constraints + * are purposely not enforced, except for the general call address. */ +static int i2c_check_client_addr_validity(const struct i2c_client *client) +{ + if (client->flags & I2C_CLIENT_TEN) { + /* 10-bit address, all values are valid */ + if (client->addr > 0x3ff) + return -EINVAL; + } else { + /* 7-bit address, reject the general call address */ + if (client->addr == 0x00 || client->addr > 0x7f) + return -EINVAL; + } + return 0; +} + +/* And this is a strict address validity check, used when probing. If a + * device uses a reserved address, then it shouldn't be probed. 7-bit + * addressing is assumed, 10-bit address devices are rare and should be + * explicitly enumerated. */ +static int i2c_check_addr_validity(unsigned short addr) +{ + /* + * Reserved addresses per I2C specification: + * 0x00 General call address / START byte + * 0x01 CBUS address + * 0x02 Reserved for different bus format + * 0x03 Reserved for future purposes + * 0x04-0x07 Hs-mode master code + * 0x78-0x7b 10-bit slave addressing + * 0x7c-0x7f Reserved for future purposes + */ + if (addr < 0x08 || addr > 0x77) + return -EINVAL; + return 0; +} + +static int __i2c_check_addr_busy(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + int addr = *(int *)addrp; + + if (client && client->addr == addr) + return -EBUSY; + return 0; +} + +/* walk up mux tree */ +static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result; + + result = device_for_each_child(&adapter->dev, &addr, + __i2c_check_addr_busy); + + if (!result && parent) + result = i2c_check_mux_parents(parent, addr); + + return result; +} + +/* recurse down mux tree */ +static int i2c_check_mux_children(struct device *dev, void *addrp) +{ + int result; + + if (dev->type == &i2c_adapter_type) + result = device_for_each_child(dev, addrp, + i2c_check_mux_children); + else + result = __i2c_check_addr_busy(dev, addrp); + + return result; +} + +static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result = 0; + + if (parent) + result = i2c_check_mux_parents(parent, addr); + + if (!result) + result = device_for_each_child(&adapter->dev, &addr, + i2c_check_mux_children); + + return result; +} + +/** + * i2c_lock_adapter - Get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +void i2c_lock_adapter(struct i2c_adapter *adapter) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + + if (parent) + i2c_lock_adapter(parent); + else + rt_mutex_lock(&adapter->bus_lock); +} +EXPORT_SYMBOL_GPL(i2c_lock_adapter); + +/** + * i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +static int i2c_trylock_adapter(struct i2c_adapter *adapter) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + + if (parent) + return i2c_trylock_adapter(parent); + else + return rt_mutex_trylock(&adapter->bus_lock); +} + +/** + * i2c_unlock_adapter - Release exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + */ +void i2c_unlock_adapter(struct i2c_adapter *adapter) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + + if (parent) + i2c_unlock_adapter(parent); + else + rt_mutex_unlock(&adapter->bus_lock); +} +EXPORT_SYMBOL_GPL(i2c_unlock_adapter); + +/** + * i2c_new_device - instantiate an i2c device + * @adap: the adapter managing the device + * @info: describes one I2C device; bus_num is ignored + * Context: can sleep + * + * Create an i2c device. Binding is handled through driver model + * probe()/remove() methods. A driver may be bound to this device when we + * return from this function, or any later moment (e.g. maybe hotplugging will + * load the driver module). This call is not appropriate for use by mainboard + * initialization logic, which usually runs during an arch_initcall() long + * before any i2c_adapter could exist. + * + * This returns the new i2c client, which may be saved for later use with + * i2c_unregister_device(); or NULL to indicate an error. + */ +struct i2c_client * +i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) +{ + struct i2c_client *client; + int status; + + client = kzalloc(sizeof *client, GFP_KERNEL); + if (!client) + return NULL; + + client->adapter = adap; + + client->dev.platform_data = info->platform_data; + + if (info->archdata) + client->dev.archdata = *info->archdata; + + client->flags = info->flags; + client->addr = info->addr; + client->irq = info->irq; + + strlcpy(client->name, info->type, sizeof(client->name)); + + /* Check for address validity */ + status = i2c_check_client_addr_validity(client); + if (status) { + dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n", + client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr); + goto out_err_silent; + } + + /* Check for address business */ + status = i2c_check_addr_busy(adap, client->addr); + if (status) + goto out_err; + + client->dev.parent = &client->adapter->dev; + client->dev.bus = &i2c_bus_type; + client->dev.type = &i2c_client_type; + client->dev.of_node = info->of_node; + + dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), + client->addr); + status = device_register(&client->dev); + if (status) + goto out_err; + + dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", + client->name, dev_name(&client->dev)); + + return client; + +out_err: + dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x " + "(%d)\n", client->name, client->addr, status); +out_err_silent: + kfree(client); + return NULL; +} +EXPORT_SYMBOL_GPL(i2c_new_device); + + +/** + * i2c_unregister_device - reverse effect of i2c_new_device() + * @client: value returned from i2c_new_device() + * Context: can sleep + */ +void i2c_unregister_device(struct i2c_client *client) +{ + device_unregister(&client->dev); +} +EXPORT_SYMBOL_GPL(i2c_unregister_device); + + +static const struct i2c_device_id dummy_id[] = { + { "dummy", 0 }, + { }, +}; + +static int dummy_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return 0; +} + +static int dummy_remove(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_driver dummy_driver = { + .driver.name = "dummy", + .probe = dummy_probe, + .remove = dummy_remove, + .id_table = dummy_id, +}; + +/** + * i2c_new_dummy - return a new i2c device bound to a dummy driver + * @adapter: the adapter managing the device + * @address: seven bit address to be used + * Context: can sleep + * + * This returns an I2C client bound to the "dummy" driver, intended for use + * with devices that consume multiple addresses. Examples of such chips + * include various EEPROMS (like 24c04 and 24c08 models). + * + * These dummy devices have two main uses. First, most I2C and SMBus calls + * except i2c_transfer() need a client handle; the dummy will be that handle. + * And second, this prevents the specified address from being bound to a + * different driver. + * + * This returns the new i2c client, which should be saved for later use with + * i2c_unregister_device(); or NULL to indicate an error. + */ +struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("dummy", address), + }; + + return i2c_new_device(adapter, &info); +} +EXPORT_SYMBOL_GPL(i2c_new_dummy); + +/* ------------------------------------------------------------------------- */ + +/* I2C bus adapters -- one roots each I2C or SMBUS segment */ + +static void i2c_adapter_dev_release(struct device *dev) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + complete(&adap->dev_released); +} + +/* + * Let users instantiate I2C devices through sysfs. This can be used when + * platform initialization code doesn't contain the proper data for + * whatever reason. Also useful for drivers that do device detection and + * detection fails, either because the device uses an unexpected address, + * or this is a compatible device with different ID register values. + * + * Parameter checking may look overzealous, but we really don't want + * the user to provide incorrect parameters. + */ +static ssize_t +i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + struct i2c_board_info info; + struct i2c_client *client; + char *blank, end; + int res; + + memset(&info, 0, sizeof(struct i2c_board_info)); + + blank = strchr(buf, ' '); + if (!blank) { + dev_err(dev, "%s: Missing parameters\n", "new_device"); + return -EINVAL; + } + if (blank - buf > I2C_NAME_SIZE - 1) { + dev_err(dev, "%s: Invalid device name\n", "new_device"); + return -EINVAL; + } + memcpy(info.type, buf, blank - buf); + + /* Parse remaining parameters, reject extra parameters */ + res = sscanf(++blank, "%hi%c", &info.addr, &end); + if (res < 1) { + dev_err(dev, "%s: Can't parse I2C address\n", "new_device"); + return -EINVAL; + } + if (res > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "new_device"); + return -EINVAL; + } + + client = i2c_new_device(adap, &info); + if (!client) + return -EINVAL; + + /* Keep track of the added device */ + mutex_lock(&adap->userspace_clients_lock); + list_add_tail(&client->detected, &adap->userspace_clients); + mutex_unlock(&adap->userspace_clients_lock); + dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", + info.type, info.addr); + + return count; +} + +/* + * And of course let the users delete the devices they instantiated, if + * they got it wrong. This interface can only be used to delete devices + * instantiated by i2c_sysfs_new_device above. This guarantees that we + * don't delete devices to which some kernel code still has references. + * + * Parameter checking may look overzealous, but we really don't want + * the user to delete the wrong device. + */ +static ssize_t +i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + struct i2c_client *client, *next; + unsigned short addr; + char end; + int res; + + /* Parse parameters, reject extra parameters */ + res = sscanf(buf, "%hi%c", &addr, &end); + if (res < 1) { + dev_err(dev, "%s: Can't parse I2C address\n", "delete_device"); + return -EINVAL; + } + if (res > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "delete_device"); + return -EINVAL; + } + + /* Make sure the device was added through sysfs */ + res = -ENOENT; + mutex_lock(&adap->userspace_clients_lock); + list_for_each_entry_safe(client, next, &adap->userspace_clients, + detected) { + if (client->addr == addr) { + dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", + "delete_device", client->name, client->addr); + + list_del(&client->detected); + i2c_unregister_device(client); + res = count; + break; + } + } + mutex_unlock(&adap->userspace_clients_lock); + + if (res < 0) + dev_err(dev, "%s: Can't find device in list\n", + "delete_device"); + return res; +} + +static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device); +static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device); + +static struct attribute *i2c_adapter_attrs[] = { + &dev_attr_name.attr, + &dev_attr_new_device.attr, + &dev_attr_delete_device.attr, + NULL +}; + +static struct attribute_group i2c_adapter_attr_group = { + .attrs = i2c_adapter_attrs, +}; + +static const struct attribute_group *i2c_adapter_attr_groups[] = { + &i2c_adapter_attr_group, + NULL +}; + +struct device_type i2c_adapter_type = { + .groups = i2c_adapter_attr_groups, + .release = i2c_adapter_dev_release, +}; +EXPORT_SYMBOL_GPL(i2c_adapter_type); + +#ifdef CONFIG_I2C_COMPAT +static struct class_compat *i2c_adapter_compat_class; +#endif + +static void i2c_scan_static_board_info(struct i2c_adapter *adapter) +{ + struct i2c_devinfo *devinfo; + + down_read(&__i2c_board_lock); + list_for_each_entry(devinfo, &__i2c_board_list, list) { + if (devinfo->busnum == adapter->nr + && !i2c_new_device(adapter, + &devinfo->board_info)) + dev_err(&adapter->dev, + "Can't create device at 0x%02x\n", + devinfo->board_info.addr); + } + up_read(&__i2c_board_lock); +} + +static int i2c_do_add_adapter(struct i2c_driver *driver, + struct i2c_adapter *adap) +{ + /* Detect supported devices on that bus, and instantiate them */ + i2c_detect(adap, driver); + + /* Let legacy drivers scan this bus for matching devices */ + if (driver->attach_adapter) { + dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n", + driver->driver.name); + dev_warn(&adap->dev, "Please use another way to instantiate " + "your i2c_client\n"); + /* We ignore the return code; if it fails, too bad */ + driver->attach_adapter(adap); + } + return 0; +} + +static int __process_new_adapter(struct device_driver *d, void *data) +{ + return i2c_do_add_adapter(to_i2c_driver(d), data); +} + +static int i2c_register_adapter(struct i2c_adapter *adap) +{ + int res = 0; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!i2c_bus_type.p))) { + res = -EAGAIN; + goto out_list; + } + + /* Sanity checks */ + if (unlikely(adap->name[0] == '\0')) { + pr_err("i2c-core: Attempt to register an adapter with " + "no name!\n"); + return -EINVAL; + } + if (unlikely(!adap->algo)) { + pr_err("i2c-core: Attempt to register adapter '%s' with " + "no algo!\n", adap->name); + return -EINVAL; + } + + rt_mutex_init(&adap->bus_lock); + mutex_init(&adap->userspace_clients_lock); + INIT_LIST_HEAD(&adap->userspace_clients); + + /* Set default timeout to 1 second if not already set */ + if (adap->timeout == 0) + adap->timeout = HZ; + + dev_set_name(&adap->dev, "i2c-%d", adap->nr); + adap->dev.bus = &i2c_bus_type; + adap->dev.type = &i2c_adapter_type; + res = device_register(&adap->dev); + if (res) + goto out_list; + + dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); + +#ifdef CONFIG_I2C_COMPAT + res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev, + adap->dev.parent); + if (res) + dev_warn(&adap->dev, + "Failed to create compatibility class link\n"); +#endif + + /* create pre-declared device nodes */ + if (adap->nr < __i2c_first_dynamic_bus_num) + i2c_scan_static_board_info(adap); + + /* Notify drivers */ + mutex_lock(&core_lock); + bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); + mutex_unlock(&core_lock); + + return 0; + +out_list: + mutex_lock(&core_lock); + idr_remove(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + return res; +} + +/** + * i2c_add_adapter - declare i2c adapter, use dynamic bus number + * @adapter: the adapter to add + * Context: can sleep + * + * This routine is used to declare an I2C adapter when its bus number + * doesn't matter. Examples: for I2C adapters dynamically added by + * USB links or PCI plugin cards. + * + * When this returns zero, a new bus number was allocated and stored + * in adap->nr, and the specified adapter became available for clients. + * Otherwise, a negative errno value is returned. + */ +int i2c_add_adapter(struct i2c_adapter *adapter) +{ + int id, res = 0; + +retry: + if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) + return -ENOMEM; + + mutex_lock(&core_lock); + /* "above" here means "above or equal to", sigh */ + res = idr_get_new_above(&i2c_adapter_idr, adapter, + __i2c_first_dynamic_bus_num, &id); + mutex_unlock(&core_lock); + + if (res < 0) { + if (res == -EAGAIN) + goto retry; + return res; + } + + adapter->nr = id; + return i2c_register_adapter(adapter); +} +EXPORT_SYMBOL(i2c_add_adapter); + +/** + * i2c_add_numbered_adapter - declare i2c adapter, use static bus number + * @adap: the adapter to register (with adap->nr initialized) + * Context: can sleep + * + * This routine is used to declare an I2C adapter when its bus number + * matters. For example, use it for I2C adapters from system-on-chip CPUs, + * or otherwise built in to the system's mainboard, and where i2c_board_info + * is used to properly configure I2C devices. + * + * If no devices have pre-been declared for this bus, then be sure to + * register the adapter before any dynamically allocated ones. Otherwise + * the required bus ID may not be available. + * + * When this returns zero, the specified adapter became available for + * clients using the bus number provided in adap->nr. Also, the table + * of I2C devices pre-declared using i2c_register_board_info() is scanned, + * and the appropriate driver model device nodes are created. Otherwise, a + * negative errno value is returned. + */ +int i2c_add_numbered_adapter(struct i2c_adapter *adap) +{ + int id; + int status; + + if (adap->nr & ~MAX_ID_MASK) + return -EINVAL; + +retry: + if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) + return -ENOMEM; + + mutex_lock(&core_lock); + /* "above" here means "above or equal to", sigh; + * we need the "equal to" result to force the result + */ + status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id); + if (status == 0 && id != adap->nr) { + status = -EBUSY; + idr_remove(&i2c_adapter_idr, id); + } + mutex_unlock(&core_lock); + if (status == -EAGAIN) + goto retry; + + if (status == 0) + status = i2c_register_adapter(adap); + return status; +} +EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); + +static int i2c_do_del_adapter(struct i2c_driver *driver, + struct i2c_adapter *adapter) +{ + struct i2c_client *client, *_n; + int res; + + /* Remove the devices we created ourselves as the result of hardware + * probing (using a driver's detect method) */ + list_for_each_entry_safe(client, _n, &driver->clients, detected) { + if (client->adapter == adapter) { + dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", + client->name, client->addr); + list_del(&client->detected); + i2c_unregister_device(client); + } + } + + if (!driver->detach_adapter) + return 0; + dev_warn(&adapter->dev, "%s: detach_adapter method is deprecated\n", + driver->driver.name); + res = driver->detach_adapter(adapter); + if (res) + dev_err(&adapter->dev, "detach_adapter failed (%d) " + "for driver [%s]\n", res, driver->driver.name); + return res; +} + +static int __unregister_client(struct device *dev, void *dummy) +{ + struct i2c_client *client = i2c_verify_client(dev); + if (client && strcmp(client->name, "dummy")) + i2c_unregister_device(client); + return 0; +} + +static int __unregister_dummy(struct device *dev, void *dummy) +{ + struct i2c_client *client = i2c_verify_client(dev); + if (client) + i2c_unregister_device(client); + return 0; +} + +static int __process_removed_adapter(struct device_driver *d, void *data) +{ + return i2c_do_del_adapter(to_i2c_driver(d), data); +} + +/** + * i2c_del_adapter - unregister I2C adapter + * @adap: the adapter being unregistered + * Context: can sleep + * + * This unregisters an I2C adapter which was previously registered + * by @i2c_add_adapter or @i2c_add_numbered_adapter. + */ +int i2c_del_adapter(struct i2c_adapter *adap) +{ + int res = 0; + struct i2c_adapter *found; + struct i2c_client *client, *next; + + /* First make sure that this adapter was ever added */ + mutex_lock(&core_lock); + found = idr_find(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + if (found != adap) { + pr_debug("i2c-core: attempting to delete unregistered " + "adapter [%s]\n", adap->name); + return -EINVAL; + } + + /* Tell drivers about this removal */ + mutex_lock(&core_lock); + res = bus_for_each_drv(&i2c_bus_type, NULL, adap, + __process_removed_adapter); + mutex_unlock(&core_lock); + if (res) + return res; + + /* Remove devices instantiated from sysfs */ + mutex_lock(&adap->userspace_clients_lock); + list_for_each_entry_safe(client, next, &adap->userspace_clients, + detected) { + dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name, + client->addr); + list_del(&client->detected); + i2c_unregister_device(client); + } + mutex_unlock(&adap->userspace_clients_lock); + + /* Detach any active clients. This can't fail, thus we do not + * check the returned value. This is a two-pass process, because + * we can't remove the dummy devices during the first pass: they + * could have been instantiated by real devices wishing to clean + * them up properly, so we give them a chance to do that first. */ + res = device_for_each_child(&adap->dev, NULL, __unregister_client); + res = device_for_each_child(&adap->dev, NULL, __unregister_dummy); + +#ifdef CONFIG_I2C_COMPAT + class_compat_remove_link(i2c_adapter_compat_class, &adap->dev, + adap->dev.parent); +#endif + + /* device name is gone after device_unregister */ + dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); + + /* clean up the sysfs representation */ + init_completion(&adap->dev_released); + device_unregister(&adap->dev); + + /* wait for sysfs to drop all references */ + wait_for_completion(&adap->dev_released); + + /* free bus id */ + mutex_lock(&core_lock); + idr_remove(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + + /* Clear the device structure in case this adapter is ever going to be + added again */ + memset(&adap->dev, 0, sizeof(adap->dev)); + + return 0; +} +EXPORT_SYMBOL(i2c_del_adapter); + + +/* ------------------------------------------------------------------------- */ + +int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *)) +{ + int res; + + mutex_lock(&core_lock); + res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn); + mutex_unlock(&core_lock); + + return res; +} +EXPORT_SYMBOL_GPL(i2c_for_each_dev); + +static int __process_new_driver(struct device *dev, void *data) +{ + if (dev->type != &i2c_adapter_type) + return 0; + return i2c_do_add_adapter(data, to_i2c_adapter(dev)); +} + +/* + * An i2c_driver is used with one or more i2c_client (device) nodes to access + * i2c slave chips, on a bus instance associated with some i2c_adapter. + */ + +int i2c_register_driver(struct module *owner, struct i2c_driver *driver) +{ + int res; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!i2c_bus_type.p))) + return -EAGAIN; + + /* add the driver to the list of i2c drivers in the driver core */ + driver->driver.owner = owner; + driver->driver.bus = &i2c_bus_type; + + /* When registration returns, the driver core + * will have called probe() for all matching-but-unbound devices. + */ + res = driver_register(&driver->driver); + if (res) + return res; + + /* Drivers should switch to dev_pm_ops instead. */ + if (driver->suspend) + pr_warn("i2c-core: driver [%s] using legacy suspend method\n", + driver->driver.name); + if (driver->resume) + pr_warn("i2c-core: driver [%s] using legacy resume method\n", + driver->driver.name); + + pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name); + + INIT_LIST_HEAD(&driver->clients); + /* Walk the adapters that are already present */ + i2c_for_each_dev(driver, __process_new_driver); + + return 0; +} +EXPORT_SYMBOL(i2c_register_driver); + +static int __process_removed_driver(struct device *dev, void *data) +{ + if (dev->type != &i2c_adapter_type) + return 0; + return i2c_do_del_adapter(data, to_i2c_adapter(dev)); +} + +/** + * i2c_del_driver - unregister I2C driver + * @driver: the driver being unregistered + * Context: can sleep + */ +void i2c_del_driver(struct i2c_driver *driver) +{ + i2c_for_each_dev(driver, __process_removed_driver); + + driver_unregister(&driver->driver); + pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name); +} +EXPORT_SYMBOL(i2c_del_driver); + +/* ------------------------------------------------------------------------- */ + +/** + * i2c_use_client - increments the reference count of the i2c client structure + * @client: the client being referenced + * + * Each live reference to a client should be refcounted. The driver model does + * that automatically as part of driver binding, so that most drivers don't + * need to do this explicitly: they hold a reference until they're unbound + * from the device. + * + * A pointer to the client with the incremented reference counter is returned. + */ +struct i2c_client *i2c_use_client(struct i2c_client *client) +{ + if (client && get_device(&client->dev)) + return client; + return NULL; +} +EXPORT_SYMBOL(i2c_use_client); + +/** + * i2c_release_client - release a use of the i2c client structure + * @client: the client being no longer referenced + * + * Must be called when a user of a client is finished with it. + */ +void i2c_release_client(struct i2c_client *client) +{ + if (client) + put_device(&client->dev); +} +EXPORT_SYMBOL(i2c_release_client); + +struct i2c_cmd_arg { + unsigned cmd; + void *arg; +}; + +static int i2c_cmd(struct device *dev, void *_arg) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_cmd_arg *arg = _arg; + + if (client && client->driver && client->driver->command) + client->driver->command(client, arg->cmd, arg->arg); + return 0; +} + +void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg) +{ + struct i2c_cmd_arg cmd_arg; + + cmd_arg.cmd = cmd; + cmd_arg.arg = arg; + device_for_each_child(&adap->dev, &cmd_arg, i2c_cmd); +} +EXPORT_SYMBOL(i2c_clients_command); + +static int __init i2c_init(void) +{ + int retval; + + retval = bus_register(&i2c_bus_type); + if (retval) + return retval; +#ifdef CONFIG_I2C_COMPAT + i2c_adapter_compat_class = class_compat_register("i2c-adapter"); + if (!i2c_adapter_compat_class) { + retval = -ENOMEM; + goto bus_err; + } +#endif + retval = i2c_add_driver(&dummy_driver); + if (retval) + goto class_err; + return 0; + +class_err: +#ifdef CONFIG_I2C_COMPAT + class_compat_unregister(i2c_adapter_compat_class); +bus_err: +#endif + bus_unregister(&i2c_bus_type); + return retval; +} + +static void __exit i2c_exit(void) +{ + i2c_del_driver(&dummy_driver); +#ifdef CONFIG_I2C_COMPAT + class_compat_unregister(i2c_adapter_compat_class); +#endif + bus_unregister(&i2c_bus_type); +} + +/* We must initialize early, because some subsystems register i2c drivers + * in subsys_initcall() code, but are linked (and initialized) before i2c. + */ +postcore_initcall(i2c_init); +module_exit(i2c_exit); + +/* ---------------------------------------------------- + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ + +/** + * i2c_transfer - execute a single or combined I2C message + * @adap: Handle to I2C bus + * @msgs: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Note that there is no requirement that each message be sent to + * the same slave address, although that is the most common model. + */ +int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + unsigned long orig_jiffies; + int ret, try; + + /* REVISIT the fault reporting model here is weak: + * + * - When we get an error after receiving N bytes from a slave, + * there is no way to report "N". + * + * - When we get a NAK after transmitting N bytes to a slave, + * there is no way to report "N" ... or to let the master + * continue executing the rest of this combined message, if + * that's the appropriate response. + * + * - When for example "num" is two and we successfully complete + * the first message but get an error part way through the + * second, it's unclear whether that should be reported as + * one (discarding status on the second message) or errno + * (discarding status on the first one). + */ + + if (adap->algo->master_xfer) { +#ifdef DEBUG + for (ret = 0; ret < num; ret++) { + dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, " + "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD) + ? 'R' : 'W', msgs[ret].addr, msgs[ret].len, + (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); + } +#endif + + if (in_atomic() || irqs_disabled()) { + ret = i2c_trylock_adapter(adap); + if (!ret) + /* I2C activity is ongoing. */ + return -EAGAIN; + } else { + i2c_lock_adapter(adap); + } + + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (ret = 0, try = 0; try <= adap->retries; try++) { + ret = adap->algo->master_xfer(adap, msgs, num); + if (ret != -EAGAIN) + break; + if (time_after(jiffies, orig_jiffies + adap->timeout)) + break; + } + i2c_unlock_adapter(adap); + + return ret; + } else { + dev_dbg(&adap->dev, "I2C level transfers not supported\n"); + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL(i2c_transfer); + +/** + * i2c_master_send - issue a single I2C message in master transmit mode + * @client: Handle to slave device + * @buf: Data that will be written to the slave + * @count: How many bytes to write, must be less than 64k since msg.len is u16 + * + * Returns negative errno, or else the number of bytes written. + */ +int i2c_master_send(const struct i2c_client *client, const char *buf, int count) +{ + int ret; + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.len = count; + msg.buf = (char *)buf; + + ret = i2c_transfer(adap, &msg, 1); + + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + return (ret == 1) ? count : ret; +} +EXPORT_SYMBOL(i2c_master_send); + +/** + * i2c_master_recv - issue a single I2C message in master receive mode + * @client: Handle to slave device + * @buf: Where to store data read from slave + * @count: How many bytes to read, must be less than 64k since msg.len is u16 + * + * Returns negative errno, or else the number of bytes read. + */ +int i2c_master_recv(const struct i2c_client *client, char *buf, int count) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + int ret; + + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = buf; + + ret = i2c_transfer(adap, &msg, 1); + + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + return (ret == 1) ? count : ret; +} +EXPORT_SYMBOL(i2c_master_recv); + +/* ---------------------------------------------------- + * the i2c address scanning function + * Will not work for 10-bit addresses! + * ---------------------------------------------------- + */ + +/* + * Legacy default probe function, mostly relevant for SMBus. The default + * probe method is a quick write, but it is known to corrupt the 24RF08 + * EEPROMs due to a state machine bug, and could also irreversibly + * write-protect some EEPROMs, so for address ranges 0x30-0x37 and 0x50-0x5f, + * we use a short byte read instead. Also, some bus drivers don't implement + * quick write, so we fallback to a byte read in that case too. + * On x86, there is another special case for FSC hardware monitoring chips, + * which want regular byte reads (address 0x73.) Fortunately, these are the + * only known chips using this I2C address on PC hardware. + * Returns 1 if probe succeeded, 0 if not. + */ +static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr) +{ + int err; + union i2c_smbus_data dummy; + +#ifdef CONFIG_X86 + if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON) + && i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE_DATA, &dummy); + else +#endif + if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50) + && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0, + I2C_SMBUS_QUICK, NULL); + else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &dummy); + else { + dev_warn(&adap->dev, "No suitable probing method supported\n"); + err = -EOPNOTSUPP; + } + + return err >= 0; +} + +static int i2c_detect_address(struct i2c_client *temp_client, + struct i2c_driver *driver) +{ + struct i2c_board_info info; + struct i2c_adapter *adapter = temp_client->adapter; + int addr = temp_client->addr; + int err; + + /* Make sure the address is valid */ + err = i2c_check_addr_validity(addr); + if (err) { + dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n", + addr); + return err; + } + + /* Skip if already in use */ + if (i2c_check_addr_busy(adapter, addr)) + return 0; + + /* Make sure there is something at this address */ + if (!i2c_default_probe(adapter, addr)) + return 0; + + /* Finally call the custom detection function */ + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = addr; + err = driver->detect(temp_client, &info); + if (err) { + /* -ENODEV is returned if the detection fails. We catch it + here as this isn't an error. */ + return err == -ENODEV ? 0 : err; + } + + /* Consistency check */ + if (info.type[0] == '\0') { + dev_err(&adapter->dev, "%s detection function provided " + "no name for 0x%x\n", driver->driver.name, + addr); + } else { + struct i2c_client *client; + + /* Detection succeeded, instantiate the device */ + dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n", + info.type, info.addr); + client = i2c_new_device(adapter, &info); + if (client) + list_add_tail(&client->detected, &driver->clients); + else + dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n", + info.type, info.addr); + } + return 0; +} + +static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) +{ + const unsigned short *address_list; + struct i2c_client *temp_client; + int i, err = 0; + int adap_id = i2c_adapter_id(adapter); + + address_list = driver->address_list; + if (!driver->detect || !address_list) + return 0; + + /* Stop here if the classes do not match */ + if (!(adapter->class & driver->class)) + return 0; + + /* Set up a temporary client to help detect callback */ + temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!temp_client) + return -ENOMEM; + temp_client->adapter = adapter; + + for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) { + dev_dbg(&adapter->dev, "found normal entry for adapter %d, " + "addr 0x%02x\n", adap_id, address_list[i]); + temp_client->addr = address_list[i]; + err = i2c_detect_address(temp_client, driver); + if (unlikely(err)) + break; + } + + kfree(temp_client); + return err; +} + +int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr) +{ + return i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_QUICK, NULL) >= 0; +} +EXPORT_SYMBOL_GPL(i2c_probe_func_quick_read); + +struct i2c_client * +i2c_new_probed_device(struct i2c_adapter *adap, + struct i2c_board_info *info, + unsigned short const *addr_list, + int (*probe)(struct i2c_adapter *, unsigned short addr)) +{ + int i; + + if (!probe) + probe = i2c_default_probe; + + for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) { + /* Check address validity */ + if (i2c_check_addr_validity(addr_list[i]) < 0) { + dev_warn(&adap->dev, "Invalid 7-bit address " + "0x%02x\n", addr_list[i]); + continue; + } + + /* Check address availability */ + if (i2c_check_addr_busy(adap, addr_list[i])) { + dev_dbg(&adap->dev, "Address 0x%02x already in " + "use, not probing\n", addr_list[i]); + continue; + } + + /* Test address responsiveness */ + if (probe(adap, addr_list[i])) + break; + } + + if (addr_list[i] == I2C_CLIENT_END) { + dev_dbg(&adap->dev, "Probing failed, no device found\n"); + return NULL; + } + + info->addr = addr_list[i]; + return i2c_new_device(adap, info); +} +EXPORT_SYMBOL_GPL(i2c_new_probed_device); + +struct i2c_adapter *i2c_get_adapter(int nr) +{ + struct i2c_adapter *adapter; + + mutex_lock(&core_lock); + adapter = idr_find(&i2c_adapter_idr, nr); + if (adapter && !try_module_get(adapter->owner)) + adapter = NULL; + + mutex_unlock(&core_lock); + return adapter; +} +EXPORT_SYMBOL(i2c_get_adapter); + +void i2c_put_adapter(struct i2c_adapter *adap) +{ + module_put(adap->owner); +} +EXPORT_SYMBOL(i2c_put_adapter); + +/* The SMBus parts */ + +#define POLY (0x1070U << 3) +static u8 crc8(u16 data) +{ + int i; + + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +/* Incremental CRC8 over count bytes in the array pointed to by p */ +static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count) +{ + int i; + + for (i = 0; i < count; i++) + crc = crc8((crc ^ p[i]) << 8); + return crc; +} + +/* Assume a 7-bit address, which is reasonable for SMBus */ +static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg) +{ + /* The address will be sent first */ + u8 addr = (msg->addr << 1) | !!(msg->flags & I2C_M_RD); + pec = i2c_smbus_pec(pec, &addr, 1); + + /* The data buffer follows */ + return i2c_smbus_pec(pec, msg->buf, msg->len); +} + +/* Used for write only transactions */ +static inline void i2c_smbus_add_pec(struct i2c_msg *msg) +{ + msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg); + msg->len++; +} + +/* Return <0 on CRC error + If there was a write before this read (most cases) we need to take the + partial CRC from the write part into account. + Note that this function does modify the message (we need to decrease the + message length to hide the CRC byte from the caller). */ +static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg) +{ + u8 rpec = msg->buf[--msg->len]; + cpec = i2c_smbus_msg_pec(cpec, msg); + + if (rpec != cpec) { + pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n", + rpec, cpec); + return -EBADMSG; + } + return 0; +} + +/** + * i2c_smbus_read_byte - SMBus "receive byte" protocol + * @client: Handle to slave device + * + * This executes the SMBus "receive byte" protocol, returning negative errno + * else the byte received from the device. + */ +s32 i2c_smbus_read_byte(const struct i2c_client *client) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte); + +/** + * i2c_smbus_write_byte - SMBus "send byte" protocol + * @client: Handle to slave device + * @value: Byte to be sent + * + * This executes the SMBus "send byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value) +{ + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); +} +EXPORT_SYMBOL(i2c_smbus_write_byte); + +/** + * i2c_smbus_read_byte_data - SMBus "read byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read byte" protocol, returning negative errno + * else a data byte received from the device. + */ +s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte_data); + +/** + * i2c_smbus_write_byte_data - SMBus "write byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: Byte being written + * + * This executes the SMBus "write byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, + u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_byte_data); + +/** + * i2c_smbus_read_word_data - SMBus "read word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read word" protocol, returning negative errno + * else a 16-bit unsigned "word" received from the device. + */ +s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_WORD_DATA, &data); + return (status < 0) ? status : data.word; +} +EXPORT_SYMBOL(i2c_smbus_read_word_data); + +/** + * i2c_smbus_write_word_data - SMBus "write word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: 16-bit "word" being written + * + * This executes the SMBus "write word" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, + u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_WORD_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_word_data); + +/** + * i2c_smbus_process_call - SMBus "process call" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: 16-bit "word" being written + * + * This executes the SMBus "process call" protocol, returning negative errno + * else a 16-bit unsigned "word" received from the device. + */ +s32 i2c_smbus_process_call(const struct i2c_client *client, u8 command, + u16 value) +{ + union i2c_smbus_data data; + int status; + data.word = value; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_PROC_CALL, &data); + return (status < 0) ? status : data.word; +} +EXPORT_SYMBOL(i2c_smbus_process_call); + +/** + * i2c_smbus_read_block_data - SMBus "block read" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @values: Byte array into which data will be read; big enough to hold + * the data returned by the slave. SMBus allows at most 32 bytes. + * + * This executes the SMBus "block read" protocol, returning negative errno + * else the number of data bytes in the slave's response. + * + * Note that using this function requires that the client's adapter support + * the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality. Not all adapter drivers + * support this; its emulation through I2C messaging relies on a specific + * mechanism (I2C_M_RECV_LEN) which may not be implemented. + */ +s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, + u8 *values) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BLOCK_DATA, &data); + if (status) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_block_data); + +/** + * i2c_smbus_write_block_data - SMBus "block write" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @length: Size of data block; SMBus allows at most 32 bytes + * @values: Byte array which will be written. + * + * This executes the SMBus "block write" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(&data.block[1], values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_block_data); + +/* Returns the number of read bytes */ +s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + union i2c_smbus_data data; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); + if (status < 0) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); + +s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(data.block + 1, values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data); + +/* Simulate a SMBus command using the i2c protocol + No checking of parameters is done! */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + /* So we need to generate a series of msgs. In the case of writing, we + need to use only one message; when reading, we need two. We initialize + most things with sane defaults, to keep the code below somewhat + simpler. */ + unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; + unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; + int num = read_write == I2C_SMBUS_READ ? 2 : 1; + struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, + { addr, flags | I2C_M_RD, 0, msgbuf1 } + }; + int i; + u8 partial_pec = 0; + int status; + + msgbuf0[0] = command; + switch (size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write == I2C_SMBUS_READ ? + I2C_M_RD : 0); + num = 1; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + /* Special case: only a read! */ + msg[0].flags = I2C_M_RD | flags; + num = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len = 3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + } + break; + case I2C_SMBUS_PROC_CALL: + num = 2; /* Special case */ + read_write = I2C_SMBUS_READ; + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + } else { + msg[0].len = data->block[0] + 2; + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + for (i = 1; i < msg[0].len; i++) + msgbuf0[i] = data->block[i-1]; + } + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + num = 2; /* Another special case */ + read_write = I2C_SMBUS_READ; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + msg[0].len = data->block[0] + 2; + for (i = 1; i < msg[0].len; i++) + msgbuf0[i] = data->block[i-1]; + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + msg[1].len = data->block[0]; + } else { + msg[0].len = data->block[0] + 1; + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + for (i = 1; i <= data->block[0]; i++) + msgbuf0[i] = data->block[i]; + } + break; + default: + dev_err(&adapter->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK + && size != I2C_SMBUS_I2C_BLOCK_DATA); + if (i) { + /* Compute PEC if first message is a write */ + if (!(msg[0].flags & I2C_M_RD)) { + if (num == 1) /* Write only */ + i2c_smbus_add_pec(&msg[0]); + else /* Write followed by read */ + partial_pec = i2c_smbus_msg_pec(0, &msg[0]); + } + /* Ask for PEC if last message is a read */ + if (msg[num-1].flags & I2C_M_RD) + msg[num-1].len++; + } + + status = i2c_transfer(adapter, msg, num); + if (status < 0) + return status; + + /* Check PEC if last message is a read */ + if (i && (msg[num-1].flags & I2C_M_RD)) { + status = i2c_smbus_check_pec(partial_pec, &msg[num-1]); + if (status < 0) + return status; + } + + if (read_write == I2C_SMBUS_READ) + switch (size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + for (i = 0; i < data->block[0]; i++) + data->block[i+1] = msgbuf1[i]; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + for (i = 0; i < msgbuf1[0] + 1; i++) + data->block[i] = msgbuf1[i]; + break; + } + return 0; +} + +/** + * i2c_smbus_xfer - execute SMBus protocol operations + * @adapter: Handle to I2C bus + * @addr: Address of SMBus slave on that bus + * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC) + * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE + * @command: Byte interpreted by slave, for protocols which use such bytes + * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL + * @data: Data to be read or written + * + * This executes an SMBus protocol operation, and returns a negative + * errno code else zero on success. + */ +s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, + char read_write, u8 command, int protocol, + union i2c_smbus_data *data) +{ + unsigned long orig_jiffies; + int try; + s32 res; + + flags &= I2C_M_TEN | I2C_CLIENT_PEC; + + if (adapter->algo->smbus_xfer) { + i2c_lock_adapter(adapter); + + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (res = 0, try = 0; try <= adapter->retries; try++) { + res = adapter->algo->smbus_xfer(adapter, addr, flags, + read_write, command, + protocol, data); + if (res != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + adapter->timeout)) + break; + } + i2c_unlock_adapter(adapter); + } else + res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, + command, protocol, data); + + return res; +} +EXPORT_SYMBOL(i2c_smbus_xfer); + +MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C-Bus main module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h new file mode 100644 index 00000000..9f9c57ff --- /dev/null +++ b/drivers/i2c/i2c-core.h @@ -0,0 +1,33 @@ +/* + * i2c-core.h - interfaces internal to the I2C framework + * + * 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/rwsem.h> + +struct i2c_devinfo { + struct list_head list; + int busnum; + struct i2c_board_info board_info; +}; + +/* board_lock protects board_list and first_dynamic_bus_num. + * only i2c core components are allowed to use these symbols. + */ +extern struct rw_semaphore __i2c_board_lock; +extern struct list_head __i2c_board_list; +extern int __i2c_first_dynamic_bus_num; + diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c new file mode 100644 index 00000000..c90ce50b --- /dev/null +++ b/drivers/i2c/i2c-dev.c @@ -0,0 +1,656 @@ +/* + i2c-dev.c - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl> + Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.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. + + 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. +*/ + +/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. + But I have used so much of his original code and ideas that it seems + only fair to recognize him as co-author -- Frodo */ + +/* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/notifier.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/jiffies.h> +#include <linux/uaccess.h> + +/* + * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a + * slave (i2c_client) with which messages will be exchanged. It's coupled + * with a character special file which is accessed by user mode drivers. + * + * The list of i2c_dev structures is parallel to the i2c_adapter lists + * maintained by the driver model, and is updated using bus notifications. + */ +struct i2c_dev { + struct list_head list; + struct i2c_adapter *adap; + struct device *dev; +}; + +#define I2C_MINORS 256 +static LIST_HEAD(i2c_dev_list); +static DEFINE_SPINLOCK(i2c_dev_list_lock); + +static struct i2c_dev *i2c_dev_get_by_minor(unsigned index) +{ + struct i2c_dev *i2c_dev; + + spin_lock(&i2c_dev_list_lock); + list_for_each_entry(i2c_dev, &i2c_dev_list, list) { + if (i2c_dev->adap->nr == index) + goto found; + } + i2c_dev = NULL; +found: + spin_unlock(&i2c_dev_list_lock); + return i2c_dev; +} + +static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap) +{ + struct i2c_dev *i2c_dev; + + if (adap->nr >= I2C_MINORS) { + printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n", + adap->nr); + return ERR_PTR(-ENODEV); + } + + i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return ERR_PTR(-ENOMEM); + i2c_dev->adap = adap; + + spin_lock(&i2c_dev_list_lock); + list_add_tail(&i2c_dev->list, &i2c_dev_list); + spin_unlock(&i2c_dev_list_lock); + return i2c_dev; +} + +static void return_i2c_dev(struct i2c_dev *i2c_dev) +{ + spin_lock(&i2c_dev_list_lock); + list_del(&i2c_dev->list); + spin_unlock(&i2c_dev_list_lock); + kfree(i2c_dev); +} + +static ssize_t show_adapter_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt)); + + if (!i2c_dev) + return -ENODEV; + return sprintf(buf, "%s\n", i2c_dev->adap->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL); + +/* ------------------------------------------------------------------------- */ + +/* + * After opening an instance of this character special file, a file + * descriptor starts out associated only with an i2c_adapter (and bus). + * + * Using the I2C_RDWR ioctl(), you can then *immediately* issue i2c_msg + * traffic to any devices on the bus used by that adapter. That's because + * the i2c_msg vectors embed all the addressing information they need, and + * are submitted directly to an i2c_adapter. However, SMBus-only adapters + * don't support that interface. + * + * To use read()/write() system calls on that file descriptor, or to use + * SMBus interfaces (and work with SMBus-only hosts!), you must first issue + * an I2C_SLAVE (or I2C_SLAVE_FORCE) ioctl. That configures an anonymous + * (never registered) i2c_client so it holds the addressing information + * needed by those system calls and by this SMBus interface. + */ + +static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, + loff_t *offset) +{ + char *tmp; + int ret; + + struct i2c_client *client = file->private_data; + + if (count > 8192) + count = 8192; + + tmp = kmalloc(count, GFP_KERNEL); + if (tmp == NULL) + return -ENOMEM; + + pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n", + iminor(file->f_path.dentry->d_inode), count); + + ret = i2c_master_recv(client, tmp, count); + if (ret >= 0) + ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; + kfree(tmp); + return ret; +} + +static ssize_t i2cdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + int ret; + char *tmp; + struct i2c_client *client = file->private_data; + + if (count > 8192) + count = 8192; + + tmp = memdup_user(buf, count); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n", + iminor(file->f_path.dentry->d_inode), count); + + ret = i2c_master_send(client, tmp, count); + kfree(tmp); + return ret; +} + +static int i2cdev_check(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + + if (!client || client->addr != *(unsigned int *)addrp) + return 0; + + return dev->driver ? -EBUSY : 0; +} + +/* walk up mux tree */ +static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result; + + result = device_for_each_child(&adapter->dev, &addr, i2cdev_check); + if (!result && parent) + result = i2cdev_check_mux_parents(parent, addr); + + return result; +} + +/* recurse down mux tree */ +static int i2cdev_check_mux_children(struct device *dev, void *addrp) +{ + int result; + + if (dev->type == &i2c_adapter_type) + result = device_for_each_child(dev, addrp, + i2cdev_check_mux_children); + else + result = i2cdev_check(dev, addrp); + + return result; +} + +/* This address checking function differs from the one in i2c-core + in that it considers an address with a registered device, but no + driver bound to it, as NOT busy. */ +static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result = 0; + + if (parent) + result = i2cdev_check_mux_parents(parent, addr); + + if (!result) + result = device_for_each_child(&adapter->dev, &addr, + i2cdev_check_mux_children); + + return result; +} + +static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client, + unsigned long arg) +{ + struct i2c_rdwr_ioctl_data rdwr_arg; + struct i2c_msg *rdwr_pa; + u8 __user **data_ptrs; + int i, res; + + if (copy_from_user(&rdwr_arg, + (struct i2c_rdwr_ioctl_data __user *)arg, + sizeof(rdwr_arg))) + return -EFAULT; + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) + return -EINVAL; + + rdwr_pa = kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL); + if (!rdwr_pa) + return -ENOMEM; + + if (copy_from_user(rdwr_pa, rdwr_arg.msgs, + rdwr_arg.nmsgs * sizeof(struct i2c_msg))) { + kfree(rdwr_pa); + return -EFAULT; + } + + data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL); + if (data_ptrs == NULL) { + kfree(rdwr_pa); + return -ENOMEM; + } + + res = 0; + for (i = 0; i < rdwr_arg.nmsgs; i++) { + /* Limit the size of the message to a sane amount; + * and don't let length change either. */ + if ((rdwr_pa[i].len > 8192) || + (rdwr_pa[i].flags & I2C_M_RECV_LEN)) { + res = -EINVAL; + break; + } + data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; + rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len); + if (IS_ERR(rdwr_pa[i].buf)) { + res = PTR_ERR(rdwr_pa[i].buf); + break; + } + } + if (res < 0) { + int j; + for (j = 0; j < i; ++j) + kfree(rdwr_pa[j].buf); + kfree(data_ptrs); + kfree(rdwr_pa); + return res; + } + + res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs); + while (i-- > 0) { + if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) { + if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf, + rdwr_pa[i].len)) + res = -EFAULT; + } + kfree(rdwr_pa[i].buf); + } + kfree(data_ptrs); + kfree(rdwr_pa); + return res; +} + +static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, + unsigned long arg) +{ + struct i2c_smbus_ioctl_data data_arg; + union i2c_smbus_data temp; + int datasize, res; + + if (copy_from_user(&data_arg, + (struct i2c_smbus_ioctl_data __user *) arg, + sizeof(struct i2c_smbus_ioctl_data))) + return -EFAULT; + if ((data_arg.size != I2C_SMBUS_BYTE) && + (data_arg.size != I2C_SMBUS_QUICK) && + (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA) && + (data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) && + (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) && + (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) { + dev_dbg(&client->adapter->dev, + "size out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.size); + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((data_arg.read_write != I2C_SMBUS_READ) && + (data_arg.read_write != I2C_SMBUS_WRITE)) { + dev_dbg(&client->adapter->dev, + "read_write out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.read_write); + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((data_arg.size == I2C_SMBUS_QUICK) || + ((data_arg.size == I2C_SMBUS_BYTE) && + (data_arg.read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags, data_arg.read_write, + data_arg.command, data_arg.size, NULL); + + if (data_arg.data == NULL) { + dev_dbg(&client->adapter->dev, + "data is NULL pointer in ioctl I2C_SMBUS.\n"); + return -EINVAL; + } + + if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || + (data_arg.size == I2C_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == smbus block, i2c block, or block proc. call */ + datasize = sizeof(data_arg.data->block); + + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || + (data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) || + (data_arg.read_write == I2C_SMBUS_WRITE)) { + if (copy_from_user(&temp, data_arg.data, datasize)) + return -EFAULT; + } + if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) { + /* Convert old I2C block commands to the new + convention. This preserves binary compatibility. */ + data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA; + if (data_arg.read_write == I2C_SMBUS_READ) + temp.block[0] = I2C_SMBUS_BLOCK_MAX; + } + res = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + data_arg.read_write, data_arg.command, data_arg.size, &temp); + if (!res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) { + if (copy_to_user(data_arg.data, &temp, datasize)) + return -EFAULT; + } + return res; +} + +static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = file->private_data; + unsigned long funcs; + + dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", + cmd, arg); + + switch (cmd) { + case I2C_SLAVE: + case I2C_SLAVE_FORCE: + /* NOTE: devices set up to work with "new style" drivers + * can't use I2C_SLAVE, even when the device node is not + * bound to a driver. Only I2C_SLAVE_FORCE will work. + * + * Setting the PEC flag here won't affect kernel drivers, + * which will be using the i2c_client node registered with + * the driver model core. Likewise, when that client has + * the PEC flag already set, the i2c-dev driver won't see + * (or use) this setting. + */ + if ((arg > 0x3ff) || + (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg)) + return -EBUSY; + /* REVISIT: address could become busy later */ + client->addr = arg; + return 0; + case I2C_TENBIT: + if (arg) + client->flags |= I2C_M_TEN; + else + client->flags &= ~I2C_M_TEN; + return 0; + case I2C_PEC: + if (arg) + client->flags |= I2C_CLIENT_PEC; + else + client->flags &= ~I2C_CLIENT_PEC; + return 0; + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return put_user(funcs, (unsigned long __user *)arg); + + case I2C_RDWR: + return i2cdev_ioctl_rdrw(client, arg); + + case I2C_SMBUS: + return i2cdev_ioctl_smbus(client, arg); + + case I2C_RETRIES: + client->adapter->retries = arg; + break; + case I2C_TIMEOUT: + /* For historical reasons, user-space sets the timeout + * value in units of 10 ms. + */ + client->adapter->timeout = msecs_to_jiffies(arg * 10); + break; + default: + /* NOTE: returning a fault code here could cause trouble + * in buggy userspace code. Some old kernel bugs returned + * zero in this case, and userspace code might accidentally + * have depended on that bug. + */ + return -ENOTTY; + } + return 0; +} + +static int i2cdev_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct i2c_client *client; + struct i2c_adapter *adap; + struct i2c_dev *i2c_dev; + + i2c_dev = i2c_dev_get_by_minor(minor); + if (!i2c_dev) + return -ENODEV; + + adap = i2c_get_adapter(i2c_dev->adap->nr); + if (!adap) + return -ENODEV; + + /* This creates an anonymous i2c_client, which may later be + * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. + * + * This client is ** NEVER REGISTERED ** with the driver model + * or I2C core code!! It just holds private copies of addressing + * information and maybe a PEC flag. + */ + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) { + i2c_put_adapter(adap); + return -ENOMEM; + } + snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); + + client->adapter = adap; + file->private_data = client; + + return 0; +} + +static int i2cdev_release(struct inode *inode, struct file *file) +{ + struct i2c_client *client = file->private_data; + + i2c_put_adapter(client->adapter); + kfree(client); + file->private_data = NULL; + + return 0; +} + +static const struct file_operations i2cdev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = i2cdev_read, + .write = i2cdev_write, + .unlocked_ioctl = i2cdev_ioctl, + .open = i2cdev_open, + .release = i2cdev_release, +}; + +/* ------------------------------------------------------------------------- */ + +static struct class *i2c_dev_class; + +static int i2cdev_attach_adapter(struct device *dev, void *dummy) +{ + struct i2c_adapter *adap; + struct i2c_dev *i2c_dev; + int res; + + if (dev->type != &i2c_adapter_type) + return 0; + adap = to_i2c_adapter(dev); + + i2c_dev = get_free_i2c_dev(adap); + if (IS_ERR(i2c_dev)) + return PTR_ERR(i2c_dev); + + /* register this i2c device with the driver core */ + i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, + MKDEV(I2C_MAJOR, adap->nr), NULL, + "i2c-%d", adap->nr); + if (IS_ERR(i2c_dev->dev)) { + res = PTR_ERR(i2c_dev->dev); + goto error; + } + res = device_create_file(i2c_dev->dev, &dev_attr_name); + if (res) + goto error_destroy; + + pr_debug("i2c-dev: adapter [%s] registered as minor %d\n", + adap->name, adap->nr); + return 0; +error_destroy: + device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); +error: + return_i2c_dev(i2c_dev); + return res; +} + +static int i2cdev_detach_adapter(struct device *dev, void *dummy) +{ + struct i2c_adapter *adap; + struct i2c_dev *i2c_dev; + + if (dev->type != &i2c_adapter_type) + return 0; + adap = to_i2c_adapter(dev); + + i2c_dev = i2c_dev_get_by_minor(adap->nr); + if (!i2c_dev) /* attach_adapter must have failed */ + return 0; + + device_remove_file(i2c_dev->dev, &dev_attr_name); + return_i2c_dev(i2c_dev); + device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); + + pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); + return 0; +} + +int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + return i2cdev_attach_adapter(dev, NULL); + case BUS_NOTIFY_DEL_DEVICE: + return i2cdev_detach_adapter(dev, NULL); + } + + return 0; +} + +static struct notifier_block i2cdev_notifier = { + .notifier_call = i2cdev_notifier_call, +}; + +/* ------------------------------------------------------------------------- */ + +/* + * module load/unload record keeping + */ + +static int __init i2c_dev_init(void) +{ + int res; + + printk(KERN_INFO "i2c /dev entries driver\n"); + + res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); + if (res) + goto out; + + i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); + if (IS_ERR(i2c_dev_class)) { + res = PTR_ERR(i2c_dev_class); + goto out_unreg_chrdev; + } + + /* Keep track of adapters which will be added or removed later */ + res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier); + if (res) + goto out_unreg_class; + + /* Bind to already existing adapters right away */ + i2c_for_each_dev(NULL, i2cdev_attach_adapter); + + return 0; + +out_unreg_class: + class_destroy(i2c_dev_class); +out_unreg_chrdev: + unregister_chrdev(I2C_MAJOR, "i2c"); +out: + printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__); + return res; +} + +static void __exit i2c_dev_exit(void) +{ + bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier); + i2c_for_each_dev(NULL, i2cdev_detach_adapter); + class_destroy(i2c_dev_class); + unregister_chrdev(I2C_MAJOR, "i2c"); +} + +MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " + "Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C /dev entries driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_dev_init); +module_exit(i2c_dev_exit); diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c new file mode 100644 index 00000000..d7a4833b --- /dev/null +++ b/drivers/i2c/i2c-mux.c @@ -0,0 +1,164 @@ +/* + * Multiplexed I2C bus driver. + * + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it> + * Copyright (c) 2009-2010 NSN GmbH & Co KG <michael.lawnick.ext@nsn.com> + * + * Simplifies access to complex multiplexed I2C bus topologies, by presenting + * each multiplexed bus segment as an additional I2C adapter. + * Supports multi-level mux'ing (mux behind a mux). + * + * Based on: + * i2c-virt.c from Kumar Gala <galak@kernel.crashing.org> + * i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc. + * i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> + +/* multiplexer per channel data */ +struct i2c_mux_priv { + struct i2c_adapter adap; + struct i2c_algorithm algo; + + struct i2c_adapter *parent; + void *mux_dev; /* the mux chip/device */ + u32 chan_id; /* the channel id */ + + int (*select)(struct i2c_adapter *, void *mux_dev, u32 chan_id); + int (*deselect)(struct i2c_adapter *, void *mux_dev, u32 chan_id); +}; + +static int i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->parent; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + + ret = priv->select(parent, priv->mux_dev, priv->chan_id); + if (ret >= 0) + ret = parent->algo->master_xfer(parent, msgs, num); + if (priv->deselect) + priv->deselect(parent, priv->mux_dev, priv->chan_id); + + return ret; +} + +static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, + u16 addr, unsigned short flags, + char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->parent; + int ret; + + /* Select the right mux port and perform the transfer. */ + + ret = priv->select(parent, priv->mux_dev, priv->chan_id); + if (ret >= 0) + ret = parent->algo->smbus_xfer(parent, addr, flags, + read_write, command, size, data); + if (priv->deselect) + priv->deselect(parent, priv->mux_dev, priv->chan_id); + + return ret; +} + +/* Return the parent's functionality */ +static u32 i2c_mux_functionality(struct i2c_adapter *adap) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_adapter *parent = priv->parent; + + return parent->algo->functionality(parent); +} + +struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, + void *mux_dev, u32 force_nr, u32 chan_id, + int (*select) (struct i2c_adapter *, + void *, u32), + int (*deselect) (struct i2c_adapter *, + void *, u32)) +{ + struct i2c_mux_priv *priv; + int ret; + + priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL); + if (!priv) + return NULL; + + /* Set up private adapter data */ + priv->parent = parent; + priv->mux_dev = mux_dev; + priv->chan_id = chan_id; + priv->select = select; + priv->deselect = deselect; + + /* Need to do algo dynamically because we don't know ahead + * of time what sort of physical adapter we'll be dealing with. + */ + if (parent->algo->master_xfer) + priv->algo.master_xfer = i2c_mux_master_xfer; + if (parent->algo->smbus_xfer) + priv->algo.smbus_xfer = i2c_mux_smbus_xfer; + priv->algo.functionality = i2c_mux_functionality; + + /* Now fill out new adapter structure */ + snprintf(priv->adap.name, sizeof(priv->adap.name), + "i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id); + priv->adap.owner = THIS_MODULE; + priv->adap.algo = &priv->algo; + priv->adap.algo_data = priv; + priv->adap.dev.parent = &parent->dev; + + if (force_nr) { + priv->adap.nr = force_nr; + ret = i2c_add_numbered_adapter(&priv->adap); + } else { + ret = i2c_add_adapter(&priv->adap); + } + if (ret < 0) { + dev_err(&parent->dev, + "failed to add mux-adapter (error=%d)\n", + ret); + kfree(priv); + return NULL; + } + + dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", + i2c_adapter_id(&priv->adap)); + + return &priv->adap; +} +EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); + +int i2c_del_mux_adapter(struct i2c_adapter *adap) +{ + struct i2c_mux_priv *priv = adap->algo_data; + int ret; + + ret = i2c_del_adapter(adap); + if (ret < 0) + return ret; + kfree(priv); + + return 0; +} +EXPORT_SYMBOL_GPL(i2c_del_mux_adapter); + +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); +MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c new file mode 100644 index 00000000..f61ccc1e --- /dev/null +++ b/drivers/i2c/i2c-smbus.c @@ -0,0 +1,262 @@ +/* + * i2c-smbus.c - SMBus extensions to the I2C protocol + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2010 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/module.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/i2c.h> +#include <linux/i2c-smbus.h> +#include <linux/slab.h> + +struct i2c_smbus_alert { + unsigned int alert_edge_triggered:1; + int irq; + struct work_struct alert; + struct i2c_client *ara; /* Alert response address */ +}; + +struct alert_data { + unsigned short addr; + u8 flag:1; +}; + +/* If this is the alerting device, notify its driver */ +static int smbus_do_alert(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct alert_data *data = addrp; + + if (!client || client->addr != data->addr) + return 0; + if (client->flags & I2C_CLIENT_TEN) + return 0; + + /* + * Drivers should either disable alerts, or provide at least + * a minimal handler. Lock so client->driver won't change. + */ + device_lock(dev); + if (client->driver) { + if (client->driver->alert) + client->driver->alert(client, data->flag); + else + dev_warn(&client->dev, "no driver alert()!\n"); + } else + dev_dbg(&client->dev, "alert with no driver\n"); + device_unlock(dev); + + /* Stop iterating after we find the device */ + return -EBUSY; +} + +/* + * The alert IRQ handler needs to hand work off to a task which can issue + * SMBus calls, because those sleeping calls can't be made in IRQ context. + */ +static void smbus_alert(struct work_struct *work) +{ + struct i2c_smbus_alert *alert; + struct i2c_client *ara; + unsigned short prev_addr = 0; /* Not a valid address */ + + alert = container_of(work, struct i2c_smbus_alert, alert); + ara = alert->ara; + + for (;;) { + s32 status; + struct alert_data data; + + /* + * Devices with pending alerts reply in address order, low + * to high, because of slave transmit arbitration. After + * responding, an SMBus device stops asserting SMBALERT#. + * + * Note that SMBus 2.0 reserves 10-bit addresess for future + * use. We neither handle them, nor try to use PEC here. + */ + status = i2c_smbus_read_byte(ara); + if (status < 0) + break; + + data.flag = status & 1; + data.addr = status >> 1; + + if (data.addr == prev_addr) { + dev_warn(&ara->dev, "Duplicate SMBALERT# from dev " + "0x%02x, skipping\n", data.addr); + break; + } + dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n", + data.addr, data.flag); + + /* Notify driver for the device which issued the alert */ + device_for_each_child(&ara->adapter->dev, &data, + smbus_do_alert); + prev_addr = data.addr; + } + + /* We handled all alerts; re-enable level-triggered IRQs */ + if (!alert->alert_edge_triggered) + enable_irq(alert->irq); +} + +static irqreturn_t smbalert_irq(int irq, void *d) +{ + struct i2c_smbus_alert *alert = d; + + /* Disable level-triggered IRQs until we handle them */ + if (!alert->alert_edge_triggered) + disable_irq_nosync(irq); + + schedule_work(&alert->alert); + return IRQ_HANDLED; +} + +/* Setup SMBALERT# infrastructure */ +static int smbalert_probe(struct i2c_client *ara, + const struct i2c_device_id *id) +{ + struct i2c_smbus_alert_setup *setup = ara->dev.platform_data; + struct i2c_smbus_alert *alert; + struct i2c_adapter *adapter = ara->adapter; + int res; + + alert = kzalloc(sizeof(struct i2c_smbus_alert), GFP_KERNEL); + if (!alert) + return -ENOMEM; + + alert->alert_edge_triggered = setup->alert_edge_triggered; + alert->irq = setup->irq; + INIT_WORK(&alert->alert, smbus_alert); + alert->ara = ara; + + if (setup->irq > 0) { + res = devm_request_irq(&ara->dev, setup->irq, smbalert_irq, + 0, "smbus_alert", alert); + if (res) { + kfree(alert); + return res; + } + } + + i2c_set_clientdata(ara, alert); + dev_info(&adapter->dev, "supports SMBALERT#, %s trigger\n", + setup->alert_edge_triggered ? "edge" : "level"); + + return 0; +} + +/* IRQ resource is managed so it is freed automatically */ +static int smbalert_remove(struct i2c_client *ara) +{ + struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); + + cancel_work_sync(&alert->alert); + + kfree(alert); + return 0; +} + +static const struct i2c_device_id smbalert_ids[] = { + { "smbus_alert", 0 }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, smbalert_ids); + +static struct i2c_driver smbalert_driver = { + .driver = { + .name = "smbus_alert", + }, + .probe = smbalert_probe, + .remove = smbalert_remove, + .id_table = smbalert_ids, +}; + +/** + * i2c_setup_smbus_alert - Setup SMBus alert support + * @adapter: the target adapter + * @setup: setup data for the SMBus alert handler + * Context: can sleep + * + * Setup handling of the SMBus alert protocol on a given I2C bus segment. + * + * Handling can be done either through our IRQ handler, or by the + * adapter (from its handler, periodic polling, or whatever). + * + * NOTE that if we manage the IRQ, we *MUST* know if it's level or + * edge triggered in order to hand it to the workqueue correctly. + * If triggering the alert seems to wedge the system, you probably + * should have said it's level triggered. + * + * This returns the ara client, which should be saved for later use with + * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL + * to indicate an error. + */ +struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, + struct i2c_smbus_alert_setup *setup) +{ + struct i2c_board_info ara_board_info = { + I2C_BOARD_INFO("smbus_alert", 0x0c), + .platform_data = setup, + }; + + return i2c_new_device(adapter, &ara_board_info); +} +EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert); + +/** + * i2c_handle_smbus_alert - Handle an SMBus alert + * @ara: the ARA client on the relevant adapter + * Context: can't sleep + * + * Helper function to be called from an I2C bus driver's interrupt + * handler. It will schedule the alert work, in turn calling the + * corresponding I2C device driver's alert function. + * + * It is assumed that ara is a valid i2c client previously returned by + * i2c_setup_smbus_alert(). + */ +int i2c_handle_smbus_alert(struct i2c_client *ara) +{ + struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); + + return schedule_work(&alert->alert); +} +EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); + +static int __init i2c_smbus_init(void) +{ + return i2c_add_driver(&smbalert_driver); +} + +static void __exit i2c_smbus_exit(void) +{ + i2c_del_driver(&smbalert_driver); +} + +module_init(i2c_smbus_init); +module_exit(i2c_smbus_exit); + +MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("SMBus protocol extensions support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig new file mode 100644 index 00000000..90b7a016 --- /dev/null +++ b/drivers/i2c/muxes/Kconfig @@ -0,0 +1,40 @@ +# +# Multiplexer I2C chip drivers configuration +# + +menu "Multiplexer I2C Chip support" + depends on I2C_MUX + +config I2C_MUX_GPIO + tristate "GPIO-based I2C multiplexer" + depends on GENERIC_GPIO + help + If you say yes to this option, support will be included for a + GPIO based I2C multiplexer. This driver provides access to + I2C busses connected through a MUX, which is controlled + through GPIO pins. + + This driver can also be built as a module. If so, the module + will be called gpio-i2cmux. + +config I2C_MUX_PCA9541 + tristate "NXP PCA9541 I2C Master Selector" + depends on EXPERIMENTAL + help + If you say yes here you get support for the NXP PCA9541 + I2C Master Selector. + + This driver can also be built as a module. If so, the module + will be called pca9541. + +config I2C_MUX_PCA954x + tristate "Philips PCA954x I2C Mux/switches" + depends on EXPERIMENTAL + help + If you say yes here you get support for the Philips PCA954x + I2C mux/switch devices. + + This driver can also be built as a module. If so, the module + will be called pca954x. + +endmenu diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile new file mode 100644 index 00000000..4640436e --- /dev/null +++ b/drivers/i2c/muxes/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for multiplexer I2C chip drivers. + +obj-$(CONFIG_I2C_MUX_GPIO) += gpio-i2cmux.o +obj-$(CONFIG_I2C_MUX_PCA9541) += pca9541.o +obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o + +ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/muxes/gpio-i2cmux.c b/drivers/i2c/muxes/gpio-i2cmux.c new file mode 100644 index 00000000..7b6ce624 --- /dev/null +++ b/drivers/i2c/muxes/gpio-i2cmux.c @@ -0,0 +1,184 @@ +/* + * I2C multiplexer using GPIO API + * + * Peter Korsgaard <peter.korsgaard@barco.com> + * + * 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. + */ + +#include <linux/i2c.h> +#include <linux/i2c-mux.h> +#include <linux/gpio-i2cmux.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/gpio.h> + +struct gpiomux { + struct i2c_adapter *parent; + struct i2c_adapter **adap; /* child busses */ + struct gpio_i2cmux_platform_data data; +}; + +static void gpiomux_set(const struct gpiomux *mux, unsigned val) +{ + int i; + + for (i = 0; i < mux->data.n_gpios; i++) + gpio_set_value(mux->data.gpios[i], val & (1 << i)); +} + +static int gpiomux_select(struct i2c_adapter *adap, void *data, u32 chan) +{ + struct gpiomux *mux = data; + + gpiomux_set(mux, mux->data.values[chan]); + + return 0; +} + +static int gpiomux_deselect(struct i2c_adapter *adap, void *data, u32 chan) +{ + struct gpiomux *mux = data; + + gpiomux_set(mux, mux->data.idle); + + return 0; +} + +static int __devinit gpiomux_probe(struct platform_device *pdev) +{ + struct gpiomux *mux; + struct gpio_i2cmux_platform_data *pdata; + struct i2c_adapter *parent; + int (*deselect) (struct i2c_adapter *, void *, u32); + unsigned initial_state; + int i, ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + + parent = i2c_get_adapter(pdata->parent); + if (!parent) { + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", + pdata->parent); + return -ENODEV; + } + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) { + ret = -ENOMEM; + goto alloc_failed; + } + + mux->parent = parent; + mux->data = *pdata; + mux->adap = kzalloc(sizeof(struct i2c_adapter *) * pdata->n_values, + GFP_KERNEL); + if (!mux->adap) { + ret = -ENOMEM; + goto alloc_failed2; + } + + if (pdata->idle != GPIO_I2CMUX_NO_IDLE) { + initial_state = pdata->idle; + deselect = gpiomux_deselect; + } else { + initial_state = pdata->values[0]; + deselect = NULL; + } + + for (i = 0; i < pdata->n_gpios; i++) { + ret = gpio_request(pdata->gpios[i], "gpio-i2cmux"); + if (ret) + goto err_request_gpio; + gpio_direction_output(pdata->gpios[i], + initial_state & (1 << i)); + } + + for (i = 0; i < pdata->n_values; i++) { + u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0; + + mux->adap[i] = i2c_add_mux_adapter(parent, mux, nr, i, + gpiomux_select, deselect); + if (!mux->adap[i]) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to add adapter %d\n", i); + goto add_adapter_failed; + } + } + + dev_info(&pdev->dev, "%d port mux on %s adapter\n", + pdata->n_values, parent->name); + + platform_set_drvdata(pdev, mux); + + return 0; + +add_adapter_failed: + for (; i > 0; i--) + i2c_del_mux_adapter(mux->adap[i - 1]); + i = pdata->n_gpios; +err_request_gpio: + for (; i > 0; i--) + gpio_free(pdata->gpios[i - 1]); + kfree(mux->adap); +alloc_failed2: + kfree(mux); +alloc_failed: + i2c_put_adapter(parent); + + return ret; +} + +static int __devexit gpiomux_remove(struct platform_device *pdev) +{ + struct gpiomux *mux = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < mux->data.n_values; i++) + i2c_del_mux_adapter(mux->adap[i]); + + for (i = 0; i < mux->data.n_gpios; i++) + gpio_free(mux->data.gpios[i]); + + platform_set_drvdata(pdev, NULL); + i2c_put_adapter(mux->parent); + kfree(mux->adap); + kfree(mux); + + return 0; +} + +static struct platform_driver gpiomux_driver = { + .probe = gpiomux_probe, + .remove = __devexit_p(gpiomux_remove), + .driver = { + .owner = THIS_MODULE, + .name = "gpio-i2cmux", + }, +}; + +static int __init gpiomux_init(void) +{ + return platform_driver_register(&gpiomux_driver); +} + +static void __exit gpiomux_exit(void) +{ + platform_driver_unregister(&gpiomux_driver); +} + +module_init(gpiomux_init); +module_exit(gpiomux_exit); + +MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver"); +MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@barco.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-i2cmux"); diff --git a/drivers/i2c/muxes/pca9541.c b/drivers/i2c/muxes/pca9541.c new file mode 100644 index 00000000..ed699c5a --- /dev/null +++ b/drivers/i2c/muxes/pca9541.c @@ -0,0 +1,411 @@ +/* + * I2C multiplexer driver for PCA9541 bus master selector + * + * Copyright (c) 2010 Ericsson AB. + * + * Author: Guenter Roeck <guenter.roeck@ericsson.com> + * + * Derived from: + * pca954x.c + * + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> + +#include <linux/i2c/pca954x.h> + +/* + * The PCA9541 is a bus master selector. It supports two I2C masters connected + * to a single slave bus. + * + * Before each bus transaction, a master has to acquire bus ownership. After the + * transaction is complete, bus ownership has to be released. This fits well + * into the I2C multiplexer framework, which provides select and release + * functions for this purpose. For this reason, this driver is modeled as + * single-channel I2C bus multiplexer. + * + * This driver assumes that the two bus masters are controlled by two different + * hosts. If a single host controls both masters, platform code has to ensure + * that only one of the masters is instantiated at any given time. + */ + +#define PCA9541_CONTROL 0x01 +#define PCA9541_ISTAT 0x02 + +#define PCA9541_CTL_MYBUS (1 << 0) +#define PCA9541_CTL_NMYBUS (1 << 1) +#define PCA9541_CTL_BUSON (1 << 2) +#define PCA9541_CTL_NBUSON (1 << 3) +#define PCA9541_CTL_BUSINIT (1 << 4) +#define PCA9541_CTL_TESTON (1 << 6) +#define PCA9541_CTL_NTESTON (1 << 7) + +#define PCA9541_ISTAT_INTIN (1 << 0) +#define PCA9541_ISTAT_BUSINIT (1 << 1) +#define PCA9541_ISTAT_BUSOK (1 << 2) +#define PCA9541_ISTAT_BUSLOST (1 << 3) +#define PCA9541_ISTAT_MYTEST (1 << 6) +#define PCA9541_ISTAT_NMYTEST (1 << 7) + +#define BUSON (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON) +#define MYBUS (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS) +#define mybus(x) (!((x) & MYBUS) || ((x) & MYBUS) == MYBUS) +#define busoff(x) (!((x) & BUSON) || ((x) & BUSON) == BUSON) + +/* arbitration timeouts, in jiffies */ +#define ARB_TIMEOUT (HZ / 8) /* 125 ms until forcing bus ownership */ +#define ARB2_TIMEOUT (HZ / 4) /* 250 ms until acquisition failure */ + +/* arbitration retry delays, in us */ +#define SELECT_DELAY_SHORT 50 +#define SELECT_DELAY_LONG 1000 + +struct pca9541 { + struct i2c_adapter *mux_adap; + unsigned long select_timeout; + unsigned long arb_timeout; +}; + +static const struct i2c_device_id pca9541_id[] = { + {"pca9541", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pca9541_id); + +/* + * Write to chip register. Don't use i2c_transfer()/i2c_smbus_xfer() + * as they will try to lock the adapter a second time. + */ +static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val) +{ + struct i2c_adapter *adap = client->adapter; + int ret; + + if (adap->algo->master_xfer) { + struct i2c_msg msg; + char buf[2]; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + buf[0] = command; + buf[1] = val; + msg.buf = buf; + ret = adap->algo->master_xfer(adap, &msg, 1); + } else { + union i2c_smbus_data data; + + data.byte = val; + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_WRITE, + command, + I2C_SMBUS_BYTE_DATA, &data); + } + + return ret; +} + +/* + * Read from chip register. Don't use i2c_transfer()/i2c_smbus_xfer() + * as they will try to lock adapter a second time. + */ +static int pca9541_reg_read(struct i2c_client *client, u8 command) +{ + struct i2c_adapter *adap = client->adapter; + int ret; + u8 val; + + if (adap->algo->master_xfer) { + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &command + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &val + } + }; + ret = adap->algo->master_xfer(adap, msg, 2); + if (ret == 2) + ret = val; + else if (ret >= 0) + ret = -EIO; + } else { + union i2c_smbus_data data; + + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_READ, + command, + I2C_SMBUS_BYTE_DATA, &data); + if (!ret) + ret = data.byte; + } + return ret; +} + +/* + * Arbitration management functions + */ + +/* Release bus. Also reset NTESTON and BUSINIT if it was set. */ +static void pca9541_release_bus(struct i2c_client *client) +{ + int reg; + + reg = pca9541_reg_read(client, PCA9541_CONTROL); + if (reg >= 0 && !busoff(reg) && mybus(reg)) + pca9541_reg_write(client, PCA9541_CONTROL, + (reg & PCA9541_CTL_NBUSON) >> 1); +} + +/* + * Arbitration is defined as a two-step process. A bus master can only activate + * the slave bus if it owns it; otherwise it has to request ownership first. + * This multi-step process ensures that access contention is resolved + * gracefully. + * + * Bus Ownership Other master Action + * state requested access + * ---------------------------------------------------- + * off - yes wait for arbitration timeout or + * for other master to drop request + * off no no take ownership + * off yes no turn on bus + * on yes - done + * on no - wait for arbitration timeout or + * for other master to release bus + * + * The main contention point occurs if the slave bus is off and both masters + * request ownership at the same time. In this case, one master will turn on + * the slave bus, believing that it owns it. The other master will request + * bus ownership. Result is that the bus is turned on, and master which did + * _not_ own the slave bus before ends up owning it. + */ + +/* Control commands per PCA9541 datasheet */ +static const u8 pca9541_control[16] = { + 4, 0, 1, 5, 4, 4, 5, 5, 0, 0, 1, 1, 0, 4, 5, 1 +}; + +/* + * Channel arbitration + * + * Return values: + * <0: error + * 0 : bus not acquired + * 1 : bus acquired + */ +static int pca9541_arbitrate(struct i2c_client *client) +{ + struct pca9541 *data = i2c_get_clientdata(client); + int reg; + + reg = pca9541_reg_read(client, PCA9541_CONTROL); + if (reg < 0) + return reg; + + if (busoff(reg)) { + int istat; + /* + * Bus is off. Request ownership or turn it on unless + * other master requested ownership. + */ + istat = pca9541_reg_read(client, PCA9541_ISTAT); + if (!(istat & PCA9541_ISTAT_NMYTEST) + || time_is_before_eq_jiffies(data->arb_timeout)) { + /* + * Other master did not request ownership, + * or arbitration timeout expired. Take the bus. + */ + pca9541_reg_write(client, + PCA9541_CONTROL, + pca9541_control[reg & 0x0f] + | PCA9541_CTL_NTESTON); + data->select_timeout = SELECT_DELAY_SHORT; + } else { + /* + * Other master requested ownership. + * Set extra long timeout to give it time to acquire it. + */ + data->select_timeout = SELECT_DELAY_LONG * 2; + } + } else if (mybus(reg)) { + /* + * Bus is on, and we own it. We are done with acquisition. + * Reset NTESTON and BUSINIT, then return success. + */ + if (reg & (PCA9541_CTL_NTESTON | PCA9541_CTL_BUSINIT)) + pca9541_reg_write(client, + PCA9541_CONTROL, + reg & ~(PCA9541_CTL_NTESTON + | PCA9541_CTL_BUSINIT)); + return 1; + } else { + /* + * Other master owns the bus. + * If arbitration timeout has expired, force ownership. + * Otherwise request it. + */ + data->select_timeout = SELECT_DELAY_LONG; + if (time_is_before_eq_jiffies(data->arb_timeout)) { + /* Time is up, take the bus and reset it. */ + pca9541_reg_write(client, + PCA9541_CONTROL, + pca9541_control[reg & 0x0f] + | PCA9541_CTL_BUSINIT + | PCA9541_CTL_NTESTON); + } else { + /* Request bus ownership if needed */ + if (!(reg & PCA9541_CTL_NTESTON)) + pca9541_reg_write(client, + PCA9541_CONTROL, + reg | PCA9541_CTL_NTESTON); + } + } + return 0; +} + +static int pca9541_select_chan(struct i2c_adapter *adap, void *client, u32 chan) +{ + struct pca9541 *data = i2c_get_clientdata(client); + int ret; + unsigned long timeout = jiffies + ARB2_TIMEOUT; + /* give up after this time */ + + data->arb_timeout = jiffies + ARB_TIMEOUT; + /* force bus ownership after this time */ + + do { + ret = pca9541_arbitrate(client); + if (ret) + return ret < 0 ? ret : 0; + + if (data->select_timeout == SELECT_DELAY_SHORT) + udelay(data->select_timeout); + else + msleep(data->select_timeout / 1000); + } while (time_is_after_eq_jiffies(timeout)); + + return -ETIMEDOUT; +} + +static int pca9541_release_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + pca9541_release_bus(client); + return 0; +} + +/* + * I2C init/probing/exit functions + */ +static int pca9541_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = client->adapter; + struct pca954x_platform_data *pdata = client->dev.platform_data; + struct pca9541 *data; + int force; + int ret = -ENODEV; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) + goto err; + + data = kzalloc(sizeof(struct pca9541), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + + i2c_set_clientdata(client, data); + + /* + * I2C accesses are unprotected here. + * We have to lock the adapter before releasing the bus. + */ + i2c_lock_adapter(adap); + pca9541_release_bus(client); + i2c_unlock_adapter(adap); + + /* Create mux adapter */ + + force = 0; + if (pdata) + force = pdata->modes[0].adap_id; + data->mux_adap = i2c_add_mux_adapter(adap, client, force, 0, + pca9541_select_chan, + pca9541_release_chan); + + if (data->mux_adap == NULL) { + dev_err(&client->dev, "failed to register master selector\n"); + goto exit_free; + } + + dev_info(&client->dev, "registered master selector for I2C %s\n", + client->name); + + return 0; + +exit_free: + kfree(data); +err: + return ret; +} + +static int pca9541_remove(struct i2c_client *client) +{ + struct pca9541 *data = i2c_get_clientdata(client); + + i2c_del_mux_adapter(data->mux_adap); + + kfree(data); + return 0; +} + +static struct i2c_driver pca9541_driver = { + .driver = { + .name = "pca9541", + .owner = THIS_MODULE, + }, + .probe = pca9541_probe, + .remove = pca9541_remove, + .id_table = pca9541_id, +}; + +static int __init pca9541_init(void) +{ + return i2c_add_driver(&pca9541_driver); +} + +static void __exit pca9541_exit(void) +{ + i2c_del_driver(&pca9541_driver); +} + +module_init(pca9541_init); +module_exit(pca9541_exit); + +MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); +MODULE_DESCRIPTION("PCA9541 I2C master selector driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c new file mode 100644 index 00000000..6f895366 --- /dev/null +++ b/drivers/i2c/muxes/pca954x.c @@ -0,0 +1,302 @@ +/* + * I2C multiplexer + * + * Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it> + * + * This module supports the PCA954x series of I2C multiplexer/switch chips + * made by Philips Semiconductors. + * This includes the: + * PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547 + * and PCA9548. + * + * These chips are all controlled via the I2C bus itself, and all have a + * single 8-bit register. The upstream "parent" bus fans out to two, + * four, or eight downstream busses or channels; which of these + * are selected is determined by the chip type and register contents. A + * mux can select only one sub-bus at a time; a switch can select any + * combination simultaneously. + * + * Based on: + * pca954x.c from Kumar Gala <galak@kernel.crashing.org> + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com> + * and + * pca9540.c from Jean Delvare <khali@linux-fr.org>. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/i2c-mux.h> + +#include <linux/i2c/pca954x.h> + +#define PCA954X_MAX_NCHANS 8 + +enum pca_type { + pca_9540, + pca_9542, + pca_9543, + pca_9544, + pca_9545, + pca_9546, + pca_9547, + pca_9548, +}; + +struct pca954x { + enum pca_type type; + struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS]; + + u8 last_chan; /* last register value */ +}; + +struct chip_desc { + u8 nchans; + u8 enable; /* used for muxes only */ + enum muxtype { + pca954x_ismux = 0, + pca954x_isswi + } muxtype; +}; + +/* Provide specs for the PCA954x types we know about */ +static const struct chip_desc chips[] = { + [pca_9540] = { + .nchans = 2, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9543] = { + .nchans = 2, + .muxtype = pca954x_isswi, + }, + [pca_9544] = { + .nchans = 4, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9545] = { + .nchans = 4, + .muxtype = pca954x_isswi, + }, + [pca_9547] = { + .nchans = 8, + .enable = 0x8, + .muxtype = pca954x_ismux, + }, + [pca_9548] = { + .nchans = 8, + .muxtype = pca954x_isswi, + }, +}; + +static const struct i2c_device_id pca954x_id[] = { + { "pca9540", pca_9540 }, + { "pca9542", pca_9540 }, + { "pca9543", pca_9543 }, + { "pca9544", pca_9544 }, + { "pca9545", pca_9545 }, + { "pca9546", pca_9545 }, + { "pca9547", pca_9547 }, + { "pca9548", pca_9548 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca954x_id); + +/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() + for this as they will try to lock adapter a second time */ +static int pca954x_reg_write(struct i2c_adapter *adap, + struct i2c_client *client, u8 val) +{ + int ret = -ENODEV; + + if (adap->algo->master_xfer) { + struct i2c_msg msg; + char buf[1]; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + buf[0] = val; + msg.buf = buf; + ret = adap->algo->master_xfer(adap, &msg, 1); + } else { + union i2c_smbus_data data; + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_WRITE, + val, I2C_SMBUS_BYTE, &data); + } + + return ret; +} + +static int pca954x_select_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + const struct chip_desc *chip = &chips[data->type]; + u8 regval; + int ret = 0; + + /* we make switches look like muxes, not sure how to be smarter */ + if (chip->muxtype == pca954x_ismux) + regval = chan | chip->enable; + else + regval = 1 << chan; + + /* Only select the channel if its different from the last channel */ + if (data->last_chan != regval) { + ret = pca954x_reg_write(adap, client, regval); + data->last_chan = regval; + } + + return ret; +} + +static int pca954x_deselect_mux(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + + /* Deselect active channel */ + data->last_chan = 0; + return pca954x_reg_write(adap, client, data->last_chan); +} + +/* + * I2C init/probing/exit functions + */ +static int pca954x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + struct pca954x_platform_data *pdata = client->dev.platform_data; + int num, force; + struct pca954x *data; + int ret = -ENODEV; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + goto err; + + data = kzalloc(sizeof(struct pca954x), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + + i2c_set_clientdata(client, data); + + /* Write the mux register at addr to verify + * that the mux is in fact present. This also + * initializes the mux to disconnected state. + */ + if (i2c_smbus_write_byte(client, 0) < 0) { + dev_warn(&client->dev, "probe failed\n"); + goto exit_free; + } + + data->type = id->driver_data; + data->last_chan = 0; /* force the first selection */ + + /* Now create an adapter for each channel */ + for (num = 0; num < chips[data->type].nchans; num++) { + force = 0; /* dynamic adap number */ + if (pdata) { + if (num < pdata->num_modes) + /* force static number */ + force = pdata->modes[num].adap_id; + else + /* discard unconfigured channels */ + break; + } + + data->virt_adaps[num] = + i2c_add_mux_adapter(adap, client, + force, num, pca954x_select_chan, + (pdata && pdata->modes[num].deselect_on_exit) + ? pca954x_deselect_mux : NULL); + + if (data->virt_adaps[num] == NULL) { + ret = -ENODEV; + dev_err(&client->dev, + "failed to register multiplexed adapter" + " %d as bus %d\n", num, force); + goto virt_reg_failed; + } + } + + dev_info(&client->dev, + "registered %d multiplexed busses for I2C %s %s\n", + num, chips[data->type].muxtype == pca954x_ismux + ? "mux" : "switch", client->name); + + return 0; + +virt_reg_failed: + for (num--; num >= 0; num--) + i2c_del_mux_adapter(data->virt_adaps[num]); +exit_free: + kfree(data); +err: + return ret; +} + +static int pca954x_remove(struct i2c_client *client) +{ + struct pca954x *data = i2c_get_clientdata(client); + const struct chip_desc *chip = &chips[data->type]; + int i, err; + + for (i = 0; i < chip->nchans; ++i) + if (data->virt_adaps[i]) { + err = i2c_del_mux_adapter(data->virt_adaps[i]); + if (err) + return err; + data->virt_adaps[i] = NULL; + } + + kfree(data); + return 0; +} + +static struct i2c_driver pca954x_driver = { + .driver = { + .name = "pca954x", + .owner = THIS_MODULE, + }, + .probe = pca954x_probe, + .remove = pca954x_remove, + .id_table = pca954x_id, +}; + +static int __init pca954x_init(void) +{ + return i2c_add_driver(&pca954x_driver); +} + +static void __exit pca954x_exit(void) +{ + i2c_del_driver(&pca954x_driver); +} + +module_init(pca954x_init); +module_exit(pca954x_exit); + +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); +MODULE_LICENSE("GPL v2"); |