summaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses')
-rw-r--r--drivers/i2c/busses/Kconfig16
-rw-r--r--drivers/i2c/busses/Makefile3
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c2
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c2
-rw-r--r--drivers/i2c/busses/i2c-designware.c488
-rw-r--r--drivers/i2c/busses/i2c-i801.c3
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c6
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c3
-rw-r--r--drivers/i2c/busses/i2c-omap.c64
-rw-r--r--drivers/i2c/busses/i2c-pnx.c12
-rw-r--r--drivers/i2c/busses/i2c-powermac.c139
-rw-r--r--drivers/i2c/busses/i2c-scmi.c2
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c2
-rw-r--r--drivers/i2c/busses/i2c-sis630.c2
-rw-r--r--drivers/i2c/busses/i2c-stub.c34
-rw-r--r--drivers/i2c/busses/i2c-voodoo3.c248
16 files changed, 518 insertions, 508 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index e8fe7f169e2..5f318ce2977 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -640,22 +640,6 @@ config I2C_TINY_USB
This driver can also be built as a module. If so, the module
will be called i2c-tiny-usb.
-comment "Graphics adapter I2C/DDC channel drivers"
- depends on PCI
-
-config I2C_VOODOO3
- tristate "Voodoo 3 (DEPRECATED)"
- depends on PCI
- select I2C_ALGOBIT
- help
- If you say yes to this option, support will be included for the
- Voodoo 3 I2C interface. This driver is deprecated and you should
- use the tdfxfb driver instead, which additionally provides
- framebuffer support.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-voodoo3.
-
comment "Other I2C/SMBus bus drivers"
config I2C_ACORN
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index ff937ac69f5..302c551977b 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -61,9 +61,6 @@ 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
-# Graphics adapter I2C/DDC channel drivers
-obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
-
# Other I2C/SMBus bus drivers
obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index d108450df06..8de7d7b87bb 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -138,7 +138,7 @@ static unsigned short ali1535_smba;
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 ali1535_setup(struct pci_dev *dev)
+static int __devinit ali1535_setup(struct pci_dev *dev)
{
int retval = -ENODEV;
unsigned char temp;
diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c
index d627fceb790..e7e3205f128 100644
--- a/drivers/i2c/busses/i2c-ali15x3.c
+++ b/drivers/i2c/busses/i2c-ali15x3.c
@@ -131,7 +131,7 @@ MODULE_PARM_DESC(force_addr,
static struct pci_driver ali15x3_driver;
static unsigned short ali15x3_smba;
-static int ali15x3_setup(struct pci_dev *ALI15X3_dev)
+static int __devinit ali15x3_setup(struct pci_dev *ALI15X3_dev)
{
u16 a;
unsigned char temp;
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c
index b444762e9b9..9e18ef97f15 100644
--- a/drivers/i2c/busses/i2c-designware.c
+++ b/drivers/i2c/busses/i2c-designware.c
@@ -1,5 +1,5 @@
/*
- * Synopsys Designware I2C adapter driver (master only).
+ * Synopsys DesignWare I2C adapter driver (master only).
*
* Based on the TI DAVINCI I2C adapter driver.
*
@@ -49,7 +49,20 @@
#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
@@ -64,9 +77,23 @@
#define DW_IC_CON_RESTART_EN 0x20
#define DW_IC_CON_SLAVE_DISABLE 0x40
-#define DW_IC_INTR_TX_EMPTY 0x10
-#define DW_IC_INTR_TX_ABRT 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
@@ -96,31 +123,49 @@
#define ABRT_SBYTE_ACKDET 7
#define ABRT_SBYTE_NORSTRT 9
#define ABRT_10B_RD_NORSTRT 10
-#define ARB_MASTER_DIS 11
+#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] =
+ [ABRT_7B_ADDR_NOACK] =
"slave address not acknowledged (7bit mode)",
- [ABRT_10ADDR1_NOACK] =
+ [ABRT_10ADDR1_NOACK] =
"first address byte not acknowledged (10bit mode)",
- [ABRT_10ADDR2_NOACK] =
+ [ABRT_10ADDR2_NOACK] =
"second address byte not acknowledged (10bit mode)",
- [ABRT_TXDATA_NOACK] =
+ [ABRT_TXDATA_NOACK] =
"data not acknowledged",
- [ABRT_GCALL_NOACK] =
+ [ABRT_GCALL_NOACK] =
"no acknowledgement for a general call",
- [ABRT_GCALL_READ] =
+ [ABRT_GCALL_READ] =
"read after general call",
- [ABRT_SBYTE_ACKDET] =
+ [ABRT_SBYTE_ACKDET] =
"start byte acknowledged",
- [ABRT_SBYTE_NORSTRT] =
+ [ABRT_SBYTE_NORSTRT] =
"trying to send start byte when restart is disabled",
- [ABRT_10B_RD_NORSTRT] =
+ [ABRT_10B_RD_NORSTRT] =
"trying to read when restart is disabled (10bit mode)",
- [ARB_MASTER_DIS] =
+ [ABRT_MASTER_DIS] =
"trying to use disabled adapter",
- [ARB_LOST] =
+ [ARB_LOST] =
"lost arbitration",
};
@@ -129,7 +174,6 @@ static char *abort_sources[] = {
* @dev: driver model device node
* @base: IO registers pointer
* @cmd_complete: tx completion indicator
- * @pump_msg: continue in progress transfers
* @lock: protect this struct and IO registers
* @clk: input reference clock
* @cmd_err: run time hadware error code
@@ -155,27 +199,81 @@ struct dw_i2c_dev {
struct device *dev;
void __iomem *base;
struct completion cmd_complete;
- struct tasklet_struct pump_msg;
struct mutex lock;
struct clk *clk;
int cmd_err;
struct i2c_msg *msgs;
int msgs_num;
int msg_write_idx;
- u16 tx_buf_len;
+ u32 tx_buf_len;
u8 *tx_buf;
int msg_read_idx;
- u16 rx_buf_len;
+ u32 rx_buf_len;
u8 *rx_buf;
int msg_err;
unsigned int status;
- u16 abort_source;
+ 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
@@ -187,25 +285,49 @@ struct dw_i2c_dev {
static void i2c_dw_init(struct dw_i2c_dev *dev)
{
u32 input_clock_khz = clk_get_rate(dev->clk) / 1000;
- u16 ic_con;
+ u32 ic_con, hcnt, lcnt;
/* Disable the adapter */
- writeb(0, dev->base + DW_IC_ENABLE);
+ writel(0, dev->base + DW_IC_ENABLE);
/* set standard and fast speed deviders for high/low periods */
- writew((input_clock_khz * 40 / 10000)+1, /* std speed high, 4us */
- dev->base + DW_IC_SS_SCL_HCNT);
- writew((input_clock_khz * 47 / 10000)+1, /* std speed low, 4.7us */
- dev->base + DW_IC_SS_SCL_LCNT);
- writew((input_clock_khz * 6 / 10000)+1, /* fast speed high, 0.6us */
- dev->base + DW_IC_FS_SCL_HCNT);
- writew((input_clock_khz * 13 / 10000)+1, /* fast speed low, 1.3us */
- dev->base + DW_IC_FS_SCL_LCNT);
+
+ /* 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;
- writew(ic_con, dev->base + DW_IC_CON);
+ writel(ic_con, dev->base + DW_IC_CON);
}
/*
@@ -215,7 +337,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
{
int timeout = TIMEOUT;
- while (readb(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
+ 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;
@@ -227,106 +349,125 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
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 low level master read/write transaction.
- * This function is called from i2c_dw_xfer when starting a transfer.
- * This function is also called from dw_i2c_pump_msg to continue a transfer
- * that is longer than the size of the TX FIFO.
+ * 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 i2c_adapter *adap)
+i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
struct i2c_msg *msgs = dev->msgs;
- int num = dev->msgs_num;
- u16 ic_con, intr_mask;
- int tx_limit = dev->tx_fifo_depth - readb(dev->base + DW_IC_TXFLR);
- int rx_limit = dev->rx_fifo_depth - readb(dev->base + DW_IC_RXFLR);
- u16 addr = msgs[dev->msg_write_idx].addr;
- u16 buf_len = dev->tx_buf_len;
-
- if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
- /* Disable the adapter */
- writeb(0, dev->base + DW_IC_ENABLE);
-
- /* set the slave (target) address */
- writew(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+ 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;;
- /* if the slave address is ten bit address, enable 10BITADDR */
- ic_con = readw(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;
- writew(ic_con, dev->base + DW_IC_CON);
+ intr_mask = DW_IC_INTR_DEFAULT_MASK;
- /* Enable the adapter */
- writeb(1, dev->base + DW_IC_ENABLE);
- }
-
- for (; dev->msg_write_idx < num; dev->msg_write_idx++) {
- /* if target address has changed, we need to
+ 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)
- return;
+ 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;
- return;
+ break;
}
if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
/* new i2c_msg */
- dev->tx_buf = msgs[dev->msg_write_idx].buf;
+ 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) {
- writew(0x100, dev->base + DW_IC_DATA_CMD);
+ writel(0x100, dev->base + DW_IC_DATA_CMD);
rx_limit--;
} else
- writew(*(dev->tx_buf++),
- dev->base + DW_IC_DATA_CMD);
+ 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;
}
- intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
- if (buf_len > 0) { /* more bytes to be written */
- intr_mask |= DW_IC_INTR_TX_EMPTY;
- dev->status |= STATUS_WRITE_IN_PROGRESS;
- } else
- dev->status &= ~STATUS_WRITE_IN_PROGRESS;
- writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+ /*
+ * 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;
- dev->tx_buf_len = buf_len;
+ writel(intr_mask, dev->base + DW_IC_INTR_MASK);
}
static void
-i2c_dw_read(struct i2c_adapter *adap)
+i2c_dw_read(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
struct i2c_msg *msgs = dev->msgs;
- int num = dev->msgs_num;
- u16 addr = msgs[dev->msg_read_idx].addr;
- int rx_valid = readw(dev->base + DW_IC_RXFLR);
+ int rx_valid;
- for (; dev->msg_read_idx < num; dev->msg_read_idx++) {
- u16 len;
+ 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;
- /* different i2c client, reprogram the i2c adapter */
- if (msgs[dev->msg_read_idx].addr != addr)
- return;
-
if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
len = msgs[dev->msg_read_idx].len;
buf = msgs[dev->msg_read_idx].buf;
@@ -335,8 +476,10 @@ i2c_dw_read(struct i2c_adapter *adap)
buf = dev->rx_buf;
}
+ rx_valid = readl(dev->base + DW_IC_RXFLR);
+
for (; len > 0 && rx_valid > 0; len--, rx_valid--)
- *buf++ = readb(dev->base + DW_IC_DATA_CMD);
+ *buf++ = readl(dev->base + DW_IC_DATA_CMD);
if (len > 0) {
dev->status |= STATUS_READ_IN_PROGRESS;
@@ -348,6 +491,29 @@ i2c_dw_read(struct i2c_adapter *adap)
}
}
+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_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
+ dev_dbg(dev->dev,
+ "%s: %s\n", __func__, abort_sources[i]);
+ return -EREMOTEIO;
+ }
+
+ for_each_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
*/
@@ -369,13 +535,14 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
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_msg(adap);
+ i2c_dw_xfer_init(dev);
/* wait for tx to complete */
ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ);
@@ -394,23 +561,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
/* no error */
if (likely(!dev->cmd_err)) {
- /* read rx fifo, and disable the adapter */
- do {
- i2c_dw_read(adap);
- } while (dev->status & STATUS_READ_IN_PROGRESS);
- writeb(0, dev->base + DW_IC_ENABLE);
+ /* 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) {
- unsigned long abort_source = dev->abort_source;
- int i;
-
- for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) {
- dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
- }
+ ret = i2c_dw_handle_tx_abort(dev);
+ goto done;
}
ret = -EIO;
@@ -422,21 +582,67 @@ done:
static u32 i2c_dw_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
+ 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 void dw_i2c_pump_msg(unsigned long data)
+static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = (struct dw_i2c_dev *) data;
- u16 intr_mask;
-
- i2c_dw_read(&dev->adapter);
- i2c_dw_xfer_msg(&dev->adapter);
-
- intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
- if (dev->status & STATUS_WRITE_IN_PROGRESS)
- intr_mask |= DW_IC_INTR_TX_EMPTY;
- writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+ 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;
}
/*
@@ -446,20 +652,37 @@ static void dw_i2c_pump_msg(unsigned long data)
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
{
struct dw_i2c_dev *dev = dev_id;
- u16 stat;
+ u32 stat;
- stat = readw(dev->base + DW_IC_INTR_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->abort_source = readw(dev->base + DW_IC_TX_ABRT_SOURCE);
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
dev->status = STATUS_IDLE;
- } else if (stat & DW_IC_INTR_TX_EMPTY)
- tasklet_schedule(&dev->pump_msg);
- readb(dev->base + DW_IC_CLR_INTR); /* clear interrupts */
- writew(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
- if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
+ /*
+ * 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;
@@ -474,8 +697,8 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
{
struct dw_i2c_dev *dev;
struct i2c_adapter *adap;
- struct resource *mem, *irq, *ioarea;
- int r;
+ struct resource *mem, *ioarea;
+ int irq, r;
/* NOTE: driver uses the static register mapping */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -484,10 +707,10 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
return -EINVAL;
}
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq) {
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
- return -EINVAL;
+ return irq; /* -ENXIO */
}
ioarea = request_mem_region(mem->start, resource_size(mem),
@@ -504,10 +727,9 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
}
init_completion(&dev->cmd_complete);
- tasklet_init(&dev->pump_msg, dw_i2c_pump_msg, (unsigned long) dev);
mutex_init(&dev->lock);
dev->dev = get_device(&pdev->dev);
- dev->irq = irq->start;
+ dev->irq = irq;
platform_set_drvdata(pdev, dev);
dev->clk = clk_get(&pdev->dev, NULL);
@@ -531,8 +753,8 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
}
i2c_dw_init(dev);
- writew(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
- r = request_irq(dev->irq, i2c_dw_isr, 0, pdev->name, 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;
@@ -587,7 +809,7 @@ static int __devexit dw_i2c_remove(struct platform_device *pdev)
clk_put(dev->clk);
dev->clk = NULL;
- writeb(0, dev->base + DW_IC_ENABLE);
+ writel(0, dev->base + DW_IC_ENABLE);
free_irq(dev->irq, dev);
kfree(dev);
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 55edcfe5b85..df6ab553f97 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -767,6 +767,9 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
/* set up the sysfs linkage to our parent device */
i801_adapter.dev.parent = &dev->dev;
+ /* Retry up to 3 times on lost arbitration */
+ i801_adapter.retries = 3;
+
snprintf(i801_adapter.name, sizeof(i801_adapter.name),
"SMBus I801 adapter at %04lx", i801_smba);
err = i2c_add_adapter(&i801_adapter);
diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c
index a75c75e77b9..5901707fc66 100644
--- a/drivers/i2c/busses/i2c-iop3xx.c
+++ b/drivers/i2c/busses/i2c-iop3xx.c
@@ -56,12 +56,6 @@ iic_cook_addr(struct i2c_msg *msg)
if (msg->flags & I2C_M_RD)
addr |= 1;
- /*
- * Read or Write?
- */
- if (msg->flags & I2C_M_REV_DIR_ADDR)
- addr ^= 1;
-
return addr;
}
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index bbab0e16663..ed387ffa473 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -338,9 +338,6 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
if (msg->flags & I2C_M_RD)
dir = 1;
- if (msg->flags & I2C_M_REV_DIR_ADDR)
- 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;
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 827da085813..75bf3ad1809 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -178,6 +178,12 @@ struct omap_i2c_dev {
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;
};
static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
@@ -230,9 +236,18 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev)
clk_enable(dev->iclk);
clk_enable(dev->fclk);
+ 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;
- if (dev->iestate)
- omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
+ omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
}
static void omap_i2c_idle(struct omap_i2c_dev *dev)
@@ -258,7 +273,7 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev)
static int omap_i2c_init(struct omap_i2c_dev *dev)
{
- u16 psc = 0, scll = 0, sclh = 0;
+ 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;
@@ -287,24 +302,22 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
SYSC_AUTOIDLE_MASK);
} else if (dev->rev >= OMAP_I2C_REV_ON_3430) {
- u32 v;
-
- v = SYSC_AUTOIDLE_MASK;
- v |= SYSC_ENAWAKEUP_MASK;
- v |= (SYSC_IDLEMODE_SMART <<
+ dev->syscstate = SYSC_AUTOIDLE_MASK;
+ dev->syscstate |= SYSC_ENAWAKEUP_MASK;
+ dev->syscstate |= (SYSC_IDLEMODE_SMART <<
__ffs(SYSC_SIDLEMODE_MASK));
- v |= (SYSC_CLOCKACTIVITY_FCLK <<
+ dev->syscstate |= (SYSC_CLOCKACTIVITY_FCLK <<
__ffs(SYSC_CLOCKACTIVITY_MASK));
- omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, v);
+ 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.
*/
- omap_i2c_write_reg(dev, OMAP_I2C_WE_REG,
- OMAP_I2C_WE_ALL);
-
+ 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);
@@ -394,23 +407,28 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
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 */
- omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
- (dev->fifo_size - 1) << 8 | /* RTRSH */
- OMAP_I2C_BUF_RXFIF_CLR |
- (dev->fifo_size - 1) | /* XTRSH */
- OMAP_I2C_BUF_TXFIF_CLR);
+ 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);
/* Enable interrupts */
- omap_i2c_write_reg(dev, OMAP_I2C_IE_REG,
- (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
+ 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_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;
}
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index 6ff6c20f1e7..5d1c2603a13 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -19,7 +19,9 @@
#include <linux/completion.h>
#include <linux/platform_device.h>
#include <linux/i2c-pnx.h>
+#include <linux/io.h>
#include <mach/hardware.h>
+#include <mach/i2c.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
@@ -54,6 +56,9 @@ static inline void i2c_pnx_arm_timer(struct i2c_adapter *adap)
struct timer_list *timer = &data->mif.timer;
int expires = I2C_PNX_TIMEOUT / (1000 / HZ);
+ if (expires <= 1)
+ expires = 2;
+
del_timer_sync(timer);
dev_dbg(&adap->dev, "Timer armed at %lu plus %u jiffies.\n",
@@ -633,7 +638,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
/* Register this adapter with the I2C subsystem */
i2c_pnx->adapter->dev.parent = &pdev->dev;
- ret = i2c_add_adapter(i2c_pnx->adapter);
+ i2c_pnx->adapter->nr = pdev->id;
+ ret = i2c_add_numbered_adapter(i2c_pnx->adapter);
if (ret < 0) {
dev_err(&pdev->dev, "I2C: Failed to add bus\n");
goto out_irq;
@@ -645,7 +651,7 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
return 0;
out_irq:
- free_irq(alg_data->irq, alg_data);
+ free_irq(alg_data->irq, i2c_pnx->adapter);
out_clock:
i2c_pnx->set_clock_stop(pdev);
out_unmap:
@@ -664,7 +670,7 @@ static int __devexit i2c_pnx_remove(struct platform_device *pdev)
struct i2c_adapter *adap = i2c_pnx->adapter;
struct i2c_pnx_algo_data *alg_data = adap->algo_data;
- free_irq(alg_data->irq, alg_data);
+ free_irq(alg_data->irq, i2c_pnx->adapter);
i2c_del_adapter(adap);
i2c_pnx->set_clock_stop(pdev);
iounmap((void *)alg_data->ioaddr);
diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c
index 3c9d71f6018..1c440a70ec6 100644
--- a/drivers/i2c/busses/i2c-powermac.c
+++ b/drivers/i2c/busses/i2c-powermac.c
@@ -49,48 +49,38 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* 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];
- rc = pmac_i2c_open(bus, 0);
- if (rc)
- return rc;
+ 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:
- rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 0, 0, NULL, 0);
+ buf = NULL;
+ len = 0;
break;
case I2C_SMBUS_BYTE:
- rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 0, 0, &data->byte, 1);
- break;
case I2C_SMBUS_BYTE_DATA:
- rc = pmac_i2c_setmode(bus, read ?
- pmac_i2c_mode_combined :
- pmac_i2c_mode_stdsub);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 1, command, &data->byte, 1);
+ buf = &data->byte;
+ len = 1;
break;
case I2C_SMBUS_WORD_DATA:
- rc = pmac_i2c_setmode(bus, read ?
- pmac_i2c_mode_combined :
- pmac_i2c_mode_stdsub);
- if (rc)
- goto bail;
if (!read) {
local[0] = data->word & 0xff;
local[1] = (data->word >> 8) & 0xff;
}
- rc = pmac_i2c_xfer(bus, addrdir, 1, command, local, 2);
- if (rc == 0 && read) {
- data->word = ((u16)local[1]) << 8;
- data->word |= local[0];
- }
+ buf = local;
+ len = 2;
break;
/* Note that these are broken vs. the expected smbus API where
@@ -105,28 +95,44 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap,
* a repeat start/addr phase (but not stop in between)
*/
case I2C_SMBUS_BLOCK_DATA:
- rc = pmac_i2c_setmode(bus, read ?
- pmac_i2c_mode_combined :
- pmac_i2c_mode_stdsub);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 1, command, data->block,
- data->block[0] + 1);
-
+ buf = data->block;
+ len = data->block[0] + 1;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
- rc = pmac_i2c_setmode(bus, read ?
- pmac_i2c_mode_combined :
- pmac_i2c_mode_stdsub);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 1, command,
- &data->block[1], data->block[0]);
+ buf = &data->block[1];
+ len = data->block[0];
break;
default:
- rc = -EINVAL;
+ 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) {
+ 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;
@@ -146,20 +152,33 @@ static int i2c_powermac_master_xfer( struct i2c_adapter *adap,
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;
- if (msgs->flags & I2C_M_REV_DIR_ADDR)
- addrdir ^= 1;
rc = pmac_i2c_open(bus, 0);
- if (rc)
+ 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)
+ 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)
+ 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;
@@ -183,19 +202,16 @@ static const struct i2c_algorithm i2c_powermac_algorithm = {
static int __devexit i2c_powermac_remove(struct platform_device *dev)
{
struct i2c_adapter *adapter = platform_get_drvdata(dev);
- struct pmac_i2c_bus *bus = i2c_get_adapdata(adapter);
int rc;
rc = i2c_del_adapter(adapter);
- pmac_i2c_detach_adapter(bus, adapter);
- i2c_set_adapdata(adapter, NULL);
/* 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);
- kfree(adapter);
+ memset(adapter, 0, sizeof(*adapter));
return 0;
}
@@ -206,12 +222,12 @@ 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;
- char name[32];
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
@@ -237,29 +253,22 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev)
default:
return -EINVAL;
}
- snprintf(name, 32, "%s %d", basename, pmac_i2c_get_channel(bus));
+ snprintf(adapter->name, sizeof(adapter->name), "%s %d", basename,
+ pmac_i2c_get_channel(bus));
of_node_put(parent);
- adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
- if (adapter == NULL) {
- printk(KERN_ERR "i2c-powermac: can't allocate inteface !\n");
- return -ENOMEM;
- }
platform_set_drvdata(dev, adapter);
- strcpy(adapter->name, name);
adapter->algo = &i2c_powermac_algorithm;
i2c_set_adapdata(adapter, bus);
adapter->dev.parent = &dev->dev;
- pmac_i2c_attach_adapter(bus, adapter);
rc = i2c_add_adapter(adapter);
if (rc) {
printk(KERN_ERR "i2c-powermac: Adapter %s registration "
- "failed\n", name);
- i2c_set_adapdata(adapter, NULL);
- pmac_i2c_detach_adapter(bus, adapter);
+ "failed\n", adapter->name);
+ memset(adapter, 0, sizeof(*adapter));
}
- printk(KERN_INFO "PowerMac i2c bus %s registered\n", name);
+ printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name);
if (!strncmp(basename, "uni-n", 5)) {
struct device_node *np;
diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c
index b4a55d407bf..365e0becaf1 100644
--- a/drivers/i2c/busses/i2c-scmi.c
+++ b/drivers/i2c/busses/i2c-scmi.c
@@ -363,7 +363,7 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
smbus_cmi->cap_write = 0;
acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
- acpi_smbus_cmi_query_methods, smbus_cmi, NULL);
+ acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL);
if (smbus_cmi->cap_info == 0)
goto err;
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c
index 139f0c7f12a..844569f7d8b 100644
--- a/drivers/i2c/busses/i2c-sis5595.c
+++ b/drivers/i2c/busses/i2c-sis5595.c
@@ -142,7 +142,7 @@ static void sis5595_write(u8 reg, u8 data)
outb(data, sis5595_base + SMB_DAT);
}
-static int sis5595_setup(struct pci_dev *SIS5595_dev)
+static int __devinit sis5595_setup(struct pci_dev *SIS5595_dev)
{
u16 a;
u8 val;
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index 70ca41e90e5..68cff7af701 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -389,7 +389,7 @@ static u32 sis630_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA;
}
-static int sis630_setup(struct pci_dev *sis630_dev)
+static int __devinit sis630_setup(struct pci_dev *sis630_dev)
{
unsigned char b;
struct pci_dev *dummy = NULL;
diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c
index 1b7b2af9403..0c770eabe85 100644
--- a/drivers/i2c/busses/i2c-stub.c
+++ b/drivers/i2c/busses/i2c-stub.c
@@ -35,6 +35,10 @@ 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 = ~0UL;
+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
@@ -48,7 +52,7 @@ 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;
+ int i, len;
struct stub_chip *chip = NULL;
/* Search for the right chip */
@@ -118,6 +122,29 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
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;
@@ -129,8 +156,9 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
static u32 stub_func(struct i2c_adapter *adapter)
{
- return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA;
+ return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK) & functionality;
}
static const struct i2c_algorithm smbus_algorithm = {
diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c
deleted file mode 100644
index 7663d57833a..00000000000
--- a/drivers/i2c/busses/i2c-voodoo3.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>,
- Philip Edelbrock <phil@netroedge.com>,
- Ralph Metzler <rjkm@thp.uni-koeln.de>, and
- Mark D. Studebaker <mdsxyz123@yahoo.com>
-
- Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
- Simon 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.
-*/
-
-/* This interfaces to the I2C bus of the Voodoo3 to gain access to
- the BT869 and possibly other I2C devices. */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <asm/io.h>
-
-/* the only registers we use */
-#define REG 0x78
-#define REG2 0x70
-
-/* bit locations in the register */
-#define DDC_ENAB 0x00040000
-#define DDC_SCL_OUT 0x00080000
-#define DDC_SDA_OUT 0x00100000
-#define DDC_SCL_IN 0x00200000
-#define DDC_SDA_IN 0x00400000
-#define I2C_ENAB 0x00800000
-#define I2C_SCL_OUT 0x01000000
-#define I2C_SDA_OUT 0x02000000
-#define I2C_SCL_IN 0x04000000
-#define I2C_SDA_IN 0x08000000
-
-/* initialization states */
-#define INIT2 0x2
-#define INIT3 0x4
-
-/* delays */
-#define CYCLE_DELAY 10
-#define TIMEOUT (HZ / 2)
-
-
-static void __iomem *ioaddr;
-
-/* The voo GPIO registers don't have individual masks for each bit
- so we always have to read before writing. */
-
-static void bit_vooi2c_setscl(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if (val)
- r |= I2C_SCL_OUT;
- else
- r &= ~I2C_SCL_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-static void bit_vooi2c_setsda(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if (val)
- r |= I2C_SDA_OUT;
- else
- r &= ~I2C_SDA_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-/* The GPIO pins are open drain, so the pins always remain outputs.
- We rely on the i2c-algo-bit routines to set the pins high before
- reading the input from other chips. */
-
-static int bit_vooi2c_getscl(void *data)
-{
- return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
-}
-
-static int bit_vooi2c_getsda(void *data)
-{
- return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
-}
-
-static void bit_vooddc_setscl(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if (val)
- r |= DDC_SCL_OUT;
- else
- r &= ~DDC_SCL_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-static void bit_vooddc_setsda(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if (val)
- r |= DDC_SDA_OUT;
- else
- r &= ~DDC_SDA_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-static int bit_vooddc_getscl(void *data)
-{
- return (0 != (readl(ioaddr + REG) & DDC_SCL_IN));
-}
-
-static int bit_vooddc_getsda(void *data)
-{
- return (0 != (readl(ioaddr + REG) & DDC_SDA_IN));
-}
-
-static int config_v3(struct pci_dev *dev)
-{
- unsigned long cadr;
-
- /* map Voodoo3 memory */
- cadr = dev->resource[0].start;
- cadr &= PCI_BASE_ADDRESS_MEM_MASK;
- ioaddr = ioremap_nocache(cadr, 0x1000);
- if (ioaddr) {
- writel(0x8160, ioaddr + REG2);
- writel(0xcffc0020, ioaddr + REG);
- dev_info(&dev->dev, "Using Banshee/Voodoo3 I2C device at %p\n", ioaddr);
- return 0;
- }
- return -ENODEV;
-}
-
-static struct i2c_algo_bit_data voo_i2c_bit_data = {
- .setsda = bit_vooi2c_setsda,
- .setscl = bit_vooi2c_setscl,
- .getsda = bit_vooi2c_getsda,
- .getscl = bit_vooi2c_getscl,
- .udelay = CYCLE_DELAY,
- .timeout = TIMEOUT
-};
-
-static struct i2c_adapter voodoo3_i2c_adapter = {
- .owner = THIS_MODULE,
- .name = "I2C Voodoo3/Banshee adapter",
- .algo_data = &voo_i2c_bit_data,
-};
-
-static struct i2c_algo_bit_data voo_ddc_bit_data = {
- .setsda = bit_vooddc_setsda,
- .setscl = bit_vooddc_setscl,
- .getsda = bit_vooddc_getsda,
- .getscl = bit_vooddc_getscl,
- .udelay = CYCLE_DELAY,
- .timeout = TIMEOUT
-};
-
-static struct i2c_adapter voodoo3_ddc_adapter = {
- .owner = THIS_MODULE,
- .class = I2C_CLASS_DDC,
- .name = "DDC Voodoo3/Banshee adapter",
- .algo_data = &voo_ddc_bit_data,
-};
-
-static struct pci_device_id voodoo3_ids[] __devinitdata = {
- { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3) },
- { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE) },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE (pci, voodoo3_ids);
-
-static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id)
-{
- int retval;
-
- retval = config_v3(dev);
- if (retval)
- return retval;
-
- /* set up the sysfs linkage to our parent device */
- voodoo3_i2c_adapter.dev.parent = &dev->dev;
- voodoo3_ddc_adapter.dev.parent = &dev->dev;
-
- retval = i2c_bit_add_bus(&voodoo3_i2c_adapter);
- if (retval)
- return retval;
- retval = i2c_bit_add_bus(&voodoo3_ddc_adapter);
- if (retval)
- i2c_del_adapter(&voodoo3_i2c_adapter);
- return retval;
-}
-
-static void __devexit voodoo3_remove(struct pci_dev *dev)
-{
- i2c_del_adapter(&voodoo3_i2c_adapter);
- i2c_del_adapter(&voodoo3_ddc_adapter);
- iounmap(ioaddr);
-}
-
-static struct pci_driver voodoo3_driver = {
- .name = "voodoo3_smbus",
- .id_table = voodoo3_ids,
- .probe = voodoo3_probe,
- .remove = __devexit_p(voodoo3_remove),
-};
-
-static int __init i2c_voodoo3_init(void)
-{
- return pci_register_driver(&voodoo3_driver);
-}
-
-static void __exit i2c_voodoo3_exit(void)
-{
- pci_unregister_driver(&voodoo3_driver);
-}
-
-
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
- "Philip Edelbrock <phil@netroedge.com>, "
- "Ralph Metzler <rjkm@thp.uni-koeln.de>, "
- "and Mark D. Studebaker <mdsxyz123@yahoo.com>");
-MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver");
-MODULE_LICENSE("GPL");
-
-module_init(i2c_voodoo3_init);
-module_exit(i2c_voodoo3_exit);