From a8a9542a71cb2c7befeb04a3da2a76f8bb5c4bbe Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 14 Oct 2012 19:49:04 +0800 Subject: [PATCH 01/12] import include/linux/math64.h need by mtd_dataflash Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- include/linux/math64.h | 121 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 include/linux/math64.h diff --git a/include/linux/math64.h b/include/linux/math64.h new file mode 100644 index 000000000..71dd6d710 --- /dev/null +++ b/include/linux/math64.h @@ -0,0 +1,121 @@ +#ifndef _LINUX_MATH64_H +#define _LINUX_MATH64_H + +#include +#include + +#if BITS_PER_LONG == 64 + +#define div64_long(x,y) div64_s64((x),(y)) + +/** + * div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder + * + * This is commonly provided by 32bit archs to provide an optimized 64bit + * divide. + */ +static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + +/** + * div_s64_rem - signed 64bit divide with 32bit divisor with remainder + */ +static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + +/** + * div64_u64 - unsigned 64bit divide with 64bit divisor + */ +static inline u64 div64_u64(u64 dividend, u64 divisor) +{ + return dividend / divisor; +} + +/** + * div64_s64 - signed 64bit divide with 64bit divisor + */ +static inline s64 div64_s64(s64 dividend, s64 divisor) +{ + return dividend / divisor; +} + +#elif BITS_PER_LONG == 32 + +#define div64_long(x,y) div_s64((x),(y)) + +#ifndef div_u64_rem +static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) +{ + *remainder = do_div(dividend, divisor); + return dividend; +} +#endif + +#ifndef div_s64_rem +extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); +#endif + +#ifndef div64_u64 +extern u64 div64_u64(u64 dividend, u64 divisor); +#endif + +#ifndef div64_s64 +extern s64 div64_s64(s64 dividend, s64 divisor); +#endif + +#endif /* BITS_PER_LONG */ + +/** + * div_u64 - unsigned 64bit divide with 32bit divisor + * + * This is the most common 64bit divide and should be used if possible, + * as many 32bit archs can optimize this variant better than a full 64bit + * divide. + */ +#ifndef div_u64 +static inline u64 div_u64(u64 dividend, u32 divisor) +{ + u32 remainder; + return div_u64_rem(dividend, divisor, &remainder); +} +#endif + +/** + * div_s64 - signed 64bit divide with 32bit divisor + */ +#ifndef div_s64 +static inline s64 div_s64(s64 dividend, s32 divisor) +{ + s32 remainder; + return div_s64_rem(dividend, divisor, &remainder); +} +#endif + +u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder); + +static __always_inline u32 +__iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder) +{ + u32 ret = 0; + + while (dividend >= divisor) { + /* The following asm() prevents the compiler from + optimising this loop into a modulo operation. */ + asm("" : "+rm"(dividend)); + + dividend -= divisor; + ret++; + } + + *remainder = dividend; + + return ret; +} + +#endif /* _LINUX_MATH64_H */ From 0e9a078d204d9404b4eb57db11f89d57eb961f00 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 2 Nov 2012 10:36:49 +0100 Subject: [PATCH 02/12] i2c: adapter: register it's own device so we can show the this of i2c busses set the bus device as parent of all devices. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/i2c/busses/i2c-imx.c | 30 +++++++++++++++--------------- drivers/i2c/busses/i2c-omap.c | 30 +++++++++++++++--------------- drivers/i2c/i2c.c | 17 ++++++++++++++++- include/i2c/i2c.h | 2 +- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index a1012a784..68348eb15 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -154,7 +154,7 @@ static int i2c_fsl_bus_busy(struct i2c_adapter *adapter, int for_busy) if (!for_busy && !(temp & I2SR_IBB)) break; if (is_timeout(start, 500 * MSECOND)) { - dev_err(adapter->dev, + dev_err(&adapter->dev, "<%s> timeout waiting for I2C bus %s\n", __func__,for_busy ? "busy" : "not busy"); return -EIO; @@ -177,7 +177,7 @@ static int i2c_fsl_trx_complete(struct i2c_adapter *adapter) break; if (is_timeout(start, 100 * MSECOND)) { - dev_err(adapter->dev, "<%s> TXR timeout\n", __func__); + dev_err(&adapter->dev, "<%s> TXR timeout\n", __func__); return -EIO; } } @@ -199,7 +199,7 @@ static int i2c_fsl_acked(struct i2c_adapter *adapter) break; if (is_timeout(start, MSECOND)) { - dev_dbg(adapter->dev, "<%s> No ACK\n", __func__); + dev_dbg(&adapter->dev, "<%s> No ACK\n", __func__); return -EIO; } } @@ -296,7 +296,7 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, * Translate to dfsr = 5 * Frequency / 100,000,000 */ dfsr = (5 * (i2c_clk / 1000)) / 100000; - dev_dbg(i2c_fsl->adapter.dev, + dev_dbg(&i2c_fsl->adapter.dev, "<%s> requested speed:%d, i2c_clk:%d\n", __func__, rate, i2c_clk); if (!dfsr) @@ -315,12 +315,12 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, bin_ga = (ga & 0x3) | ((ga & 0x4) << 3); fdr = bin_gb | bin_ga; rate = i2c_clk / est_div; - dev_dbg(i2c_fsl->adapter.dev, + dev_dbg(&i2c_fsl->adapter.dev, "FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x," " a:%d, b:%d, speed:%d\n", fdr, est_div, ga, gb, a, b, rate); /* Condition 2 not accounted for */ - dev_dbg(i2c_fsl->adapter.dev, + dev_dbg(&i2c_fsl->adapter.dev, "Tr <= %d ns\n", (b - 3 * dfsr) * 1000000 / (i2c_clk / 1000)); } @@ -330,9 +330,9 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, if (a == 24) a += 4; } - dev_dbg(i2c_fsl->adapter.dev, + dev_dbg(&i2c_fsl->adapter.dev, "divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr); - dev_dbg(i2c_fsl->adapter.dev, "FDR:0x%.2x, speed:%d\n", fdr, rate); + dev_dbg(&i2c_fsl->adapter.dev, "FDR:0x%.2x, speed:%d\n", fdr, rate); i2c_fsl->ifdr = fdr; i2c_fsl->dfsrr = dfsr; } @@ -368,9 +368,9 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, (500000U * i2c_clk_div[i][0] + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2); - dev_dbg(i2c_fsl->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n", + dev_dbg(&i2c_fsl->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n", __func__, i2c_clk_rate, div); - dev_dbg(i2c_fsl->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n", + dev_dbg(&i2c_fsl->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n", __func__, i2c_clk_div[i][1], i2c_clk_div[i][0]); } #endif @@ -382,7 +382,7 @@ static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) int i, result; if ( !(msgs->flags & I2C_M_DATA_ONLY) ) { - dev_dbg(adapter->dev, + dev_dbg(&adapter->dev, "<%s> write slave address: addr=0x%02x\n", __func__, msgs->addr << 1); @@ -399,7 +399,7 @@ static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) /* write data */ for (i = 0; i < msgs->len; i++) { - dev_dbg(adapter->dev, + dev_dbg(&adapter->dev, "<%s> write byte: B%d=0x%02X\n", __func__, i, msgs->buf[i]); writeb(msgs->buf[i], base + FSL_I2C_I2DR); @@ -425,7 +425,7 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) writeb(0x0, base + FSL_I2C_I2SR); if ( !(msgs->flags & I2C_M_DATA_ONLY) ) { - dev_dbg(adapter->dev, + dev_dbg(&adapter->dev, "<%s> write slave address: addr=0x%02x\n", __func__, (msgs->addr << 1) | 0x01); @@ -478,7 +478,7 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) } msgs->buf[i] = readb(base + FSL_I2C_I2DR); - dev_dbg(adapter->dev, "<%s> read byte: B%d=0x%02X\n", + dev_dbg(&adapter->dev, "<%s> read byte: B%d=0x%02X\n", __func__, i, msgs->buf[i]); } return 0; @@ -544,7 +544,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev) /* Setup i2c_fsl driver structure */ i2c_fsl->adapter.master_xfer = i2c_fsl_xfer; i2c_fsl->adapter.nr = pdev->id; - i2c_fsl->adapter.dev = pdev; + i2c_fsl->adapter.dev.parent = pdev; i2c_fsl->base = dev_request_mem_region(pdev, 0); i2c_fsl->dfsrr = -1; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 24961ebda..f371875aa 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -318,7 +318,7 @@ static int omap_i2c_init(struct omap_i2c_struct *i2c_omap) while (!(omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSS_REG) & SYSS_RESETDONE_MASK)) { if (is_timeout(start, MSECOND)) { - dev_warn(i2c_omap->adapter.dev, "timeout waiting " + dev_warn(&i2c_omap->adapter.dev, "timeout waiting " "for controller reset\n"); return -ETIMEDOUT; } @@ -453,7 +453,7 @@ static int omap_i2c_wait_for_bb(struct i2c_adapter *adapter) start = get_time_ns(); while (omap_i2c_read_reg(i2c_omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) { if (is_timeout(start, MSECOND)) { - dev_warn(adapter->dev, "timeout waiting for bus ready\n"); + dev_warn(&adapter->dev, "timeout waiting for bus ready\n"); return -ETIMEDOUT; } } @@ -476,9 +476,9 @@ omap_i2c_isr(struct omap_i2c_struct *dev) 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->adapter.dev, "IRQ (ISR = 0x%04x)\n", stat); + dev_dbg(&dev->adapter.dev, "IRQ (ISR = 0x%04x)\n", stat); if (count++ == 100) { - dev_warn(dev->adapter.dev, "Too much work in one IRQ\n"); + dev_warn(&dev->adapter.dev, "Too much work in one IRQ\n"); break; } @@ -499,7 +499,7 @@ complete: OMAP_I2C_CON_STP); } if (stat & OMAP_I2C_STAT_AL) { - dev_err(dev->adapter.dev, "Arbitration lost\n"); + dev_err(&dev->adapter.dev, "Arbitration lost\n"); err |= OMAP_I2C_STAT_AL; } if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | @@ -536,11 +536,11 @@ complete: } } else { if (stat & OMAP_I2C_STAT_RRDY) - dev_err(dev->adapter.dev, + dev_err(&dev->adapter.dev, "RRDY IRQ while no data" " requested\n"); if (stat & OMAP_I2C_STAT_RDR) - dev_err(dev->adapter.dev, + dev_err(&dev->adapter.dev, "RDR IRQ while no data" " requested\n"); break; @@ -577,11 +577,11 @@ complete: } } else { if (stat & OMAP_I2C_STAT_XRDY) - dev_err(dev->adapter.dev, + dev_err(&dev->adapter.dev, "XRDY IRQ while no " "data to send\n"); if (stat & OMAP_I2C_STAT_XDR) - dev_err(dev->adapter.dev, + dev_err(&dev->adapter.dev, "XDR IRQ while no " "data to send\n"); break; @@ -613,11 +613,11 @@ complete: continue; } if (stat & OMAP_I2C_STAT_ROVR) { - dev_err(dev->adapter.dev, "Receive overrun\n"); + dev_err(&dev->adapter.dev, "Receive overrun\n"); dev->cmd_err |= OMAP_I2C_STAT_ROVR; } if (stat & OMAP_I2C_STAT_XUDF) { - dev_err(dev->adapter.dev, "Transmit underflow\n"); + dev_err(&dev->adapter.dev, "Transmit underflow\n"); dev->cmd_err |= OMAP_I2C_STAT_XUDF; } } @@ -639,7 +639,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adapter, int ret = 0; - dev_dbg(adapter->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", + dev_dbg(&adapter->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", msg->addr, msg->len, msg->flags, stop); if (msg->len == 0) @@ -687,7 +687,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adapter, /* Let the user know if i2c is in a bad state */ if (is_timeout(start, MSECOND)) { - dev_err(adapter->dev, "controller timed out " + dev_err(&adapter->dev, "controller timed out " "waiting for start condition to finish\n"); return -ETIMEDOUT; } @@ -707,7 +707,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adapter, while (ret){ ret = omap_i2c_isr(i2c_omap); if (is_timeout(start, MSECOND)) { - dev_err(adapter->dev, + dev_err(&adapter->dev, "timed out on polling for " "open i2c message handling\n"); return -ETIMEDOUT; @@ -835,7 +835,7 @@ i2c_omap_probe(struct device_d *pdev) i2c_omap->adapter.master_xfer = omap_i2c_xfer, i2c_omap->adapter.nr = pdev->id; - i2c_omap->adapter.dev = pdev; + i2c_omap->adapter.dev.parent = pdev; /* i2c device drivers may be active on return from add_adapter() */ r = i2c_add_numbered_adapter(&i2c_omap->adapter); diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c index 27fd256cf..3af5c32ec 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -80,7 +80,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) */ for (ret = 0; ret < num; ret++) { - dev_dbg(adap->dev, "master_xfer[%d] %c, addr=0x%02x, " + dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, " "len=%d\n", ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W', msgs[ret].addr, msgs[ret].len); } @@ -256,6 +256,9 @@ struct i2c_client *i2c_new_device(struct i2c_adapter *adapter, client->adapter = adapter; client->addr = chip->addr; + client->dev.parent = &adapter->dev; + dev_add_child(client->dev.parent, &client->dev); + status = register_device(&client->dev); #if 0 @@ -363,9 +366,21 @@ struct i2c_adapter *i2c_get_adapter(int busnum) */ int i2c_add_numbered_adapter(struct i2c_adapter *adapter) { + int ret; + if (i2c_get_adapter(adapter->nr)) return -EBUSY; + adapter->dev.id = adapter->nr; + strcpy(adapter->dev.name, "i2c"); + + if (adapter->dev.parent) + dev_add_child(adapter->dev.parent, &adapter->dev); + + ret = register_device(&adapter->dev); + if (ret) + return ret; + list_add_tail(&adapter->list, &adapter_list); /* populate children from any i2c device tables */ diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h index de2a7ea27..5021dd4e5 100644 --- a/include/i2c/i2c.h +++ b/include/i2c/i2c.h @@ -66,7 +66,7 @@ struct i2c_msg { * */ struct i2c_adapter { - struct device_d *dev; /* ptr to device */ + struct device_d dev; /* ptr to device */ int nr; /* bus number */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); struct list_head list; From 334e8f3b62f922370723b911c9afda7e6cd5d27d Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 2 Nov 2012 10:36:50 +0100 Subject: [PATCH 03/12] i2c: add i2c algo bit support This is needed for i2c-gpio support Based on linux 3.7-rc2 Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/i2c/Kconfig | 1 + drivers/i2c/Makefile | 2 +- drivers/i2c/algos/Kconfig | 6 + drivers/i2c/algos/Makefile | 5 + drivers/i2c/algos/i2c-algo-bit.c | 585 +++++++++++++++++++++++++++++++ include/i2c/i2c-algo-bit.h | 55 +++ include/i2c/i2c.h | 2 + 7 files changed, 655 insertions(+), 1 deletion(-) create mode 100644 drivers/i2c/algos/Kconfig create mode 100644 drivers/i2c/algos/Makefile create mode 100644 drivers/i2c/algos/i2c-algo-bit.c create mode 100644 include/i2c/i2c-algo-bit.h diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index c2af81839..038e2ee45 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -3,6 +3,7 @@ menuconfig I2C if I2C +source drivers/i2c/algos/Kconfig source drivers/i2c/busses/Kconfig endif diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 42e22c01b..5ce032471 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -1 +1 @@ -obj-$(CONFIG_I2C) += i2c.o busses/ +obj-$(CONFIG_I2C) += i2c.o busses/ algos/ diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig new file mode 100644 index 000000000..c74b148f5 --- /dev/null +++ b/drivers/i2c/algos/Kconfig @@ -0,0 +1,6 @@ +# +# I2C algorithm drivers configuration +# + +config I2C_ALGOBIT + bool diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile new file mode 100644 index 000000000..e0a03995b --- /dev/null +++ b/drivers/i2c/algos/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the i2c algorithms +# + +obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c new file mode 100644 index 000000000..37af27cbb --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -0,0 +1,585 @@ +/* ------------------------------------------------------------------------- + * 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., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. + * ------------------------------------------------------------------------- */ + +/* With some changes from Frodo Looijaard , Kyösti Mälkki + and Jean Delvare */ + +#include +#include +#include +#include +#include +#include + +/* ----- 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 */ + +#ifdef DEBUG +static int i2c_debug = 1; +#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) +{ + uint64_t start; + + setscl(adap, 1); + + /* Not all adapters have scl sense line... */ + if (!adap->getscl) + goto done; + + start = get_time_ns(); + 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 (is_timeout(start, adap->timeout_ms * MSECOND)) { + /* Test one last time, as we may have been preempted + * between last check and timeout test. + */ + if (getscl(adap)) + break; + return -ETIMEDOUT; + } + } +#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; + struct device_d *dev = &i2c_adap->dev; + int scl, sda, ret; + + if (adap->pre_xfer) { + ret = adap->pre_xfer(i2c_adap); + if (ret < 0) + return -ENODEV; + } + + if (adap->getscl == NULL) + dev_info(dev, "Testing SDA only, SCL is not readable\n"); + + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (!scl || !sda) { + dev_warn(dev, "bus seems to be busy (scl=%d, sda=%d)\n", scl, sda); + goto bailout; + } + + sdalo(adap); + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (sda) { + dev_warn(dev, "SDA stuck high!\n"); + goto bailout; + } + if (!scl) { + dev_warn(dev, "SCL unexpected low while pulling SDA low!\n"); + goto bailout; + } + + sdahi(adap); + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (!sda) { + dev_warn(dev, "SDA stuck low!\n"); + goto bailout; + } + if (!scl) { + dev_warn(dev, "SCL unexpected low while pulling SDA high!\n"); + goto bailout; + } + + scllo(adap); + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 0 : getscl(adap); + if (scl) { + dev_warn(dev, "SCL stuck high!\n"); + goto bailout; + } + if (!sda) { + dev_warn(dev, "SDA unexpected low while pulling SCL low!\n"); + goto bailout; + } + + sclhi(adap); + sda = getsda(adap); + scl = (adap->getscl == NULL) ? 1 : getscl(adap); + if (!scl) { + dev_warn(dev, "SCL stuck low!\n"); + goto bailout; + } + if (!sda) { + dev_warn(dev, "SDA unexpected low while pulling SCL high!\n"); + goto bailout; + } + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + + dev_info(dev, "Test OK\n"); + 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); + 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 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; + + while (count > 0) { + inval = i2c_inb(i2c_adap); + if (inval >= 0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + break; + } + + temp++; + count--; + + bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n", + inval, + (flags & I2C_M_NO_RD_ACK) + ? "(no ack/nak)" + : (count ? "A" : "NA")); + } + 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: -ENXIO 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 -ENXIO; + } + /* 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 -ENXIO; + } + 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 -EIO; + } + } + } else { /* normal 7bit address */ + addr = msg->addr << 1; + if (flags & I2C_M_RD) + 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 (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 = -EIO; + 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 = -EIO; + 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; +} + +/* + * 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 (bit_test >= 2 && ret < 0) + return -ENODEV; + } + + /* register new adapter to i2c module... */ + //adap->algo = &i2c_bit_algo; + adap->retries = 3; + adap->master_xfer = bit_xfer; + + 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_numbered_bus(struct i2c_adapter *adap) +{ + return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter); +} +EXPORT_SYMBOL(i2c_bit_add_numbered_bus); diff --git a/include/i2c/i2c-algo-bit.h b/include/i2c/i2c-algo-bit.h new file mode 100644 index 000000000..1b7221968 --- /dev/null +++ b/include/i2c/i2c-algo-bit.h @@ -0,0 +1,55 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-bit.h i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* 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., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +#ifndef _LINUX_I2C_ALGO_BIT_H +#define _LINUX_I2C_ALGO_BIT_H + +/* --- Defines for bit-adapters --------------------------------------- */ +/* + * This struct contains the hw-dependent functions of bit-style adapters to + * manipulate the line states, and to init any hw-specific features. This is + * only used if you have more than one hw-type of adapter running. + */ +struct i2c_algo_bit_data { + void *data; /* private data for lowlevel routines */ + void (*setsda) (void *data, int state); + void (*setscl) (void *data, int state); + int (*getsda) (void *data); + int (*getscl) (void *data); + int (*pre_xfer) (struct i2c_adapter *); + void (*post_xfer) (struct i2c_adapter *); + + /* local settings */ + int udelay; /* half clock cycle time in us, + minimum 2 us for fast-mode I2C, + minimum 5 us for standard-mode I2C and SMBus, + maximum 50 us for SMBus */ + int timeout_ms; /* in ms */ +}; + +int i2c_bit_add_bus(struct i2c_adapter *); +int i2c_bit_add_numbered_bus(struct i2c_adapter *); +extern const struct i2c_algorithm i2c_bit_algo; + +#endif /* _LINUX_I2C_ALGO_BIT_H */ diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h index 5021dd4e5..b05911909 100644 --- a/include/i2c/i2c.h +++ b/include/i2c/i2c.h @@ -70,6 +70,8 @@ struct i2c_adapter { int nr; /* bus number */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); struct list_head list; + int retries; + void *algo_data; }; From 32a9da73c21acf07333687472c6411ab95bc7446 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 2 Nov 2012 10:36:51 +0100 Subject: [PATCH 04/12] i2c: add i2c-gpio support Based on linux 3.7-rc2 Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/i2c/busses/Kconfig | 8 ++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-gpio.c | 177 ++++++++++++++++++++++++++++++++++ include/i2c/i2c-gpio.h | 38 ++++++++ 4 files changed, 224 insertions(+) create mode 100644 drivers/i2c/busses/i2c-gpio.c create mode 100644 include/i2c/i2c-gpio.h diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 3f998eafa..17e33cb18 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -4,6 +4,14 @@ menu "I2C Hardware Bus support" +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_IMX bool "MPC85xx/i.MX I2C Master driver" depends on (ARCH_IMX && !ARCH_IMX1) || ARCH_MPC85XX diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index e4c512558..18c7c46ac 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -1,2 +1,3 @@ +obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_OMAP) += i2c-omap.o diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c new file mode 100644 index 000000000..98ce2d59a --- /dev/null +++ b/drivers/i2c/busses/i2c-gpio.c @@ -0,0 +1,177 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +struct i2c_gpio_private_data { + struct i2c_adapter adap; + struct i2c_algo_bit_data bit_data; + struct i2c_gpio_platform_data pdata; +}; + +/* 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 i2c_gpio_probe(struct device_d *dev) +{ + struct i2c_gpio_private_data *priv; + struct i2c_gpio_platform_data *pdata; + struct i2c_algo_bit_data *bit_data; + struct i2c_adapter *adap; + int ret; + + priv = xzalloc(sizeof(*priv)); + + adap = &priv->adap; + bit_data = &priv->bit_data; + pdata = &priv->pdata; + + if (!dev->platform_data) + return -ENXIO; + memcpy(pdata, dev->platform_data, sizeof(*pdata)); + + 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_ms) + bit_data->timeout_ms = pdata->timeout_ms; + else + bit_data->timeout_ms = 100; /* 100 ms */ + + bit_data->data = pdata; + + adap->algo_data = bit_data; + adap->dev.parent = dev; + + adap->nr = dev->id; + ret = i2c_bit_add_numbered_bus(adap); + if (ret) + goto err_add_bus; + + dev_info(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: + return ret; +} + +static struct driver_d i2c_gpio_driver = { + .name = "i2c-gpio", + .probe = i2c_gpio_probe, +}; + +static int __init i2c_gpio_init(void) +{ + return platform_driver_register(&i2c_gpio_driver); +} +device_initcall(i2c_gpio_init); diff --git a/include/i2c/i2c-gpio.h b/include/i2c/i2c-gpio.h new file mode 100644 index 000000000..55feb82a9 --- /dev/null +++ b/include/i2c/i2c-gpio.h @@ -0,0 +1,38 @@ +/* + * i2c-gpio interface to platform code + * + * 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. + */ +#ifndef _LINUX_I2C_GPIO_H +#define _LINUX_I2C_GPIO_H + +/** + * struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio + * @sda_pin: GPIO pin ID to use for SDA + * @scl_pin: GPIO pin ID to use for SCL + * @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz + * @timeout_ms: clock stretching timeout in ms. If the slave keeps + * SCL low for longer than this, the transfer will time out. + * @sda_is_open_drain: SDA is configured as open drain, i.e. the pin + * isn't actively driven high when setting the output value high. + * gpio_get_value() must return the actual pin state even if the + * pin is configured as an output. + * @scl_is_open_drain: SCL is set up as open drain. Same requirements + * as for sda_is_open_drain apply. + * @scl_is_output_only: SCL output drivers cannot be turned off. + */ +struct i2c_gpio_platform_data { + unsigned int sda_pin; + unsigned int scl_pin; + int udelay; + int timeout_ms; + unsigned int sda_is_open_drain:1; + unsigned int scl_is_open_drain:1; + unsigned int scl_is_output_only:1; +}; + +#endif /* _LINUX_I2C_GPIO_H */ From fcbb4c7472c36a72bfb7a12f87d6f2034a3131d2 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 2 Nov 2012 10:36:52 +0100 Subject: [PATCH 05/12] i2c: add versatile support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/i2c/busses/Kconfig | 8 +++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-versatile.c | 112 +++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 drivers/i2c/busses/i2c-versatile.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 17e33cb18..68d9b469e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -20,4 +20,12 @@ config I2C_OMAP bool "OMAP I2C Master driver" depends on ARCH_OMAP +config I2C_VERSATILE + tristate "ARM Versatile/Realview I2C bus support" + depends on ARCH_VERSATILE + select I2C_ALGOBIT + help + Say yes if you want to support the I2C serial bus on ARMs Versatile + range of platforms. + endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18c7c46ac..a30f9b87e 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_OMAP) += i2c-omap.o +obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c new file mode 100644 index 000000000..7c993226f --- /dev/null +++ b/drivers/i2c/busses/i2c-versatile.c @@ -0,0 +1,112 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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_ms = 100, +}; + +static int i2c_versatile_probe(struct device_d *dev) +{ + struct i2c_versatile *i2c; + int ret; + + i2c = kzalloc(sizeof(struct i2c_versatile), GFP_KERNEL); + if (!i2c) { + ret = -ENOMEM; + goto err_release; + } + + i2c->base = dev_request_mem_region(dev, 0); + if (!i2c->base) { + ret = -ENOMEM; + goto err_free; + } + + writel(SCL | SDA, i2c->base + I2C_CONTROLS); + + i2c->adap.algo_data = &i2c->algo; + i2c->adap.dev.parent = dev; + i2c->algo = i2c_versatile_algo; + i2c->algo.data = i2c; + + i2c->adap.nr = dev->id; + ret = i2c_bit_add_numbered_bus(&i2c->adap); + if (ret >= 0) { + return 0; + } + + err_free: + kfree(i2c); + err_release: + return ret; +} + +static struct driver_d i2c_versatile_driver = { + .name = "versatile-i2c", + .probe = i2c_versatile_probe, +}; + +static int __init i2c_versatile_init(void) +{ + return platform_driver_register(&i2c_versatile_driver); +} + +device_initcall(i2c_versatile_init); From 37e4bec9f73aaddf50e0ba996fa1fc5a47febbaf Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 1 Nov 2012 10:52:47 +0100 Subject: [PATCH 06/12] versatilepb: add i2c support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- arch/arm/boards/versatile/versatilepb.c | 1 + arch/arm/configs/versatilepb_defconfig | 3 +++ arch/arm/mach-versatile/core.c | 7 +++++++ arch/arm/mach-versatile/include/mach/init.h | 1 + 4 files changed, 12 insertions(+) diff --git a/arch/arm/boards/versatile/versatilepb.c b/arch/arm/boards/versatile/versatilepb.c index 5a9c0b65e..2eb7473b7 100644 --- a/arch/arm/boards/versatile/versatilepb.c +++ b/arch/arm/boards/versatile/versatilepb.c @@ -47,6 +47,7 @@ mem_initcall(vpb_mem_init); static int vpb_devices_init(void) { add_cfi_flash_device(DEVICE_ID_DYNAMIC, VERSATILE_FLASH_BASE, VERSATILE_FLASH_SIZE, 0); + versatile_register_i2c(); devfs_add_partition("nor0", 0x00000, 0x40000, DEVFS_PARTITION_FIXED, "self"); devfs_add_partition("nor0", 0x40000, 0x20000, DEVFS_PARTITION_FIXED, "env0"); diff --git a/arch/arm/configs/versatilepb_defconfig b/arch/arm/configs/versatilepb_defconfig index 87aec4d52..eb29a22ad 100644 --- a/arch/arm/configs/versatilepb_defconfig +++ b/arch/arm/configs/versatilepb_defconfig @@ -33,6 +33,7 @@ CONFIG_CMD_GO=y CONFIG_CMD_TIMEOUT=y CONFIG_CMD_PARTITION=y CONFIG_CMD_UNCOMPRESS=y +CONFIG_CMD_I2C=y CONFIG_NET=y CONFIG_NET_DHCP=y CONFIG_NET_NFS=y @@ -43,6 +44,8 @@ CONFIG_NET_NETCONSOLE=y CONFIG_NET_RESOLV=y CONFIG_SERIAL_AMBA_PL011=y CONFIG_DRIVER_NET_SMC91111=y +CONFIG_I2C=y +CONFIG_I2C_VERSATILE=y CONFIG_FS_CRAMFS=y CONFIG_SHA1=y CONFIG_SHA256=y diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index 5c75e11e9..853437991 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -182,6 +183,12 @@ void versatile_register_uart(unsigned id) amba_apb_device_add(NULL, "uart-pl011", id, start, 4096, NULL, 0); } +void versatile_register_i2c(void) +{ + add_generic_device("versatile-i2c", DEVICE_ID_DYNAMIC, NULL, + VERSATILE_I2C_BASE, SZ_4K, IORESOURCE_MEM, NULL); +} + void __noreturn reset_cpu (unsigned long ignored) { u32 val; diff --git a/arch/arm/mach-versatile/include/mach/init.h b/arch/arm/mach-versatile/include/mach/init.h index 878cde037..b40e4f90b 100644 --- a/arch/arm/mach-versatile/include/mach/init.h +++ b/arch/arm/mach-versatile/include/mach/init.h @@ -4,5 +4,6 @@ void versatile_add_sdram(u32 size); void versatile_register_uart(unsigned id); +void versatile_register_i2c(void); #endif From 106742b9009b0d04faabe52d21f9fd3cff408b32 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 3 Nov 2012 21:53:07 +0100 Subject: [PATCH 07/12] i2c: algo-bit add missing acknak This is need for sequential read/write. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/i2c/algos/i2c-algo-bit.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index 37af27cbb..dc43eb83a 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -390,6 +390,22 @@ static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) 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; @@ -414,6 +430,10 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) (flags & I2C_M_NO_RD_ACK) ? "(no ack/nak)" : (count ? "A" : "NA")); + + inval = acknak(i2c_adap, count); + if (inval < 0) + return inval; } return rdcount; } From 47e176461fbbff0b6f3ad863ef7f1fd3bc2d3c02 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 3 Nov 2012 21:53:08 +0100 Subject: [PATCH 08/12] i2c: introduce i2c_new_dummy 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). Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/i2c/i2c.c | 28 ++++++++++++++++++++++++++++ include/i2c/i2c.h | 8 ++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c index 3af5c32ec..92d9442f2 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -281,6 +281,34 @@ struct i2c_client *i2c_new_device(struct i2c_adapter *adapter, } EXPORT_SYMBOL(i2c_new_device); +/** + * 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_register_board_info - register I2C devices for a given board * diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h index b05911909..dc5e5fc99 100644 --- a/include/i2c/i2c.h +++ b/include/i2c/i2c.h @@ -129,6 +129,14 @@ static inline int i2c_register_board_info(int busnum, extern int i2c_add_numbered_adapter(struct i2c_adapter *adapter); struct i2c_adapter *i2c_get_adapter(int busnum); +/* For devices that use several addresses, use i2c_new_dummy() to make + * client handles for the extra addresses. + */ +extern struct i2c_client * +i2c_new_dummy(struct i2c_adapter *adap, u16 address); + + + extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); extern int i2c_master_send(struct i2c_client *client, const char *buf, int count); extern int i2c_master_recv(struct i2c_client *client, char *buf, int count); From bcf272af8bd11e4742c527d6e29c2687864f9988 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 3 Nov 2012 21:53:09 +0100 Subject: [PATCH 09/12] i2c: add id_table support this will be use by at24 driver Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/i2c/i2c.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c index 92d9442f2..94f23dd85 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -420,7 +420,22 @@ EXPORT_SYMBOL(i2c_add_numbered_adapter); static int i2c_match(struct device_d *dev, struct driver_d *drv) { - return strcmp(dev->name, drv->name) ? -1 : 0; + if (!strcmp(dev->name, drv->name)) + return 0; + + if (drv->id_table) { + struct platform_device_id *id = drv->id_table; + + while (id->name) { + if (!strcmp(id->name, dev->name)) { + dev->id_entry = id; + return 0; + } + id++; + } + } + + return -1; } static int i2c_probe(struct device_d *dev) From 85ca16d028d0032797f7b91a4fa4e60741768758 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 3 Nov 2012 21:58:28 +0100 Subject: [PATCH 10/12] import log2 support from linux Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- include/linux/log2.h | 190 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 include/linux/log2.h diff --git a/include/linux/log2.h b/include/linux/log2.h new file mode 100644 index 000000000..389043a93 --- /dev/null +++ b/include/linux/log2.h @@ -0,0 +1,190 @@ +/* Integer base 2 logarithm calculation + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.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. + */ + +#ifndef _LINUX_LOG2_H +#define _LINUX_LOG2_H + +#include +#include + +/* + * deal with unrepresentable constant logarithms + */ +extern __attribute__((const, noreturn)) +int ____ilog2_NaN(void); + +/* + * non-constant log of base 2 calculators + * - the arch may override these in asm/bitops.h if they can be implemented + * more efficiently than using fls() and fls64() + * - the arch is not required to handle n==0 if implementing the fallback + */ +#ifndef CONFIG_ARCH_HAS_ILOG2_U32 +static inline __attribute__((const)) +int __ilog2_u32(u32 n) +{ + return fls(n) - 1; +} +#endif + +#ifndef CONFIG_ARCH_HAS_ILOG2_U64 +static inline __attribute__((const)) +int __ilog2_u64(u64 n) +{ + return fls64(n) - 1; +} +#endif + +/* + * Determine whether some value is a power of two, where zero is + * *not* considered a power of two. + */ + +static inline __attribute__((const)) +bool is_power_of_2(unsigned long n) +{ + return (n != 0 && ((n & (n - 1)) == 0)); +} + +/** + * ilog2 - log of base 2 of 32-bit or a 64-bit unsigned value + * @n - parameter + * + * constant-capable log of base 2 calculation + * - this can be used to initialise global variables from constant data, hence + * the massive ternary operator construction + * + * selects the appropriately-sized optimised version depending on sizeof(n) + */ +#define ilog2(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (n) < 1 ? ____ilog2_NaN() : \ + (n) & (1ULL << 63) ? 63 : \ + (n) & (1ULL << 62) ? 62 : \ + (n) & (1ULL << 61) ? 61 : \ + (n) & (1ULL << 60) ? 60 : \ + (n) & (1ULL << 59) ? 59 : \ + (n) & (1ULL << 58) ? 58 : \ + (n) & (1ULL << 57) ? 57 : \ + (n) & (1ULL << 56) ? 56 : \ + (n) & (1ULL << 55) ? 55 : \ + (n) & (1ULL << 54) ? 54 : \ + (n) & (1ULL << 53) ? 53 : \ + (n) & (1ULL << 52) ? 52 : \ + (n) & (1ULL << 51) ? 51 : \ + (n) & (1ULL << 50) ? 50 : \ + (n) & (1ULL << 49) ? 49 : \ + (n) & (1ULL << 48) ? 48 : \ + (n) & (1ULL << 47) ? 47 : \ + (n) & (1ULL << 46) ? 46 : \ + (n) & (1ULL << 45) ? 45 : \ + (n) & (1ULL << 44) ? 44 : \ + (n) & (1ULL << 43) ? 43 : \ + (n) & (1ULL << 42) ? 42 : \ + (n) & (1ULL << 41) ? 41 : \ + (n) & (1ULL << 40) ? 40 : \ + (n) & (1ULL << 39) ? 39 : \ + (n) & (1ULL << 38) ? 38 : \ + (n) & (1ULL << 37) ? 37 : \ + (n) & (1ULL << 36) ? 36 : \ + (n) & (1ULL << 35) ? 35 : \ + (n) & (1ULL << 34) ? 34 : \ + (n) & (1ULL << 33) ? 33 : \ + (n) & (1ULL << 32) ? 32 : \ + (n) & (1ULL << 31) ? 31 : \ + (n) & (1ULL << 30) ? 30 : \ + (n) & (1ULL << 29) ? 29 : \ + (n) & (1ULL << 28) ? 28 : \ + (n) & (1ULL << 27) ? 27 : \ + (n) & (1ULL << 26) ? 26 : \ + (n) & (1ULL << 25) ? 25 : \ + (n) & (1ULL << 24) ? 24 : \ + (n) & (1ULL << 23) ? 23 : \ + (n) & (1ULL << 22) ? 22 : \ + (n) & (1ULL << 21) ? 21 : \ + (n) & (1ULL << 20) ? 20 : \ + (n) & (1ULL << 19) ? 19 : \ + (n) & (1ULL << 18) ? 18 : \ + (n) & (1ULL << 17) ? 17 : \ + (n) & (1ULL << 16) ? 16 : \ + (n) & (1ULL << 15) ? 15 : \ + (n) & (1ULL << 14) ? 14 : \ + (n) & (1ULL << 13) ? 13 : \ + (n) & (1ULL << 12) ? 12 : \ + (n) & (1ULL << 11) ? 11 : \ + (n) & (1ULL << 10) ? 10 : \ + (n) & (1ULL << 9) ? 9 : \ + (n) & (1ULL << 8) ? 8 : \ + (n) & (1ULL << 7) ? 7 : \ + (n) & (1ULL << 6) ? 6 : \ + (n) & (1ULL << 5) ? 5 : \ + (n) & (1ULL << 4) ? 4 : \ + (n) & (1ULL << 3) ? 3 : \ + (n) & (1ULL << 2) ? 2 : \ + (n) & (1ULL << 1) ? 1 : \ + (n) & (1ULL << 0) ? 0 : \ + ____ilog2_NaN() \ + ) : \ + (sizeof(n) <= 4) ? \ + __ilog2_u32(n) : \ + __ilog2_u64(n) \ + ) + +/** + * roundup_pow_of_two - round the given value up to nearest power of two + * @n - parameter + * + * round the given value up to the nearest power of two + * - the result is undefined when n == 0 + * - this can be used to initialise global variables from constant data + */ +#define roundup_pow_of_two(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (n == 1) ? 1 : \ + (1UL << (ilog2((n) - 1) + 1)) \ + ) : \ + __roundup_pow_of_two(n) \ + ) + +/** + * rounddown_pow_of_two - round the given value down to nearest power of two + * @n - parameter + * + * round the given value down to the nearest power of two + * - the result is undefined when n == 0 + * - this can be used to initialise global variables from constant data + */ +#define rounddown_pow_of_two(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (1UL << ilog2(n))) : \ + __rounddown_pow_of_two(n) \ + ) + +/** + * order_base_2 - calculate the (rounded up) base 2 order of the argument + * @n: parameter + * + * The first few values calculated by this routine: + * ob2(0) = 0 + * ob2(1) = 0 + * ob2(2) = 1 + * ob2(3) = 2 + * ob2(4) = 2 + * ob2(5) = 3 + * ... and so on. + */ + +#define order_base_2(n) ilog2(roundup_pow_of_two(n)) + +#endif /* _LINUX_LOG2_H */ From b27a52c9d60e11d0bd3ac1bcd7f3ccfa49216bdf Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 3 Nov 2012 21:58:29 +0100 Subject: [PATCH 11/12] add roundup and rounddown support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- include/linux/kernel.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index e9e2f0764..92c3391f1 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -90,5 +90,20 @@ __val = __val < __min ? __min: __val; \ __val > __max ? __max: __val; }) + +/* The `const' in roundup() prevents gcc-3.3 from calling __divdi3 */ +#define roundup(x, y) ( \ +{ \ + const typeof(y) __y = y; \ + (((x) + (__y - 1)) / __y) * __y; \ +} \ +) +#define rounddown(x, y) ( \ +{ \ + typeof(x) __x = (x); \ + __x - (__x % (y)); \ +} \ +) + #endif /* _LINUX_KERNEL_H */ From 7f8547648ce9c96016de0c22f8f304431269bd65 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 3 Nov 2012 21:58:30 +0100 Subject: [PATCH 12/12] eeprom: add at24 support This driver to get read/write support to most I2C EEPROMs, after you configure the driver to know about each EEPROM on your target board. Use these generic chip names, instead of vendor-specific ones like at24c64 or 24lc02: 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024 Unless you like data loss puzzles, always be sure that any chip you configure as a 24c32 (32 kbit) or larger is NOT really a 24c16 (16 kbit) or smaller, and vice versa. Marking the chip as read-only won't help recover from this. Also, if your chip has any software write-protect mechanism you may want to review the code to make sure this driver won't turn it on by accident. Based on linux 3.6 Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- drivers/eeprom/Kconfig | 19 ++ drivers/eeprom/Makefile | 1 + drivers/eeprom/at24.c | 460 ++++++++++++++++++++++++++++++++++++++++ include/i2c/at24.h | 35 +++ 4 files changed, 515 insertions(+) create mode 100644 drivers/eeprom/at24.c create mode 100644 include/i2c/at24.h diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig index a0b54899c..ce9cfe7ec 100644 --- a/drivers/eeprom/Kconfig +++ b/drivers/eeprom/Kconfig @@ -8,4 +8,23 @@ config EEPROM_AT25 after you configure the board init code to know about each eeprom on your target board. +config EEPROM_AT24 + bool "at24 based eeprom" + depends on I2C + help + Enable this driver to get read/write support to most I2C EEPROMs, + after you configure the driver to know about each EEPROM on + your target board. Use these generic chip names, instead of + vendor-specific ones like at24c64 or 24lc02: + + 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, + 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024 + + Unless you like data loss puzzles, always be sure that any chip + you configure as a 24c32 (32 kbit) or larger is NOT really a + 24c16 (16 kbit) or smaller, and vice versa. Marking the chip + as read-only won't help recover from this. Also, if your chip + has any software write-protect mechanism you may want to review the + code to make sure this driver won't turn it on by accident. + endmenu diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile index e323bd0ca..e287eb02f 100644 --- a/drivers/eeprom/Makefile +++ b/drivers/eeprom/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_EEPROM_AT25) += at25.o +obj-$(CONFIG_EEPROM_AT24) += at24.o diff --git a/drivers/eeprom/at24.c b/drivers/eeprom/at24.c new file mode 100644 index 000000000..6f0133acd --- /dev/null +++ b/drivers/eeprom/at24.c @@ -0,0 +1,460 @@ +/* + * at24.c - handle most I2C EEPROMs + * + * Copyright (C) 2005-2007 David Brownell + * Copyright (C) 2008 Wolfram Sang, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. + * Differences between different vendor product lines (like Atmel AT24C or + * MicroChip 24LC, etc) won't much matter for typical read/write access. + * There are also I2C RAM chips, likewise interchangeable. One example + * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes). + * + * However, misconfiguration can lose data. "Set 16-bit memory address" + * to a part with 8-bit addressing will overwrite data. Writing with too + * big a page size also loses data. And it's not safe to assume that the + * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC + * uses 0x51, for just one example. + * + * So this driver uses "new style" I2C driver binding, expecting to be + * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or + * similar kernel-resident tables; or, configuration data coming from + * a bootloader. + * + * Other than binding model, current differences from "eeprom" driver are + * that this one handles write access and isn't restricted to 24c02 devices. + * It also handles larger devices (32 kbit and up) with two-byte addresses, + * which won't work on pure SMBus systems. + */ + +struct at24_data { + struct at24_platform_data chip; + + struct cdev cdev; + struct file_operations fops; + + u8 *writebuf; + unsigned write_max; + unsigned num_addresses; + + /* + * Some chips tie up multiple I2C addresses; dummy devices reserve + * them for us. + */ + struct i2c_client *client[]; +}; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned io_limit = 128; + +/* + * Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned write_timeout = 25; + +#define AT24_SIZE_BYTELEN 5 +#define AT24_SIZE_FLAGS 8 + +#define AT24_BITMASK(x) (BIT(x) - 1) + +/* create non-zero magic value for given eeprom parameters */ +#define AT24_DEVICE_MAGIC(_len, _flags) \ + ((1 << AT24_SIZE_FLAGS | (_flags)) \ + << AT24_SIZE_BYTELEN | ilog2(_len)) + +static struct platform_device_id at24_ids[] = { + /* needs 8 addresses as A0-A2 are ignored */ + { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) }, + /* old variants can't be handled with this generic entry! */ + { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) }, + { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }, + /* spd is a 24c02 in memory DIMMs */ + { "spd", AT24_DEVICE_MAGIC(2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, + { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) }, + /* 24rf08 quirk is handled at i2c-core */ + { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) }, + { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) }, + { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) }, + { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, + { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) }, + { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, + { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) }, + { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) }, + { "at24", 0 }, + { /* END OF LIST */ } +}; + +/*-------------------------------------------------------------------------*/ + +/* + * This routine supports chips which consume multiple I2C addresses. It + * computes the addressing information to be used for a given r/w request. + * Assumes that sanity checks for offset happened at sysfs-layer. + */ +static struct i2c_client *at24_translate_offset(struct at24_data *at24, + unsigned *offset) +{ + unsigned i; + + if (at24->chip.flags & AT24_FLAG_ADDR16) { + i = *offset >> 16; + *offset &= 0xffff; + } else { + i = *offset >> 8; + *offset &= 0xff; + } + + return at24->client[i]; +} + +static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, + unsigned offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + struct i2c_client *client; + int status, i; + uint64_t start, read_time; + + memset(msg, 0, sizeof(msg)); + + /* + * REVISIT some multi-address chips don't rollover page reads to + * the next slave address, so we may need to truncate the count. + * Those chips might need another quirk flag. + * + * If the real hardware used four adjacent 24c02 chips and that + * were misconfigured as one 24c08, that would be a similar effect: + * one "eeprom" file not four, but larger reads would fail when + * they crossed certain pages. + */ + + /* + * Slave address and byte offset derive from the offset. Always + * set the byte address; on a multi-master board, another master + * may have changed the chip's "current" address pointer. + */ + client = at24_translate_offset(at24, &offset); + + if (count > io_limit) + count = io_limit; + + i = 0; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msgbuf[i++] = offset >> 8; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + /* + * Reads fail if the previous write didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + start = get_time_ns(); + do { + read_time = get_time_ns(); + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + dev_dbg(&client->dev, "read %zu@%d --> %d (%llu)\n", + count, offset, status, read_time); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + mdelay(1); + } while (!is_timeout(start, write_timeout * MSECOND)); + + return -ETIMEDOUT; +} + +static ssize_t at24_read(struct at24_data *at24, + char *buf, loff_t off, size_t count) +{ + ssize_t retval = 0; + + if (unlikely(!count)) + return count; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + while (count) { + ssize_t status; + + status = at24_eeprom_read(at24, buf, off, count); + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + return retval; +} + +static ssize_t at24_cdev_read(struct cdev *cdev, void *buf, size_t count, + loff_t off, ulong flags) +{ + struct at24_data *at24 = cdev->priv; + + return at24_read(at24, buf, off, count); +} + +/* + * Note that if the hardware write-protect pin is pulled high, the whole + * chip is normally write protected. But there are plenty of product + * variants here, including OTP fuses and partial chip protect. + * + * We only use page mode writes; the alternative is sloooow. This routine + * writes at most one page. + */ +static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf, + unsigned offset, size_t count) +{ + struct i2c_client *client; + struct i2c_msg msg; + ssize_t status; + uint64_t start, write_time; + unsigned next_page; + int i = 0; + + /* Get corresponding I2C address and adjust offset */ + client = at24_translate_offset(at24, &offset); + + /* write_max is at most a page */ + if (count > at24->write_max) + count = at24->write_max; + + /* Never roll over backwards, to the start of this page */ + next_page = roundup(offset + 1, at24->chip.page_size); + if (offset + count > next_page) + count = next_page - offset; + + + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = at24->writebuf; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msg.buf[i++] = offset >> 8; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + + /* + * Writes fail if the previous one didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + start = get_time_ns(); + do { + write_time = get_time_ns(); + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + dev_dbg(&client->dev, "write %zu@%d --> %zd (%llu)\n", + count, offset, status, write_time); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + mdelay(1); + } while (!is_timeout(start, write_timeout * MSECOND)); + + return -ETIMEDOUT; +} + +static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off, + size_t count) +{ + ssize_t retval = 0; + + if (unlikely(!count)) + return count; + + while (count) { + ssize_t status; + + status = at24_eeprom_write(at24, buf, off, count); + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + return retval; +} + +static ssize_t at24_cdev_write(struct cdev *cdev, const void *buf, size_t count, + loff_t off, ulong flags) +{ + struct at24_data *at24 = cdev->priv; + + return at24_write(at24, buf, off, count); +} + +static int at24_probe(struct device_d *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct at24_platform_data chip; + bool writable; + struct at24_data *at24; + int err; + unsigned i, num_addresses; + + if (dev->platform_data) { + chip = *(struct at24_platform_data *)dev->platform_data; + } else { + unsigned long magic; + + err = dev_get_drvdata(dev, (unsigned long *)&magic); + if (err) + return err; + + chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); + magic >>= AT24_SIZE_BYTELEN; + chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + chip.page_size = 1; + } + + if (!is_power_of_2(chip.byte_len)) + dev_warn(&client->dev, + "byte_len looks suspicious (no power of 2)!\n"); + if (!chip.page_size) { + dev_err(&client->dev, "page_size must not be 0!\n"); + err = -EINVAL; + goto err_out; + } + if (!is_power_of_2(chip.page_size)) + dev_warn(&client->dev, + "page_size looks suspicious (no power of 2)!\n"); + + if (chip.flags & AT24_FLAG_TAKE8ADDR) + num_addresses = 8; + else + num_addresses = DIV_ROUND_UP(chip.byte_len, + (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256); + + at24 = xzalloc(sizeof(struct at24_data) + + num_addresses * sizeof(struct i2c_client *)); + + at24->chip = chip; + at24->num_addresses = num_addresses; + at24->cdev.name = asprintf("eeprom%d", dev->id); + at24->cdev.priv = at24; + at24->cdev.dev = dev; + at24->cdev.ops = &at24->fops; + at24->fops.lseek = dev_lseek_default; + at24->fops.read = at24_cdev_read, + at24->cdev.size = chip.byte_len; + + writable = !(chip.flags & AT24_FLAG_READONLY); + if (writable) { + unsigned write_max = chip.page_size; + + at24->fops.write = at24_cdev_write; + + if (write_max > io_limit) + write_max = io_limit; + at24->write_max = write_max; + + /* buffer (data + address at the beginning) */ + at24->writebuf = xmalloc(write_max + 2); + } + + at24->client[0] = client; + + /* use dummy devices for multiple-address chips */ + for (i = 1; i < num_addresses; i++) { + at24->client[i] = i2c_new_dummy(client->adapter, + client->addr + i); + if (!at24->client[i]) { + dev_err(&client->dev, "address 0x%02x unavailable\n", + client->addr + i); + err = -EADDRINUSE; + goto err_clients; + } + } + + devfs_create(&at24->cdev); + + return 0; + +err_clients: + kfree(at24->writebuf); + kfree(at24); +err_out: + dev_dbg(&client->dev, "probe error %d\n", err); + return err; + +} + +static struct driver_d at24_driver = { + .name = "at24", + .probe = at24_probe, + .id_table = at24_ids, +}; + +static int at24_init(void) +{ + i2c_register_driver(&at24_driver); + return 0; +} +device_initcall(at24_init); diff --git a/include/i2c/at24.h b/include/i2c/at24.h new file mode 100644 index 000000000..101330845 --- /dev/null +++ b/include/i2c/at24.h @@ -0,0 +1,35 @@ +/* + * at24.h - platform_data for the at24 (generic eeprom) driver + * (C) Copyright 2008 by Pengutronix + * (C) Copyright 2012 by Wolfram Sang + * same license as the driver + */ + +#ifndef _LINUX_AT24_H +#define _LINUX_AT24_H + +#include + +/** + * struct at24_platform_data - data to set up at24 (generic eeprom) driver + * @byte_len: size of eeprom in byte + * @page_size: number of byte which can be written in one go + * @flags: tunable options, check AT24_FLAG_* defines + * + * If you set up a custom eeprom type, please double-check the parameters. + * Especially page_size needs extra care, as you risk data loss if your value + * is bigger than what the chip actually supports! + * + */ + +struct at24_platform_data { + u32 byte_len; /* size (sum of all addr) */ + u16 page_size; /* for writes */ + u8 flags; +#define AT24_FLAG_ADDR16 0x80 /* address pointer is 16 bit */ +#define AT24_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */ +#define AT24_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */ +#define AT24_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */ +}; + +#endif /* _LINUX_AT24_H */