From fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 Mon Sep 17 00:00:00 2001 From: Jonathan Herman Date: Tue, 22 Jan 2013 10:38:37 -0500 Subject: Added missing tegra files. --- drivers/i2c/busses/i2c-designware.c | 847 ++++++++++++++++++++++++++ drivers/i2c/busses/i2c-ixp2000.c | 168 +++++ drivers/i2c/busses/i2c-slave-tegra.c | 1114 ++++++++++++++++++++++++++++++++++ drivers/i2c/busses/i2c-stub.c | 222 +++++++ drivers/i2c/i2c-slave.c | 281 +++++++++ drivers/i2c/muxes/gpio-i2cmux.c | 184 ++++++ drivers/i2c/muxes/pca9541.c | 411 +++++++++++++ drivers/i2c/muxes/pca954x.c | 404 ++++++++++++ 8 files changed, 3631 insertions(+) create mode 100644 drivers/i2c/busses/i2c-designware.c create mode 100644 drivers/i2c/busses/i2c-ixp2000.c create mode 100644 drivers/i2c/busses/i2c-slave-tegra.c create mode 100644 drivers/i2c/busses/i2c-stub.c create mode 100644 drivers/i2c/i2c-slave.c create mode 100644 drivers/i2c/muxes/gpio-i2cmux.c create mode 100644 drivers/i2c/muxes/pca9541.c create mode 100644 drivers/i2c/muxes/pca954x.c (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c new file mode 100644 index 00000000000..b7a51c43b18 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware.c @@ -0,0 +1,847 @@ +/* + * Synopsys DesignWare I2C adapter driver (master only). + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * + * ---------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ---------------------------------------------------------------------------- + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Registers offset + */ +#define DW_IC_CON 0x0 +#define DW_IC_TAR 0x4 +#define DW_IC_DATA_CMD 0x10 +#define DW_IC_SS_SCL_HCNT 0x14 +#define DW_IC_SS_SCL_LCNT 0x18 +#define DW_IC_FS_SCL_HCNT 0x1c +#define DW_IC_FS_SCL_LCNT 0x20 +#define DW_IC_INTR_STAT 0x2c +#define DW_IC_INTR_MASK 0x30 +#define DW_IC_RAW_INTR_STAT 0x34 +#define DW_IC_RX_TL 0x38 +#define DW_IC_TX_TL 0x3c +#define DW_IC_CLR_INTR 0x40 +#define DW_IC_CLR_RX_UNDER 0x44 +#define DW_IC_CLR_RX_OVER 0x48 +#define DW_IC_CLR_TX_OVER 0x4c +#define DW_IC_CLR_RD_REQ 0x50 +#define DW_IC_CLR_TX_ABRT 0x54 +#define DW_IC_CLR_RX_DONE 0x58 +#define DW_IC_CLR_ACTIVITY 0x5c +#define DW_IC_CLR_STOP_DET 0x60 +#define DW_IC_CLR_START_DET 0x64 +#define DW_IC_CLR_GEN_CALL 0x68 +#define DW_IC_ENABLE 0x6c +#define DW_IC_STATUS 0x70 +#define DW_IC_TXFLR 0x74 +#define DW_IC_RXFLR 0x78 +#define DW_IC_COMP_PARAM_1 0xf4 +#define DW_IC_TX_ABRT_SOURCE 0x80 + +#define DW_IC_CON_MASTER 0x1 +#define DW_IC_CON_SPEED_STD 0x2 +#define DW_IC_CON_SPEED_FAST 0x4 +#define DW_IC_CON_10BITADDR_MASTER 0x10 +#define DW_IC_CON_RESTART_EN 0x20 +#define DW_IC_CON_SLAVE_DISABLE 0x40 + +#define DW_IC_INTR_RX_UNDER 0x001 +#define DW_IC_INTR_RX_OVER 0x002 +#define DW_IC_INTR_RX_FULL 0x004 +#define DW_IC_INTR_TX_OVER 0x008 +#define DW_IC_INTR_TX_EMPTY 0x010 +#define DW_IC_INTR_RD_REQ 0x020 +#define DW_IC_INTR_TX_ABRT 0x040 +#define DW_IC_INTR_RX_DONE 0x080 +#define DW_IC_INTR_ACTIVITY 0x100 +#define DW_IC_INTR_STOP_DET 0x200 +#define DW_IC_INTR_START_DET 0x400 +#define DW_IC_INTR_GEN_CALL 0x800 + +#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \ + DW_IC_INTR_TX_EMPTY | \ + DW_IC_INTR_TX_ABRT | \ + DW_IC_INTR_STOP_DET) + +#define DW_IC_STATUS_ACTIVITY 0x1 + +#define DW_IC_ERR_TX_ABRT 0x1 + +/* + * status codes + */ +#define STATUS_IDLE 0x0 +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 + +#define TIMEOUT 20 /* ms */ + +/* + * hardware abort codes from the DW_IC_TX_ABRT_SOURCE register + * + * only expected abort codes are listed here + * refer to the datasheet for the full list + */ +#define ABRT_7B_ADDR_NOACK 0 +#define ABRT_10ADDR1_NOACK 1 +#define ABRT_10ADDR2_NOACK 2 +#define ABRT_TXDATA_NOACK 3 +#define ABRT_GCALL_NOACK 4 +#define ABRT_GCALL_READ 5 +#define ABRT_SBYTE_ACKDET 7 +#define ABRT_SBYTE_NORSTRT 9 +#define ABRT_10B_RD_NORSTRT 10 +#define ABRT_MASTER_DIS 11 +#define ARB_LOST 12 + +#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK) +#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK) +#define DW_IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK) +#define DW_IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK) +#define DW_IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK) +#define DW_IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ) +#define DW_IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET) +#define DW_IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT) +#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT) +#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS) +#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST) + +#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \ + DW_IC_TX_ABRT_10ADDR1_NOACK | \ + DW_IC_TX_ABRT_10ADDR2_NOACK | \ + DW_IC_TX_ABRT_TXDATA_NOACK | \ + DW_IC_TX_ABRT_GCALL_NOACK) + +static char *abort_sources[] = { + [ABRT_7B_ADDR_NOACK] = + "slave address not acknowledged (7bit mode)", + [ABRT_10ADDR1_NOACK] = + "first address byte not acknowledged (10bit mode)", + [ABRT_10ADDR2_NOACK] = + "second address byte not acknowledged (10bit mode)", + [ABRT_TXDATA_NOACK] = + "data not acknowledged", + [ABRT_GCALL_NOACK] = + "no acknowledgement for a general call", + [ABRT_GCALL_READ] = + "read after general call", + [ABRT_SBYTE_ACKDET] = + "start byte acknowledged", + [ABRT_SBYTE_NORSTRT] = + "trying to send start byte when restart is disabled", + [ABRT_10B_RD_NORSTRT] = + "trying to read when restart is disabled (10bit mode)", + [ABRT_MASTER_DIS] = + "trying to use disabled adapter", + [ARB_LOST] = + "lost arbitration", +}; + +/** + * struct dw_i2c_dev - private i2c-designware data + * @dev: driver model device node + * @base: IO registers pointer + * @cmd_complete: tx completion indicator + * @lock: protect this struct and IO registers + * @clk: input reference clock + * @cmd_err: run time hadware error code + * @msgs: points to an array of messages currently being transferred + * @msgs_num: the number of elements in msgs + * @msg_write_idx: the element index of the current tx message in the msgs + * array + * @tx_buf_len: the length of the current tx buffer + * @tx_buf: the current tx buffer + * @msg_read_idx: the element index of the current rx message in the msgs + * array + * @rx_buf_len: the length of the current rx buffer + * @rx_buf: the current rx buffer + * @msg_err: error status of the current transfer + * @status: i2c master status, one of STATUS_* + * @abort_source: copy of the TX_ABRT_SOURCE register + * @irq: interrupt number for the i2c master + * @adapter: i2c subsystem adapter node + * @tx_fifo_depth: depth of the hardware tx fifo + * @rx_fifo_depth: depth of the hardware rx fifo + */ +struct dw_i2c_dev { + struct device *dev; + void __iomem *base; + struct completion cmd_complete; + struct mutex lock; + struct clk *clk; + int cmd_err; + struct i2c_msg *msgs; + int msgs_num; + int msg_write_idx; + u32 tx_buf_len; + u8 *tx_buf; + int msg_read_idx; + u32 rx_buf_len; + u8 *rx_buf; + int msg_err; + unsigned int status; + u32 abort_source; + int irq; + struct i2c_adapter adapter; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; +}; + +static u32 +i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) +{ + /* + * DesignWare I2C core doesn't seem to have solid strategy to meet + * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec + * will result in violation of the tHD;STA spec. + */ + if (cond) + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH + * + * This is based on the DW manuals, and represents an ideal + * configuration. The resulting I2C bus speed will be + * faster than any of the others. + * + * If your hardware is free from tHD;STA issue, try this one. + */ + return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset; + else + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) + * + * This is just experimental rule; the tHD;STA period turned + * out to be proportinal to (_HCNT + 3). With this setting, + * we could meet both tHIGH and tHD;STA timing specs. + * + * If unsure, you'd better to take this alternative. + * + * The reason why we need to take into account "tf" here, + * is the same as described in i2c_dw_scl_lcnt(). + */ + return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset; +} + +static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) +{ + /* + * Conditional expression: + * + * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) + * + * DW I2C core starts counting the SCL CNTs for the LOW period + * of the SCL clock (tLOW) as soon as it pulls the SCL line. + * In order to meet the tLOW timing spec, we need to take into + * account the fall time of SCL signal (tf). Default tf value + * should be 0.3 us, for safety. + */ + return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset; +} + +/** + * i2c_dw_init() - initialize the designware i2c master hardware + * @dev: device private data + * + * This functions configures and enables the I2C master. + * This function is called during I2C init function, and in case of timeout at + * run time. + */ +static void i2c_dw_init(struct dw_i2c_dev *dev) +{ + u32 input_clock_khz = clk_get_rate(dev->clk) / 1000; + u32 ic_con, hcnt, lcnt; + + /* Disable the adapter */ + writel(0, dev->base + DW_IC_ENABLE); + + /* set standard and fast speed deviders for high/low periods */ + + /* Standard-mode */ + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 40, /* tHD;STA = tHIGH = 4.0 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 47, /* tLOW = 4.7 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT); + writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT); + dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + + /* Fast-mode */ + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 6, /* tHD;STA = tHIGH = 0.6 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 13, /* tLOW = 1.3 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT); + writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT); + dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + + /* Configure Tx/Rx FIFO threshold levels */ + writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL); + writel(0, dev->base + DW_IC_RX_TL); + + /* configure the i2c master */ + ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | + DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; + writel(ic_con, dev->base + DW_IC_CON); +} + +/* + * Waiting for bus not busy + */ +static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) +{ + int timeout = TIMEOUT; + + while (readl(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { + if (timeout <= 0) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + timeout--; + mdelay(1); + } + + return 0; +} + +static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 ic_con; + + /* Disable the adapter */ + writel(0, dev->base + DW_IC_ENABLE); + + /* set the slave (target) address */ + writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR); + + /* if the slave address is ten bit address, enable 10BITADDR */ + ic_con = readl(dev->base + DW_IC_CON); + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) + ic_con |= DW_IC_CON_10BITADDR_MASTER; + else + ic_con &= ~DW_IC_CON_10BITADDR_MASTER; + writel(ic_con, dev->base + DW_IC_CON); + + /* Enable the adapter */ + writel(1, dev->base + DW_IC_ENABLE); + + /* Enable interrupts */ + writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK); +} + +/* + * Initiate (and continue) low level master read/write transaction. + * This function is only called from i2c_dw_isr, and pumping i2c_msg + * messages into the tx buffer. Even if the size of i2c_msg data is + * longer than the size of the tx buffer, it handles everything. + */ +static void +i2c_dw_xfer_msg(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 intr_mask; + int tx_limit, rx_limit; + u32 addr = msgs[dev->msg_write_idx].addr; + u32 buf_len = dev->tx_buf_len; + u8 *buf = dev->tx_buf;; + + intr_mask = DW_IC_INTR_DEFAULT_MASK; + + for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { + /* + * if target address has changed, we need to + * reprogram the target address in the i2c + * adapter when we are done with this transfer + */ + if (msgs[dev->msg_write_idx].addr != addr) { + dev_err(dev->dev, + "%s: invalid target address\n", __func__); + dev->msg_err = -EINVAL; + break; + } + + if (msgs[dev->msg_write_idx].len == 0) { + dev_err(dev->dev, + "%s: invalid message length\n", __func__); + dev->msg_err = -EINVAL; + break; + } + + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* new i2c_msg */ + buf = msgs[dev->msg_write_idx].buf; + buf_len = msgs[dev->msg_write_idx].len; + } + + tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR); + rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR); + + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { + writel(0x100, dev->base + DW_IC_DATA_CMD); + rx_limit--; + } else + writel(*buf++, dev->base + DW_IC_DATA_CMD); + tx_limit--; buf_len--; + } + + dev->tx_buf = buf; + dev->tx_buf_len = buf_len; + + if (buf_len > 0) { + /* more bytes to be written */ + dev->status |= STATUS_WRITE_IN_PROGRESS; + break; + } else + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + } + + /* + * If i2c_msg index search is completed, we don't need TX_EMPTY + * interrupt any more. + */ + if (dev->msg_write_idx == dev->msgs_num) + intr_mask &= ~DW_IC_INTR_TX_EMPTY; + + if (dev->msg_err) + intr_mask = 0; + + writel(intr_mask, dev->base + DW_IC_INTR_MASK); +} + +static void +i2c_dw_read(struct dw_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + int rx_valid; + + for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) { + u32 len; + u8 *buf; + + if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD)) + continue; + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + len = msgs[dev->msg_read_idx].len; + buf = msgs[dev->msg_read_idx].buf; + } else { + len = dev->rx_buf_len; + buf = dev->rx_buf; + } + + rx_valid = readl(dev->base + DW_IC_RXFLR); + + for (; len > 0 && rx_valid > 0; len--, rx_valid--) + *buf++ = readl(dev->base + DW_IC_DATA_CMD); + + if (len > 0) { + dev->status |= STATUS_READ_IN_PROGRESS; + dev->rx_buf_len = len; + dev->rx_buf = buf; + return; + } else + dev->status &= ~STATUS_READ_IN_PROGRESS; + } +} + +static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) +{ + unsigned long abort_source = dev->abort_source; + int i; + + if (abort_source & DW_IC_TX_ABRT_NOACK) { + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_dbg(dev->dev, + "%s: %s\n", __func__, abort_sources[i]); + return -EREMOTEIO; + } + + for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); + + if (abort_source & DW_IC_TX_ARB_LOST) + return -EAGAIN; + else if (abort_source & DW_IC_TX_ABRT_GCALL_READ) + return -EINVAL; /* wrong msgs[] data */ + else + return -EIO; +} + +/* + * Prepare controller for a transaction and call i2c_dw_xfer_msg + */ +static int +i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + int ret; + + dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + + mutex_lock(&dev->lock); + + INIT_COMPLETION(dev->cmd_complete); + dev->msgs = msgs; + dev->msgs_num = num; + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + + ret = i2c_dw_wait_bus_not_busy(dev); + if (ret < 0) + goto done; + + /* start the transfers */ + i2c_dw_xfer_init(dev); + + /* wait for tx to complete */ + ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ); + if (ret == 0) { + dev_err(dev->dev, "controller timed out\n"); + i2c_dw_init(dev); + ret = -ETIMEDOUT; + goto done; + } else if (ret < 0) + goto done; + + if (dev->msg_err) { + ret = dev->msg_err; + goto done; + } + + /* no error */ + if (likely(!dev->cmd_err)) { + /* Disable the adapter */ + writel(0, dev->base + DW_IC_ENABLE); + ret = num; + goto done; + } + + /* We have an error */ + if (dev->cmd_err == DW_IC_ERR_TX_ABRT) { + ret = i2c_dw_handle_tx_abort(dev); + goto done; + } + ret = -EIO; + +done: + mutex_unlock(&dev->lock); + + return ret; +} + +static u32 i2c_dw_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | + I2C_FUNC_10BIT_ADDR | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) +{ + u32 stat; + + /* + * The IC_INTR_STAT register just indicates "enabled" interrupts. + * Ths unmasked raw version of interrupt status bits are available + * in the IC_RAW_INTR_STAT register. + * + * That is, + * stat = readl(IC_INTR_STAT); + * equals to, + * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK); + * + * The raw version might be useful for debugging purposes. + */ + stat = readl(dev->base + DW_IC_INTR_STAT); + + /* + * Do not use the IC_CLR_INTR register to clear interrupts, or + * you'll miss some interrupts, triggered during the period from + * readl(IC_INTR_STAT) to readl(IC_CLR_INTR). + * + * Instead, use the separately-prepared IC_CLR_* registers. + */ + if (stat & DW_IC_INTR_RX_UNDER) + readl(dev->base + DW_IC_CLR_RX_UNDER); + if (stat & DW_IC_INTR_RX_OVER) + readl(dev->base + DW_IC_CLR_RX_OVER); + if (stat & DW_IC_INTR_TX_OVER) + readl(dev->base + DW_IC_CLR_TX_OVER); + if (stat & DW_IC_INTR_RD_REQ) + readl(dev->base + DW_IC_CLR_RD_REQ); + if (stat & DW_IC_INTR_TX_ABRT) { + /* + * The IC_TX_ABRT_SOURCE register is cleared whenever + * the IC_CLR_TX_ABRT is read. Preserve it beforehand. + */ + dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE); + readl(dev->base + DW_IC_CLR_TX_ABRT); + } + if (stat & DW_IC_INTR_RX_DONE) + readl(dev->base + DW_IC_CLR_RX_DONE); + if (stat & DW_IC_INTR_ACTIVITY) + readl(dev->base + DW_IC_CLR_ACTIVITY); + if (stat & DW_IC_INTR_STOP_DET) + readl(dev->base + DW_IC_CLR_STOP_DET); + if (stat & DW_IC_INTR_START_DET) + readl(dev->base + DW_IC_CLR_START_DET); + if (stat & DW_IC_INTR_GEN_CALL) + readl(dev->base + DW_IC_CLR_GEN_CALL); + + return stat; +} + +/* + * Interrupt service routine. This gets called whenever an I2C interrupt + * occurs. + */ +static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) +{ + struct dw_i2c_dev *dev = dev_id; + u32 stat; + + stat = i2c_dw_read_clear_intrbits(dev); + dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat); + + if (stat & DW_IC_INTR_TX_ABRT) { + dev->cmd_err |= DW_IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + + /* + * Anytime TX_ABRT is set, the contents of the tx/rx + * buffers are flushed. Make sure to skip them. + */ + writel(0, dev->base + DW_IC_INTR_MASK); + goto tx_aborted; + } + + if (stat & DW_IC_INTR_RX_FULL) + i2c_dw_read(dev); + + if (stat & DW_IC_INTR_TX_EMPTY) + i2c_dw_xfer_msg(dev); + + /* + * No need to modify or disable the interrupt mask here. + * i2c_dw_xfer_msg() will take care of it according to + * the current transmit status. + */ + +tx_aborted: + if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err) + complete(&dev->cmd_complete); + + return IRQ_HANDLED; +} + +static struct i2c_algorithm i2c_dw_algo = { + .master_xfer = i2c_dw_xfer, + .functionality = i2c_dw_func, +}; + +static int __devinit dw_i2c_probe(struct platform_device *pdev) +{ + struct dw_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *mem, *ioarea; + int irq, r; + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return irq; /* -ENXIO */ + } + + ioarea = request_mem_region(mem->start, resource_size(mem), + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto err_release_region; + } + + init_completion(&dev->cmd_complete); + mutex_init(&dev->lock); + dev->dev = get_device(&pdev->dev); + dev->irq = irq; + platform_set_drvdata(pdev, dev); + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) { + r = -ENODEV; + goto err_free_mem; + } + clk_enable(dev->clk); + + dev->base = ioremap(mem->start, resource_size(mem)); + if (dev->base == NULL) { + dev_err(&pdev->dev, "failure mapping io resources\n"); + r = -EBUSY; + goto err_unuse_clocks; + } + { + u32 param1 = readl(dev->base + DW_IC_COMP_PARAM_1); + + dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1; + dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1; + } + i2c_dw_init(dev); + + writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */ + r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev); + if (r) { + dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); + goto err_iounmap; + } + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "Synopsys DesignWare I2C adapter", + sizeof(adap->name)); + adap->algo = &i2c_dw_algo; + adap->dev.parent = &pdev->dev; + + adap->nr = pdev->id; + r = i2c_add_numbered_adapter(adap); + if (r) { + dev_err(&pdev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(dev->irq, dev); +err_iounmap: + iounmap(dev->base); +err_unuse_clocks: + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; +err_free_mem: + platform_set_drvdata(pdev, NULL); + put_device(&pdev->dev); + kfree(dev); +err_release_region: + release_mem_region(mem->start, resource_size(mem)); + + return r; +} + +static int __devexit dw_i2c_remove(struct platform_device *pdev) +{ + struct dw_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&dev->adapter); + put_device(&pdev->dev); + + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; + + writel(0, dev->base + DW_IC_ENABLE); + free_irq(dev->irq, dev); + kfree(dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:i2c_designware"); + +static struct platform_driver dw_i2c_driver = { + .remove = __devexit_p(dw_i2c_remove), + .driver = { + .name = "i2c_designware", + .owner = THIS_MODULE, + }, +}; + +static int __init dw_i2c_init_driver(void) +{ + return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe); +} +module_init(dw_i2c_init_driver); + +static void __exit dw_i2c_exit_driver(void) +{ + platform_driver_unregister(&dw_i2c_driver); +} +module_exit(dw_i2c_exit_driver); + +MODULE_AUTHOR("Baruch Siach "); +MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c new file mode 100644 index 00000000000..5d8aed5ec21 --- /dev/null +++ b/drivers/i2c/busses/i2c-ixp2000.c @@ -0,0 +1,168 @@ +/* + * drivers/i2c/busses/i2c-ixp2000.c + * + * I2C adapter for IXP2000 systems using GPIOs for I2C bus + * + * Author: Deepak Saxena + * Based on IXDP2400 code by: Naeem M. Afzal + * Made generic by: Jeff Daly + * + * Copyright (c) 2003-2004 MontaVista Software Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * From Jeff Daly: + * + * I2C adapter driver for Intel IXDP2xxx platforms. This should work for any + * IXP2000 platform if it uses the HW GPIO in the same manner. Basically, + * SDA and SCL GPIOs have external pullups. Setting the respective GPIO to + * an input will make the signal a '1' via the pullup. Setting them to + * outputs will pull them down. + * + * The GPIOs are open drain signals and are used as configuration strap inputs + * during power-up so there's generally a buffer on the board that needs to be + * 'enabled' to drive the GPIOs. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include /* Pick up IXP2000-specific bits */ +#include + +static inline int ixp2000_scl_pin(void *data) +{ + return ((struct ixp2000_i2c_pins*)data)->scl_pin; +} + +static inline int ixp2000_sda_pin(void *data) +{ + return ((struct ixp2000_i2c_pins*)data)->sda_pin; +} + + +static void ixp2000_bit_setscl(void *data, int val) +{ + int i = 5000; + + if (val) { + gpio_line_config(ixp2000_scl_pin(data), GPIO_IN); + while(!gpio_line_get(ixp2000_scl_pin(data)) && i--); + } else { + gpio_line_config(ixp2000_scl_pin(data), GPIO_OUT); + } +} + +static void ixp2000_bit_setsda(void *data, int val) +{ + if (val) { + gpio_line_config(ixp2000_sda_pin(data), GPIO_IN); + } else { + gpio_line_config(ixp2000_sda_pin(data), GPIO_OUT); + } +} + +static int ixp2000_bit_getscl(void *data) +{ + return gpio_line_get(ixp2000_scl_pin(data)); +} + +static int ixp2000_bit_getsda(void *data) +{ + return gpio_line_get(ixp2000_sda_pin(data)); +} + +struct ixp2000_i2c_data { + struct ixp2000_i2c_pins *gpio_pins; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo_data; +}; + +static int ixp2000_i2c_remove(struct platform_device *plat_dev) +{ + struct ixp2000_i2c_data *drv_data = platform_get_drvdata(plat_dev); + + platform_set_drvdata(plat_dev, NULL); + + i2c_del_adapter(&drv_data->adapter); + + kfree(drv_data); + + return 0; +} + +static int ixp2000_i2c_probe(struct platform_device *plat_dev) +{ + int err; + struct ixp2000_i2c_pins *gpio = plat_dev->dev.platform_data; + struct ixp2000_i2c_data *drv_data = + kzalloc(sizeof(struct ixp2000_i2c_data), GFP_KERNEL); + + if (!drv_data) + return -ENOMEM; + drv_data->gpio_pins = gpio; + + drv_data->algo_data.data = gpio; + drv_data->algo_data.setsda = ixp2000_bit_setsda; + drv_data->algo_data.setscl = ixp2000_bit_setscl; + drv_data->algo_data.getsda = ixp2000_bit_getsda; + drv_data->algo_data.getscl = ixp2000_bit_getscl; + drv_data->algo_data.udelay = 6; + drv_data->algo_data.timeout = HZ; + + strlcpy(drv_data->adapter.name, plat_dev->dev.driver->name, + sizeof(drv_data->adapter.name)); + drv_data->adapter.algo_data = &drv_data->algo_data, + + drv_data->adapter.dev.parent = &plat_dev->dev; + + gpio_line_config(gpio->sda_pin, GPIO_IN); + gpio_line_config(gpio->scl_pin, GPIO_IN); + gpio_line_set(gpio->scl_pin, 0); + gpio_line_set(gpio->sda_pin, 0); + + if ((err = i2c_bit_add_bus(&drv_data->adapter)) != 0) { + dev_err(&plat_dev->dev, "Could not install, error %d\n", err); + kfree(drv_data); + return err; + } + + platform_set_drvdata(plat_dev, drv_data); + + return 0; +} + +static struct platform_driver ixp2000_i2c_driver = { + .probe = ixp2000_i2c_probe, + .remove = ixp2000_i2c_remove, + .driver = { + .name = "IXP2000-I2C", + .owner = THIS_MODULE, + }, +}; + +static int __init ixp2000_i2c_init(void) +{ + return platform_driver_register(&ixp2000_i2c_driver); +} + +static void __exit ixp2000_i2c_exit(void) +{ + platform_driver_unregister(&ixp2000_i2c_driver); +} + +module_init(ixp2000_i2c_init); +module_exit(ixp2000_i2c_exit); + +MODULE_AUTHOR ("Deepak Saxena "); +MODULE_DESCRIPTION("IXP2000 GPIO-based I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:IXP2000-I2C"); + diff --git a/drivers/i2c/busses/i2c-slave-tegra.c b/drivers/i2c/busses/i2c-slave-tegra.c new file mode 100644 index 00000000000..f2468d85c1f --- /dev/null +++ b/drivers/i2c/busses/i2c-slave-tegra.c @@ -0,0 +1,1114 @@ +/* + * drivers/i2c/busses/i2c-slave-tegra.c + * I2c slave driver for the Nvidia's tegra SOC. + * + * Copyright (c) 2009-2011, NVIDIA Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/*#define DEBUG 1*/ +/*#define VERBOSE_DEBUG 1*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define BYTES_PER_FIFO_WORD 4 +#define to_jiffies(msecs) msecs_to_jiffies(msecs) + +#define I2C_CNFG 0x000 +#define I2C_CNFG_PACKET_MODE_EN (1<<10) +#define I2C_CNFG_NEW_MASTER_FSM (1<<11) +#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 +#define I2C_CNFG_DEBOUNCE_CNT_MASK (0x7) + +#define I2C_STATUS 0x01C + +#define I2C_SLV_CNFG 0x020 +#define I2C_SLV_CNFG_NEWSL (1<<2) +#define I2C_SLV_CNFG_ENABLE_SL (1<<3) +#define I2C_SLV_CNFG_PKT_MODE_EN (1<<4) +#define I2C_SLV_CNFG_FIFO_XFER_EN (1<<20) +#define I2C_SLV_CNFG_ACK_LAST_BYTE (1<<6) +#define I2C_SLV_CNFG_ACK_LAST_BYTE_VALID (1<<7) + +#define I2C_SLV_ADDR1 0x02c +#define I2C_SLV_ADDR1_ADDR_SHIFT 0x0 + +#define I2C_SLV_ADDR2 0x030 +#define I2C_SLV_ADDR2_ADDR0_HI_SHIFT 0x1 +#define I2C_SLV_ADDR2_ADDR0_MASK 0x7 +#define I2C_SLV_ADDR2_ADDR0_TEN_BIT_ADDR_MODE 0x1 + +#define I2C_SLV_INT_MASK 0x040 + +#define I2C_TX_FIFO 0x050 +#define I2C_RX_FIFO 0x054 +#define I2C_PACKET_TRANSFER_STATUS 0x058 + +#define I2C_FIFO_CONTROL 0x05c +#define I2C_FIFO_CONTROL_SLV_TX_FLUSH (1<<9) +#define I2C_FIFO_CONTROL_SLV_RX_FLUSH (1<<8) +#define I2C_FIFO_CONTROL_SLV_TX_TRIG_SHIFT 13 +#define I2C_FIFO_CONTROL_SLV_TX_TRIG_MASK (0x7 << 13) +#define I2C_FIFO_CONTROL_SLV_RX_TRIG_SHIFT 10 +#define I2C_FIFO_CONTROL_SLV_RX_TRIG_MASK (1 << 10) + +#define I2C_FIFO_STATUS 0x060 +#define I2C_FIFO_STATUS_SLV_TX_MASK (0xF << 20) +#define I2C_FIFO_STATUS_SLV_TX_SHIFT 20 +#define I2C_FIFO_STATUS_SLV_RX_MASK (0x0F << 16) +#define I2C_FIFO_STATUS_SLV_RX_SHIFT 16 + +#define I2C_INT_MASK 0x064 +#define I2C_INT_STATUS 0x068 +#define I2C_INT_PACKET_XFER_COMPLETE (1<<7) +#define I2C_INT_ALL_PACKETS_XFER_COMPLETE (1<<6) +#define I2C_INT_TX_FIFO_OVERFLOW (1<<5) +#define I2C_INT_RX_FIFO_UNDERFLOW (1<<4) +#define I2C_INT_NO_ACK (1<<3) +#define I2C_INT_ARBITRATION_LOST (1<<2) +#define I2C_INT_TX_FIFO_DATA_REQ (1<<1) +#define I2C_INT_RX_FIFO_DATA_REQ (1<<0) + +#define I2C_INT_SLV_PKT_XFER_ERR (1 << 25) +#define I2C_INT_SLV_TX_BUFFER_REQ (1 << 24) +#define I2C_INT_SLV_RX_BUFFER_FILLED (1 << 23) +#define I2C_INT_SLV_PACKET_XFER_COMPLETE (1 << 22) +#define I2C_INT_SLV_TFIFO_OVF_REQ (1 << 21) +#define I2C_INT_SLV_RFIFO_UNF_REQ (1 << 20) +#define I2C_INT_SLV_TFIFO_DATA_REQ (1 << 17) +#define I2C_INT_SLV_RFIFO_DATA_REQ (1 << 16) + +#define I2C_SLV_TX_FIFO 0x078 +#define I2C_SLV_RX_FIFO 0x07c + +#define I2C_SLV_PACKET_STATUS 0x80 +#define I2C_SLV_PACKET_STATUS_BYTENUM_SHIFT 4 +#define I2C_SLV_PACKET_STATUS_BYTENUM_MASK 0xFFF0 + +#define I2C_CLK_DIVISOR 0x06c + +#define DVC_CTRL_REG1 0x000 +#define DVC_CTRL_REG1_INTR_EN (1<<10) +#define DVC_CTRL_REG2 0x004 +#define DVC_CTRL_REG3 0x008 +#define DVC_CTRL_REG3_SW_PROG (1<<26) +#define DVC_CTRL_REG3_I2C_DONE_INTR_EN (1<<30) +#define DVC_STATUS 0x00c +#define DVC_STATUS_I2C_DONE_INTR (1<<30) + +#define I2C_ERR_NONE 0x00 +#define I2C_ERR_NO_ACK 0x01 +#define I2C_ERR_ARBITRATION_LOST 0x02 +#define I2C_ERR_UNKNOWN_INTERRUPT 0x04 + +#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 +#define PACKET_HEADER0_PACKET_ID_SHIFT 16 +#define PACKET_HEADER0_CONT_ID_SHIFT 12 +#define PACKET_HEADER0_PROTOCOL_I2C (1<<4) + +#define I2C_HEADER_HIGHSPEED_MODE (1<<22) +#define I2C_HEADER_CONT_ON_NAK (1<<21) +#define I2C_HEADER_SEND_START_BYTE (1<<20) +#define I2C_HEADER_READ (1<<19) +#define I2C_HEADER_10BIT_ADDR (1<<18) +#define I2C_HEADER_IE_ENABLE (1<<17) +#define I2C_HEADER_REPEAT_START (1<<16) +#define I2C_HEADER_MASTER_ADDR_SHIFT 12 +#define I2C_HEADER_SLAVE_ADDR_SHIFT 1 + +#define I2C_FIFO_DEPTH 8 +/* Transfer state of the i2c slave */ +#define TRANSFER_STATE_NONE 0 +#define TRANSFER_STATE_READ 1 +#define TRANSFER_STATE_WRITE 2 + +#define I2C_SLV_TRANS_PREMATURE_END I2C_INT_SLV_PKT_XFER_ERR + +#define I2C_SLV_TRANS_ALL_XFER_END I2C_INT_SLV_PACKET_XFER_COMPLETE + +#define I2C_SLV_TRANS_END \ + (I2C_INT_SLV_PKT_XFER_ERR | I2C_INT_SLV_PACKET_XFER_COMPLETE) + +#define I2C_INT_STATUS_RX_BUFFER_FILLED I2C_INT_SLV_RX_BUFFER_FILLED + +#define I2C_INT_STATUS_RX_DATA_AVAILABLE \ + (I2C_INT_SLV_RX_BUFFER_FILLED | I2C_INT_SLV_RFIFO_DATA_REQ) + +#define I2C_INT_STATUS_TX_BUFFER_REQUEST \ + (I2C_INT_SLV_TX_BUFFER_REQ | I2C_INT_SLV_TFIFO_DATA_REQ) + +#define I2C_SLV_ERRORS_INT_MASK (I2C_INT_SLV_TFIFO_OVF_REQ | \ + I2C_INT_SLV_RFIFO_UNF_REQ | I2C_INT_SLV_PKT_XFER_ERR) + +#define I2C_SLV_DEFAULT_INT_MASK (I2C_INT_SLV_TFIFO_OVF_REQ | \ + I2C_INT_SLV_RFIFO_UNF_REQ | I2C_INT_SLV_PKT_XFER_ERR | \ + I2C_INT_SLV_RX_BUFFER_FILLED | I2C_INT_SLV_TX_BUFFER_REQ) + +struct tegra_i2c_slave_dev; + +struct tegra_i2c_slave_bus { + struct tegra_i2c_slave_dev *dev; + const struct tegra_pingroup_config *pinmux; + int mux_len; + unsigned long bus_clk_rate; + struct i2c_slave_adapter slv_adap; +}; + +struct tegra_i2c_slave_dev { + struct device *dev; + struct clk *clk; + struct resource *iomem; + void __iomem *base; + int cont_id; + int irq; + spinlock_t lock; + struct completion rx_msg_complete; + struct completion tx_msg_complete; + bool is_rx_waiting; + bool is_tx_waiting; + u8 *rx_msg_buff; + int rx_msg_buf_size; + int rx_msg_head; + int rx_msg_tail; + u8 *tx_msg_buff; + int tx_msg_buf_size; + int tx_msg_head; + int tx_msg_tail; + bool is_slave_started; + int slave_add; + bool is_ten_bit_addr; + u32 dummy_word; + unsigned long rx_pack_hdr1; + unsigned long rx_pack_hdr2; + unsigned long rx_pack_hdr3; + int curr_transfer; + unsigned long int_mask; + int nack_packet_count; + bool is_first_byte_read_wait; + int curr_packet_bytes_read; + unsigned int cont_status; + bool is_dummy_char_cycle; + unsigned long curr_packet_tx_tail; + const struct tegra_pingroup_config *pin_mux; + int bus_clk; + struct tegra_i2c_slave_bus bus; +}; + +#define get_space_count(rInd, wInd, maxsize) \ + (((wInd > rInd) ? (maxsize - wInd + rInd) : (rInd - wInd)) - 1) + +#define get_data_count(rInd, wInd, maxsize) \ + ((wInd >= rInd) ? (wInd - rInd) : (maxsize - rInd + wInd - 1)) + +static void set_tx_trigger_level(struct tegra_i2c_slave_dev *i2c_dev, int trig) +{ + unsigned long fifo_control = readl(i2c_dev->base + I2C_FIFO_CONTROL); + if (trig) { + fifo_control &= ~I2C_FIFO_CONTROL_SLV_TX_TRIG_MASK; + fifo_control |= (trig-1) << I2C_FIFO_CONTROL_SLV_TX_TRIG_SHIFT; + writel(fifo_control, i2c_dev->base + I2C_FIFO_CONTROL); + } +} + +static void set_rx_trigger_level(struct tegra_i2c_slave_dev *i2c_dev, int trig) +{ + unsigned long fifo_control = readl(i2c_dev->base + I2C_FIFO_CONTROL); + if (trig) { + fifo_control &= ~I2C_FIFO_CONTROL_SLV_RX_TRIG_MASK; + fifo_control |= (trig-1) << I2C_FIFO_CONTROL_SLV_RX_TRIG_SHIFT; + writel(fifo_control, i2c_dev->base + I2C_FIFO_CONTROL); + } +} + +static void reset_slave_tx_fifo(struct tegra_i2c_slave_dev *i2c_dev) +{ + unsigned long fifo_control = readl(i2c_dev->base + I2C_FIFO_CONTROL); + unsigned long timeout_count = 1000; + + writel(fifo_control | I2C_FIFO_CONTROL_SLV_TX_FLUSH, + i2c_dev->base + I2C_FIFO_CONTROL); + while (timeout_count--) { + fifo_control = readl(i2c_dev->base + I2C_FIFO_CONTROL); + if (!(fifo_control & I2C_FIFO_CONTROL_SLV_TX_FLUSH)) + break; + udelay(1); + } + if (!timeout_count) { + dev_err(i2c_dev->dev, "Not able to flush tx fifo\n"); + BUG(); + } +} + +static void do_tx_fifo_empty(struct tegra_i2c_slave_dev *i2c_dev, + unsigned long *empty_count) +{ + unsigned long fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS); + unsigned long tx_fifo_empty_count; + + tx_fifo_empty_count = (fifo_status & I2C_FIFO_STATUS_SLV_TX_MASK) >> + I2C_FIFO_STATUS_SLV_TX_SHIFT; + if (tx_fifo_empty_count < I2C_FIFO_DEPTH) + reset_slave_tx_fifo(i2c_dev); + if (empty_count) + *empty_count = tx_fifo_empty_count; +} + +static void get_packet_headers(struct tegra_i2c_slave_dev *i2c_dev, u32 msg_len, + u32 flags, unsigned long *packet_header1, + unsigned long *packet_header2, unsigned long *packet_header3) +{ + unsigned long packet_header; + *packet_header1 = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | + PACKET_HEADER0_PROTOCOL_I2C | + (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) | + (1 << PACKET_HEADER0_PACKET_ID_SHIFT); + *packet_header2 = msg_len-1; + if (i2c_dev->is_ten_bit_addr) + packet_header = i2c_dev->slave_add | I2C_HEADER_10BIT_ADDR; + else + packet_header = i2c_dev->slave_add << + I2C_HEADER_SLAVE_ADDR_SHIFT; + + if (flags & I2C_M_RD) + packet_header |= I2C_HEADER_READ; + + *packet_header3 = packet_header; +} + +static void configure_i2c_slave_packet_mode(struct tegra_i2c_slave_dev *i2c_dev) +{ + unsigned long i2c_config; + i2c_config = I2C_CNFG_PACKET_MODE_EN | I2C_CNFG_NEW_MASTER_FSM; + i2c_config |= (2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); + writel(i2c_config, i2c_dev->base + I2C_CNFG); +} + +static void configure_i2c_slave_address(struct tegra_i2c_slave_dev *i2c_dev) +{ + + unsigned long slave_add_reg; + unsigned long i2c_slv_config; + unsigned long slave_add; + + if (i2c_dev->is_ten_bit_addr) { + slave_add = i2c_dev->slave_add & 0xFF; + slave_add_reg = readl(i2c_dev->base + I2C_SLV_ADDR1); + slave_add_reg &= ~(0xFF); + slave_add_reg |= slave_add << I2C_SLV_ADDR1_ADDR_SHIFT; + writel(slave_add_reg, i2c_dev->base + I2C_SLV_ADDR1); + + slave_add = (i2c_dev->slave_add >> 8) & 0x3; + slave_add_reg = readl(i2c_dev->base + I2C_SLV_ADDR2); + slave_add_reg &= ~I2C_SLV_ADDR2_ADDR0_MASK; + slave_add_reg |= slave_add | + I2C_SLV_ADDR2_ADDR0_TEN_BIT_ADDR_MODE; + writel(slave_add_reg, i2c_dev->base + I2C_SLV_ADDR2); + } else { + slave_add = (i2c_dev->slave_add & 0x3FF); + slave_add_reg = readl(i2c_dev->base + I2C_SLV_ADDR1); + slave_add_reg &= ~(0x3FF); + slave_add_reg |= slave_add << I2C_SLV_ADDR1_ADDR_SHIFT; + writel(slave_add_reg, i2c_dev->base + I2C_SLV_ADDR1); + + slave_add_reg = readl(i2c_dev->base + I2C_SLV_ADDR2); + slave_add_reg &= ~I2C_SLV_ADDR2_ADDR0_MASK; + writel(slave_add_reg, i2c_dev->base + I2C_SLV_ADDR2); + } + + i2c_slv_config = I2C_SLV_CNFG_NEWSL; + if (i2c_dev->slave_add) { + i2c_slv_config = I2C_SLV_CNFG_ENABLE_SL | + I2C_SLV_CNFG_PKT_MODE_EN | + I2C_SLV_CNFG_FIFO_XFER_EN; + } + writel(i2c_slv_config, i2c_dev->base + I2C_SLV_CNFG); +} + +static void copy_rx_data(struct tegra_i2c_slave_dev *i2c_dev, u8 rcv_char) +{ + if (get_space_count(i2c_dev->rx_msg_tail, i2c_dev->rx_msg_head, + i2c_dev->rx_msg_buf_size)){ + i2c_dev->rx_msg_buff[i2c_dev->rx_msg_head++] = rcv_char; + if (i2c_dev->rx_msg_head == i2c_dev->rx_msg_buf_size) + i2c_dev->rx_msg_head = 0; + } else { + dev_warn(i2c_dev->dev, "The slave rx buffer is full, ignoring " + "new receive data\n"); + } +} + +static void handle_packet_first_byte_read(struct tegra_i2c_slave_dev *i2c_dev) +{ + unsigned long fifo_status; + int filled_slots; + unsigned long i2c_sl_config; + unsigned long recv_data; + + fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS); + filled_slots = (fifo_status & I2C_FIFO_STATUS_SLV_RX_MASK) >> + I2C_FIFO_STATUS_SLV_RX_SHIFT; + + writel(I2C_INT_STATUS_RX_DATA_AVAILABLE, + i2c_dev->base + I2C_INT_STATUS); + if (unlikely(filled_slots != 1)) { + dev_err(i2c_dev->dev, "Unexpected number of filed slots %d", + filled_slots); + BUG(); + } + recv_data = readl(i2c_dev->base + I2C_SLV_RX_FIFO); + copy_rx_data(i2c_dev, (u8)recv_data); + + i2c_dev->is_first_byte_read_wait = false; + i2c_dev->curr_transfer = TRANSFER_STATE_READ; + i2c_dev->curr_packet_bytes_read = 0; + + /* Write packet Header */ + writel(i2c_dev->rx_pack_hdr1, i2c_dev->base + I2C_SLV_TX_FIFO); + writel(i2c_dev->rx_pack_hdr2, i2c_dev->base + I2C_SLV_TX_FIFO); + writel(i2c_dev->rx_pack_hdr3, i2c_dev->base + I2C_SLV_TX_FIFO); + + set_rx_trigger_level(i2c_dev, 4); + i2c_dev->int_mask |= I2C_INT_SLV_RFIFO_DATA_REQ; + writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK); + + /* Ack the master */ + i2c_sl_config = readl(i2c_dev->base + I2C_SLV_CNFG); + i2c_sl_config |= I2C_SLV_CNFG_ACK_LAST_BYTE | + I2C_SLV_CNFG_ACK_LAST_BYTE_VALID; + writel(i2c_sl_config, i2c_dev->base + I2C_SLV_CNFG); +} + +static void handle_packet_byte_read(struct tegra_i2c_slave_dev *i2c_dev) +{ + unsigned long fifo_status; + int i, j; + int filled_slots; + unsigned long recv_data; + int curr_xfer_size; + + fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS); + filled_slots = (fifo_status & I2C_FIFO_STATUS_SLV_RX_MASK) >> + I2C_FIFO_STATUS_SLV_RX_SHIFT; + + curr_xfer_size = BYTES_PER_FIFO_WORD * filled_slots; + if (i2c_dev->cont_status & I2C_SLV_TRANS_PREMATURE_END) { + curr_xfer_size = readl(i2c_dev->base + I2C_SLV_PACKET_STATUS); + curr_xfer_size = + (curr_xfer_size & I2C_SLV_PACKET_STATUS_BYTENUM_MASK) >> + I2C_SLV_PACKET_STATUS_BYTENUM_SHIFT; + + BUG_ON(filled_slots != ((curr_xfer_size - + i2c_dev->curr_packet_bytes_read + 3) >> 2)); + curr_xfer_size -= i2c_dev->curr_packet_bytes_read; + } + + i2c_dev->curr_packet_bytes_read += curr_xfer_size; + for (i = 0; i < filled_slots; ++i) { + recv_data = readl(i2c_dev->base + I2C_SLV_RX_FIFO); + for (j = 0; j < BYTES_PER_FIFO_WORD; ++j) { + copy_rx_data(i2c_dev, (u8)(recv_data >> j*8)); + curr_xfer_size--; + if (!curr_xfer_size) + break; + } + } + if (i2c_dev->cont_status & I2C_SLV_TRANS_PREMATURE_END) { + writel(I2C_SLV_TRANS_END | I2C_INT_STATUS_RX_BUFFER_FILLED, + i2c_dev->base + I2C_INT_STATUS); + + i2c_dev->is_first_byte_read_wait = true; + i2c_dev->curr_transfer = TRANSFER_STATE_NONE; + i2c_dev->curr_packet_bytes_read = 0; + set_rx_trigger_level(i2c_dev, 1); + writel(0, i2c_dev->base + I2C_SLV_INT_MASK); + i2c_dev->int_mask = I2C_SLV_DEFAULT_INT_MASK; + writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK); + } +} + +static void handle_rx_interrupt(struct tegra_i2c_slave_dev *i2c_dev) +{ + if (i2c_dev->is_first_byte_read_wait) + handle_packet_first_byte_read(i2c_dev); + else + handle_packet_byte_read(i2c_dev); + + if (i2c_dev->is_rx_waiting) { + complete(&i2c_dev->rx_msg_complete); + i2c_dev->is_rx_waiting = false; + } +} + +static void handle_tx_transaction_end(struct tegra_i2c_slave_dev *i2c_dev) +{ + unsigned long curr_packet_size; + + i2c_dev->curr_transfer = TRANSFER_STATE_NONE; + curr_packet_size = readl(i2c_dev->base + I2C_SLV_PACKET_STATUS); + curr_packet_size = + (curr_packet_size & I2C_SLV_PACKET_STATUS_BYTENUM_MASK) >> + I2C_SLV_PACKET_STATUS_BYTENUM_SHIFT; + + /* Get transfer count from request size.*/ + if ((curr_packet_size == 0) && + (i2c_dev->cont_status & I2C_SLV_TRANS_ALL_XFER_END) && + (!(i2c_dev->cont_status & I2C_SLV_TRANS_PREMATURE_END))) { + if (!i2c_dev->is_dummy_char_cycle) + i2c_dev->tx_msg_tail = i2c_dev->curr_packet_tx_tail; + } else { + if (!i2c_dev->is_dummy_char_cycle) { + i2c_dev->tx_msg_tail += curr_packet_size; + if (i2c_dev->tx_msg_tail >= i2c_dev->tx_msg_buf_size) + i2c_dev->tx_msg_tail -= + i2c_dev->tx_msg_buf_size; + } + } + writel(I2C_SLV_TRANS_END, i2c_dev->base + I2C_INT_STATUS); + + i2c_dev->curr_transfer = TRANSFER_STATE_NONE; + set_tx_trigger_level(i2c_dev, 1); + writel(0, i2c_dev->base + I2C_SLV_INT_MASK); + i2c_dev->int_mask = I2C_SLV_DEFAULT_INT_MASK; + writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK); + if (i2c_dev->is_tx_waiting) { + complete(&i2c_dev->tx_msg_complete); + i2c_dev->is_tx_waiting = false; + } +} + +static void handle_tx_trigger_int(struct tegra_i2c_slave_dev *i2c_dev) +{ + unsigned long fifo_status; + int empty_slots; + int i, j; + int data_available; + unsigned long header1, header2, header3; + unsigned long tx_data; + int word_to_write; + int bytes_remain; + int bytes_in_curr_word; + int tx_tail; + int packet_len; + + fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS); + empty_slots = (fifo_status & I2C_FIFO_STATUS_SLV_TX_MASK) >> + I2C_FIFO_STATUS_SLV_TX_SHIFT; + BUG_ON(empty_slots <= 3); + if (i2c_dev->curr_transfer == TRANSFER_STATE_NONE) { + empty_slots -= 3; + + /* Clear the tfifo request. */ + writel(I2C_INT_STATUS_TX_BUFFER_REQUEST, + i2c_dev->base + I2C_INT_STATUS); + + /* Get Number of bytes it can transfer in current */ + data_available = get_data_count(i2c_dev->tx_msg_tail, + i2c_dev->tx_msg_head, i2c_dev->tx_msg_buf_size); + if (data_available) + packet_len = min(empty_slots*BYTES_PER_FIFO_WORD, + data_available); + else + packet_len = empty_slots*BYTES_PER_FIFO_WORD; + + get_packet_headers(i2c_dev, packet_len, I2C_M_RD, + &header1, &header2, &header3); + + /* Write packet Header */ + writel(header1, i2c_dev->base + I2C_SLV_TX_FIFO); + writel(header2, i2c_dev->base + I2C_SLV_TX_FIFO); + writel(header3, i2c_dev->base + I2C_SLV_TX_FIFO); + + fifo_status = readl(i2c_dev->base + I2C_FIFO_STATUS); + if (data_available) { + word_to_write = (packet_len + 3) >> 2; + bytes_remain = packet_len; + tx_tail = i2c_dev->tx_msg_tail; + for (i = 0; i < word_to_write; i++) { + bytes_in_curr_word = + min(bytes_remain, BYTES_PER_FIFO_WORD); + tx_data = 0; + for (j = 0; j < bytes_in_curr_word; ++j) { + tx_data |= (i2c_dev->tx_msg_buff[ + tx_tail++]<<(j*8)); + if (tx_tail >= i2c_dev->tx_msg_buf_size) + tx_tail = 0; + } + writel(tx_data, i2c_dev->base + + I2C_SLV_TX_FIFO); + bytes_remain -= bytes_in_curr_word; + } + i2c_dev->curr_packet_tx_tail = tx_tail; + i2c_dev->is_dummy_char_cycle = false; + } else { + i2c_dev->curr_packet_tx_tail = i2c_dev->tx_msg_tail; + for (i = 0; i < empty_slots; i++) + writel(i2c_dev->dummy_word, + i2c_dev->base + I2C_SLV_TX_FIFO); + i2c_dev->is_dummy_char_cycle = true; + } + + i2c_dev->curr_transfer = TRANSFER_STATE_WRITE; + i2c_dev->int_mask &= ~I2C_INT_SLV_TFIFO_DATA_REQ; + i2c_dev->int_mask |= I2C_SLV_TRANS_END; + writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK); + } else { + dev_err(i2c_dev->dev, "I2cSlaveIsr(): Illegal transfer at " + "this point\n"); + BUG(); + } +} + +static void handle_tx_interrupt(struct tegra_i2c_slave_dev *i2c_dev) +{ + if (i2c_dev->cont_status & I2C_SLV_TRANS_END) + handle_tx_transaction_end(i2c_dev); + else + handle_tx_trigger_int(i2c_dev); +} + +static irqreturn_t tegra_i2c_slave_isr(int irq, void *dev_id) +{ + struct tegra_i2c_slave_dev *i2c_dev = dev_id; + unsigned long flags; + + /* Read the Interrupt status register & PKT_STATUS */ + i2c_dev->cont_status = readl(i2c_dev->base + I2C_INT_STATUS); + + dev_dbg(i2c_dev->dev, "ISR ContStatus 0x%08x\n", i2c_dev->cont_status); + spin_lock_irqsave(&i2c_dev->lock, flags); + + if ((i2c_dev->cont_status & I2C_INT_STATUS_RX_DATA_AVAILABLE) || + (i2c_dev->curr_transfer == TRANSFER_STATE_READ)) { + handle_rx_interrupt(i2c_dev); + goto Done; + } + + if ((i2c_dev->cont_status & I2C_INT_STATUS_TX_BUFFER_REQUEST) || + (i2c_dev->curr_transfer == TRANSFER_STATE_WRITE)) { + handle_tx_interrupt(i2c_dev); + goto Done; + } + + dev_err(i2c_dev->dev, "Tegra I2c Slave got unwanted interrupt " + "IntStatus 0x%08x\n", i2c_dev->cont_status); + BUG(); + +Done: + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return IRQ_HANDLED; +} + +static int tegra_i2c_slave_start(struct i2c_slave_adapter *slv_adap, int addr, + int is_ten_bit_addr, unsigned char dummy_char) +{ + struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap); + struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev; + unsigned long flags; + + spin_lock_irqsave(&i2c_dev->lock, flags); + if (i2c_dev->is_slave_started) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return -EBUSY; + } + + i2c_dev->rx_msg_buff = (u8 *)(i2c_dev+1); + i2c_dev->rx_msg_head = 0; + i2c_dev->rx_msg_tail = 0; + i2c_dev->is_rx_waiting = false; + + i2c_dev->tx_msg_head = 0; + i2c_dev->tx_msg_tail = 0; + i2c_dev->is_tx_waiting = true; + + i2c_dev->dummy_word = (dummy_char << 8) | dummy_char; + i2c_dev->dummy_word |= i2c_dev->dummy_word << 16; + + i2c_dev->slave_add = addr; + i2c_dev->is_ten_bit_addr = is_ten_bit_addr; + + get_packet_headers(i2c_dev, 4096, 0, &i2c_dev->rx_pack_hdr1, + &i2c_dev->rx_pack_hdr2, &i2c_dev->rx_pack_hdr3); + + pm_runtime_get_sync(i2c_dev->dev); + configure_i2c_slave_packet_mode(i2c_dev); + configure_i2c_slave_address(i2c_dev); + do_tx_fifo_empty(i2c_dev, NULL); + set_rx_trigger_level(i2c_dev, 1); + writel(0, i2c_dev->base + I2C_SLV_INT_MASK); + + if (i2c_bus->pinmux) + tegra_pinmux_config_tristate_table(i2c_bus->pinmux, + i2c_bus->mux_len, TEGRA_TRI_NORMAL); + + i2c_dev->curr_transfer = 0; + i2c_dev->is_slave_started = true; + i2c_dev->int_mask = I2C_SLV_DEFAULT_INT_MASK; + i2c_dev->is_first_byte_read_wait = true; + writel(i2c_dev->int_mask, i2c_dev->base + I2C_INT_MASK); + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return 0; +} + +static void tegra_i2c_slave_stop(struct i2c_slave_adapter *slv_adap, + int is_buffer_clear) +{ + struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap); + struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev; + unsigned long flags; + + spin_lock_irqsave(&i2c_dev->lock, flags); + if (!i2c_dev->is_slave_started) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return; + } + + i2c_dev->slave_add = 0; + i2c_dev->is_ten_bit_addr = false; + configure_i2c_slave_address(i2c_dev); + writel(0, i2c_dev->base + I2C_SLV_INT_MASK); + writel(0, i2c_dev->base + I2C_INT_MASK); + i2c_dev->curr_transfer = 0; + i2c_dev->is_slave_started = false; + pm_runtime_put_sync(i2c_dev->dev); + if (is_buffer_clear) { + i2c_dev->rx_msg_head = 0; + i2c_dev->rx_msg_tail = 0; + i2c_dev->is_rx_waiting = false; + i2c_dev->tx_msg_head = 0; + i2c_dev->tx_msg_tail = 0; + i2c_dev->is_tx_waiting = false; + } + if (i2c_bus->pinmux) + tegra_pinmux_config_tristate_table(i2c_bus->pinmux, + i2c_bus->mux_len, TEGRA_TRI_TRISTATE); + spin_unlock_irqrestore(&i2c_dev->lock, flags); +} + +static int tegra_i2c_slave_send(struct i2c_slave_adapter *slv_adap, + const char *buf, int count) +{ + struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap); + struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev; + unsigned long flags; + unsigned long space_available; + int i; + + spin_lock_irqsave(&i2c_dev->lock, flags); + if (!i2c_dev->is_slave_started) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return -EPERM; + } + + space_available = get_space_count(i2c_dev->tx_msg_tail, + i2c_dev->tx_msg_head, i2c_dev->tx_msg_buf_size); + if (space_available < count) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return 0; + } + + for (i = 0; i < count; ++i) { + i2c_dev->tx_msg_buff[i2c_dev->tx_msg_head++] = *buf++; + if (i2c_dev->tx_msg_head >= i2c_dev->tx_msg_buf_size) + i2c_dev->tx_msg_head = 0; + } + i2c_dev->is_tx_waiting = false; + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return count; +} + +static int tegra_i2c_slave_get_tx_status(struct i2c_slave_adapter *slv_adap, + int timeout_ms) +{ + struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap); + struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev; + unsigned long flags; + unsigned long data_available; + + spin_lock_irqsave(&i2c_dev->lock, flags); + if (!i2c_dev->is_slave_started) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return -EPERM; + } + + data_available = get_data_count(i2c_dev->tx_msg_tail, + i2c_dev->tx_msg_head, i2c_dev->tx_msg_buf_size); + if (!data_available) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return 0; + } + + INIT_COMPLETION(i2c_dev->tx_msg_complete); + if (timeout_ms) + i2c_dev->is_tx_waiting = true; + spin_unlock_irqrestore(&i2c_dev->lock, flags); + if (timeout_ms) { + wait_for_completion_timeout(&i2c_dev->tx_msg_complete, + to_jiffies(timeout_ms)); + spin_lock_irqsave(&i2c_dev->lock, flags); + i2c_dev->is_tx_waiting = false; + data_available = get_data_count(i2c_dev->tx_msg_tail, + i2c_dev->tx_msg_head, + i2c_dev->tx_msg_buf_size); + spin_unlock_irqrestore(&i2c_dev->lock, flags); + if (data_available) + return -ETIMEDOUT; + } + return data_available; +} + +/* + * Timeoutms = 0, MinBytesRead = 0, read without waiting. + * Timeoutms = 0, MinBytesRead != 0, block till min bytes read. + * Timeoutms != 0, wait till timeout to read data.. + * Timeoutms = INF, wait till all req bytes read. + */ + +static int tegra_i2c_slave_recv(struct i2c_slave_adapter *slv_adap, char *buf, + int count, int min_count, int timeout_ms) +{ + struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap); + struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev; + unsigned long flags; + int data_available; + int bytes_copy; + int i; + int read_count = 0; + bool is_inf_wait = false; + int run_count = 0; + + spin_lock_irqsave(&i2c_dev->lock, flags); + if (!i2c_dev->is_slave_started) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return -EPERM; + } + + do { + data_available = get_data_count(i2c_dev->rx_msg_tail, + i2c_dev->rx_msg_head, i2c_dev->rx_msg_buf_size); + + bytes_copy = min(data_available, count); + + if (!data_available) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return 0; + } + for (i = 0; i < bytes_copy; ++i) { + *buf++ = i2c_dev->rx_msg_buff[i2c_dev->rx_msg_tail++]; + if (i2c_dev->rx_msg_tail >= i2c_dev->rx_msg_buf_size) + i2c_dev->rx_msg_tail = 0; + read_count++; + } + if (!timeout_ms) { + if ((!min_count) || (read_count >= min_count)) + break; + is_inf_wait = true; + } else { + if ((read_count == count) || run_count) + break; + } + i2c_dev->is_rx_waiting = true; + INIT_COMPLETION(i2c_dev->rx_msg_complete); + spin_unlock_irqrestore(&i2c_dev->lock, flags); + if (is_inf_wait) + wait_for_completion(&i2c_dev->rx_msg_complete); + else + wait_for_completion_timeout(&i2c_dev->rx_msg_complete, + to_jiffies(timeout_ms)); + + spin_lock_irqsave(&i2c_dev->lock, flags); + } while (1); + spin_unlock_irqrestore(&i2c_dev->lock, flags); + i2c_dev->is_rx_waiting = false; + return read_count; +} + +static int tegra_i2c_slave_flush_buffer(struct i2c_slave_adapter *slv_adap, + int is_flush_tx_buffer, int is_flush_rx_buffer) +{ + struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap); + struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev; + unsigned long flags; + + spin_lock_irqsave(&i2c_dev->lock, flags); + if (!i2c_dev->is_slave_started) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return -EPERM; + } + if (is_flush_tx_buffer) { + i2c_dev->tx_msg_head = 0; + i2c_dev->tx_msg_tail = 0; + } + if (is_flush_rx_buffer) { + i2c_dev->rx_msg_head = 0; + i2c_dev->rx_msg_tail = 0; + } + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return 0; +} + +static int tegra_i2c_slave_get_nack_cycle(struct i2c_slave_adapter *slv_adap, + int is_cout_reset) +{ + struct tegra_i2c_slave_bus *i2c_bus = i2c_get_slave_adapdata(slv_adap); + struct tegra_i2c_slave_dev *i2c_dev = i2c_bus->dev; + unsigned long flags; + int retval; + + spin_lock_irqsave(&i2c_dev->lock, flags); + if (!i2c_dev->is_slave_started) { + spin_unlock_irqrestore(&i2c_dev->lock, flags); + dev_dbg(i2c_dev->dev, "The slave bus is already started\n"); + return -EPERM; + } + + retval = i2c_dev->nack_packet_count; + if (is_cout_reset) + i2c_dev->nack_packet_count = 0; + + spin_unlock_irqrestore(&i2c_dev->lock, flags); + return retval; +} + +static const struct i2c_slave_algorithm tegra_i2c_slave_algo = { + .slave_start = tegra_i2c_slave_start, + .slave_stop = tegra_i2c_slave_stop, + .slave_send = tegra_i2c_slave_send, + .slave_get_tx_status = tegra_i2c_slave_get_tx_status, + .slave_recv = tegra_i2c_slave_recv, + .slave_flush_buffer = tegra_i2c_slave_flush_buffer, + .slave_get_nack_cycle = tegra_i2c_slave_get_nack_cycle, +}; + +static int tegra_i2c_slave_probe(struct platform_device *pdev) +{ + struct tegra_i2c_slave_dev *i2c_dev; + struct tegra_i2c_slave_bus *i2c_bus = NULL; + struct tegra_i2c_slave_platform_data *pdata = pdev->dev.platform_data; + struct resource *res; + struct resource *iomem; + struct clk *clk; + void *base; + int irq; + int ret = 0; + int rx_buffer_size; + int tx_buffer_size; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data?\n"); + return -ENODEV; + } + + if (pdata->adapter_nr < 0) { + dev_err(&pdev->dev, "invalid platform data?\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + iomem = request_mem_region(res->start, resource_size(res), pdev->name); + if (!iomem) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + base = ioremap(iomem->start, resource_size(iomem)); + if (!base) { + dev_err(&pdev->dev, "Can't ioremap I2C region\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "no irq resource?\n"); + ret = -ENODEV; + goto err_iounmap; + } + irq = res->start; + + clk = clk_get(&pdev->dev, NULL); + if (!clk) { + ret = -ENODEV; + goto err_release_region; + } + + rx_buffer_size = (pdata->max_rx_buffer_size)?:4096; + tx_buffer_size = (pdata->max_tx_buffer_size)?:4096; + i2c_dev = kzalloc(sizeof(struct tegra_i2c_slave_dev) + + rx_buffer_size + tx_buffer_size, GFP_KERNEL); + if (!i2c_dev) { + ret = -ENOMEM; + goto err_clk_put; + } + + i2c_dev->base = base; + i2c_dev->clk = clk; + i2c_dev->iomem = iomem; + i2c_dev->irq = irq; + i2c_dev->cont_id = pdev->id; + i2c_dev->dev = &pdev->dev; + i2c_dev->bus_clk = pdata->bus_clk_rate?: 100000; + i2c_dev->rx_msg_buff = (u8 *)(i2c_dev+1); + i2c_dev->rx_msg_buf_size = rx_buffer_size; + i2c_dev->rx_msg_head = 0; + i2c_dev->rx_msg_tail = 0; + i2c_dev->is_rx_waiting = 0; + i2c_dev->tx_msg_buff = i2c_dev->rx_msg_buff + rx_buffer_size; + i2c_dev->tx_msg_buf_size = tx_buffer_size; + i2c_dev->tx_msg_head = 0; + i2c_dev->tx_msg_tail = 0; + i2c_dev->is_tx_waiting = 0; + + i2c_dev->is_slave_started = false; + spin_lock_init(&i2c_dev->lock); + + init_completion(&i2c_dev->rx_msg_complete); + init_completion(&i2c_dev->tx_msg_complete); + + platform_set_drvdata(pdev, i2c_dev); + + ret = request_irq(i2c_dev->irq, tegra_i2c_slave_isr, IRQF_DISABLED, + pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); + goto err_free; + } + + i2c_bus = &i2c_dev->bus; + i2c_bus->dev = i2c_dev; + i2c_bus->pinmux = pdata->pinmux; + i2c_bus->mux_len = pdata->bus_mux_len; + i2c_bus->bus_clk_rate = pdata->bus_clk_rate ?: 100000; + + i2c_bus->slv_adap.slv_algo = &tegra_i2c_slave_algo; + i2c_bus->slv_adap.owner = THIS_MODULE; + i2c_bus->slv_adap.class = I2C_CLASS_HWMON; + strlcpy(i2c_bus->slv_adap.name, "Tegra I2C SLAVE adapter", + sizeof(i2c_bus->slv_adap.name)); + i2c_bus->slv_adap.parent_dev = &pdev->dev; + i2c_bus->slv_adap.dev = NULL; + i2c_bus->slv_adap.nr = pdata->adapter_nr; + ret = i2c_add_slave_adapter(&i2c_bus->slv_adap, true); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add I2C adapter\n"); + goto err_free_irq; + } + i2c_set_slave_adapdata(&i2c_bus->slv_adap, i2c_bus); + dev_dbg(&pdev->dev, "%s() suucess\n", __func__); + pm_runtime_enable(i2c_dev->dev); + return 0; + +err_free_irq: + free_irq(i2c_dev->irq, i2c_dev); +err_free: + kfree(i2c_dev); +err_clk_put: + clk_put(clk); +err_release_region: + release_mem_region(iomem->start, resource_size(iomem)); +err_iounmap: + iounmap(base); + dev_dbg(&pdev->dev, "%s() failed %d\n", __func__, ret); + return ret; +} + +static int tegra_i2c_slave_remove(struct platform_device *pdev) +{ + struct tegra_i2c_slave_dev *i2c_dev = platform_get_drvdata(pdev); + + i2c_del_slave_adapter(&i2c_dev->bus.slv_adap); + pm_runtime_disable(i2c_dev->dev); + free_irq(i2c_dev->irq, i2c_dev); + clk_put(i2c_dev->clk); + release_mem_region(i2c_dev->iomem->start, + resource_size(i2c_dev->iomem)); + iounmap(i2c_dev->base); + kfree(i2c_dev); + return 0; +} + +#ifdef CONFIG_PM +static int tegra_i2c_slave_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int tegra_i2c_slave_resume(struct platform_device *pdev) +{ + return 0; +} +#endif +#if defined(CONFIG_PM_RUNTIME) +static int tegra_i2c_slave_runtime_idle(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_i2c_slave_dev *i2c_dev = platform_get_drvdata(pdev); + clk_disable(i2c_dev->clk); + return 0; +} +static int tegra_i2c_slave_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_i2c_slave_dev *i2c_dev = platform_get_drvdata(pdev); + clk_enable(i2c_dev->clk); + return 0; +} +static const struct dev_pm_ops tegra_i2c_slave_dev_pm_ops = { + .runtime_idle = tegra_i2c_slave_runtime_idle, + .runtime_resume = tegra_i2c_slave_runtime_resume, +}; +#endif + +static struct platform_driver tegra_i2c_slave_driver = { + .probe = tegra_i2c_slave_probe, + .remove = tegra_i2c_slave_remove, +#ifdef CONFIG_PM + .suspend = tegra_i2c_slave_suspend, + .resume = tegra_i2c_slave_resume, +#endif + .driver = { + .name = "tegra-i2c-slave", + .owner = THIS_MODULE, +#if defined(CONFIG_PM_RUNTIME) + .pm = &tegra_i2c_slave_dev_pm_ops, +#endif + }, +}; + +static int __init tegra_i2c_slave_init_driver(void) +{ + return platform_driver_register(&tegra_i2c_slave_driver); +} + +static void __exit tegra_i2c_slave_exit_driver(void) +{ + platform_driver_unregister(&tegra_i2c_slave_driver); +} +subsys_initcall(tegra_i2c_slave_init_driver); +module_exit(tegra_i2c_slave_exit_driver); diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c new file mode 100644 index 00000000000..b1b3447942c --- /dev/null +++ b/drivers/i2c/busses/i2c-stub.c @@ -0,0 +1,222 @@ +/* + i2c-stub.c - I2C/SMBus chip emulator + + Copyright (c) 2004 Mark M. Hoffman + Copyright (C) 2007 Jean Delvare + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define DEBUG 1 + +#include +#include +#include +#include +#include +#include + +#define MAX_CHIPS 10 +#define STUB_FUNC (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_I2C_BLOCK) + +static unsigned short chip_addr[MAX_CHIPS]; +module_param_array(chip_addr, ushort, NULL, S_IRUGO); +MODULE_PARM_DESC(chip_addr, + "Chip addresses (up to 10, between 0x03 and 0x77)"); + +static unsigned long functionality = STUB_FUNC; +module_param(functionality, ulong, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(functionality, "Override functionality bitfield"); + +struct stub_chip { + u8 pointer; + u16 words[256]; /* Byte operations use the LSB as per SMBus + specification */ +}; + +static struct stub_chip *stub_chips; + +/* Return negative errno on error. */ +static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, union i2c_smbus_data * data) +{ + s32 ret; + int i, len; + struct stub_chip *chip = NULL; + + /* Search for the right chip */ + for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { + if (addr == chip_addr[i]) { + chip = stub_chips + i; + break; + } + } + if (!chip) + return -ENODEV; + + switch (size) { + + case I2C_SMBUS_QUICK: + dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr); + ret = 0; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + chip->pointer = command; + dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " + "wrote 0x%02x.\n", + addr, command); + } else { + data->byte = chip->words[chip->pointer++] & 0xff; + dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " + "read 0x%02x.\n", + addr, data->byte); + } + + ret = 0; + break; + + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) { + chip->words[command] &= 0xff00; + chip->words[command] |= data->byte; + dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " + "wrote 0x%02x at 0x%02x.\n", + addr, data->byte, command); + } else { + data->byte = chip->words[command] & 0xff; + dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " + "read 0x%02x at 0x%02x.\n", + addr, data->byte, command); + } + chip->pointer = command + 1; + + ret = 0; + break; + + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_WRITE) { + chip->words[command] = data->word; + dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " + "wrote 0x%04x at 0x%02x.\n", + addr, data->word, command); + } else { + data->word = chip->words[command]; + dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " + "read 0x%04x at 0x%02x.\n", + addr, data->word, command); + } + + ret = 0; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = data->block[0]; + if (read_write == I2C_SMBUS_WRITE) { + for (i = 0; i < len; i++) { + chip->words[command + i] &= 0xff00; + chip->words[command + i] |= data->block[1 + i]; + } + dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, " + "wrote %d bytes at 0x%02x.\n", + addr, len, command); + } else { + for (i = 0; i < len; i++) { + data->block[1 + i] = + chip->words[command + i] & 0xff; + } + dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, " + "read %d bytes at 0x%02x.\n", + addr, len, command); + } + + ret = 0; + break; + + default: + dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); + ret = -EOPNOTSUPP; + break; + } /* switch (size) */ + + return ret; +} + +static u32 stub_func(struct i2c_adapter *adapter) +{ + return STUB_FUNC & functionality; +} + +static const struct i2c_algorithm smbus_algorithm = { + .functionality = stub_func, + .smbus_xfer = stub_xfer, +}; + +static struct i2c_adapter stub_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, + .name = "SMBus stub driver", +}; + +static int __init i2c_stub_init(void) +{ + int i, ret; + + if (!chip_addr[0]) { + printk(KERN_ERR "i2c-stub: Please specify a chip address\n"); + return -ENODEV; + } + + for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { + if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) { + printk(KERN_ERR "i2c-stub: Invalid chip address " + "0x%02x\n", chip_addr[i]); + return -EINVAL; + } + + printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n", + chip_addr[i]); + } + + /* Allocate memory for all chips at once */ + stub_chips = kzalloc(i * sizeof(struct stub_chip), GFP_KERNEL); + if (!stub_chips) { + printk(KERN_ERR "i2c-stub: Out of memory\n"); + return -ENOMEM; + } + + ret = i2c_add_adapter(&stub_adapter); + if (ret) + kfree(stub_chips); + return ret; +} + +static void __exit i2c_stub_exit(void) +{ + i2c_del_adapter(&stub_adapter); + kfree(stub_chips); +} + +MODULE_AUTHOR("Mark M. Hoffman "); +MODULE_DESCRIPTION("I2C stub driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_stub_init); +module_exit(i2c_stub_exit); + diff --git a/drivers/i2c/i2c-slave.c b/drivers/i2c/i2c-slave.c new file mode 100644 index 00000000000..280a860cd2e --- /dev/null +++ b/drivers/i2c/i2c-slave.c @@ -0,0 +1,281 @@ +/* + * i2c-slave.c - a device driver for the iic-slave bus interface. + * + * Copyright (c) 2009-2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +struct i2c_slave_priv { + struct i2c_adapter master_adap; + struct i2c_slave_adapter *slave_adap; + struct i2c_algorithm master_algo; +}; + +/** + * i2c_slave_send - Sends data to master. When master issues a read cycle, the + * data is sent by the slave. + * This function copies the client data into the slave tx buffer and return to + * client. This is not a blocking call. Data will be sent to master later once + * slave got the master-ready cycle transfer. + * if there is no sufficient space to write the client buffer, it will return + * error. it will not write partial data. + * @client: Handle to i2c-slave client. + * @buf: Data that will be written to the master + * @count: How many bytes to write. + * + * Returns negative errno, or else the number of bytes written. + */ +int i2c_slave_send(struct i2c_client *client, const char *buf, int count) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_slave_priv *priv = adap->algo_data; + + if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT)) + BUG(); + + if (priv->slave_adap->slv_algo->slave_send) + return priv->slave_adap->slv_algo->slave_send(priv->slave_adap, + buf, count); + return -ENODEV; +} +EXPORT_SYMBOL(i2c_slave_send); + +/** + * i2c_slave_get_tx_status - Get amount of data available in tx buffer. If there + * is still data in tx buffer then wait for given time to transfer complete + * for a give timeout. + * @client: Handle to i2c-slave client. + * @timeout_ms: Time to wait for transfer to complete. + * + * Returns negative errno, or else the number of bytes remaining in tx buffer. + */ +int i2c_slave_get_tx_status(struct i2c_client *client, int timeout_ms) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_slave_priv *priv = adap->algo_data; + + if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT)) + BUG(); + + if (priv->slave_adap->slv_algo->slave_get_tx_status) + return priv->slave_adap->slv_algo->slave_get_tx_status( + priv->slave_adap, timeout_ms); + return -ENODEV; +} +EXPORT_SYMBOL(i2c_slave_get_tx_status); + +/** + * i2c_slave_recv - Receive data from master. The data receive from master is + * stored on slave rx buffer. When this api will be called, the data will be + * copied from the slave rx buffer to client buffer. If requested amount (count) + * of data is not available then it will wait for either min_count to be receive + * or timeout whatever first. + * + * if timeout_ms = 0, then wait for min_count data to be read. + * if timoue_ms non zero then wait for the data till timeout happen. + * @client: Handle to i2c-slave client. + * @buf: Data that will be read from the master + * @count: How many bytes to read. + * @min_count: Block till read min_count of data. + * @timeout_ms: Time to wait for read to be complete. + * + * Returns negative errno, or else the number of bytes read. + */ +int i2c_slave_recv(struct i2c_client *client, char *buf, int count, + int min_count, int timeout_ms) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_slave_priv *priv = adap->algo_data; + + if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT)) + BUG(); + + if (priv->slave_adap->slv_algo->slave_recv) + return priv->slave_adap->slv_algo->slave_recv(priv->slave_adap, + buf, count, min_count, timeout_ms); + + return -ENODEV; +} +EXPORT_SYMBOL(i2c_slave_recv); + +/** + * i2c_slave_start - Start the i2c slave to receive/transmit data. + * After this i2c controller starts responding master. + * The dummy-char will send to master if there is no data to send on slave tx + * buffer. + * @client: Handle to i2c-slave client. + * @dummy_char: Data which will be send to master if there is no data to be send + * in slave tx buffer. + * + * Returns negative errno, or else 0 for success. + */ +int i2c_slave_start(struct i2c_client *client, unsigned char dummy_char) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_slave_priv *priv = adap->algo_data; + int slave_add; + int is_10bit_addr; + + if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT)) + BUG(); + slave_add = client->addr; + is_10bit_addr = (client->flags & I2C_CLIENT_TEN) ? 1 : 0; + if (priv->slave_adap->slv_algo->slave_start) + return priv->slave_adap->slv_algo->slave_start(priv->slave_adap, + slave_add, is_10bit_addr, dummy_char); + return -ENODEV; +} +EXPORT_SYMBOL(i2c_slave_start); + +/** + * i2c_slave_stop - Stop slave to receive/transmit data. + * After this i2c controller stops responding master. + * @client: Handle to i2c-slave client. + * @is_buffer_clear: Reset the tx and rx slave buffer or not. + */ +void i2c_slave_stop(struct i2c_client *client, int is_buffer_clear) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_slave_priv *priv = adap->algo_data; + + if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT)) + BUG(); + + if (priv->slave_adap->slv_algo->slave_stop) + return priv->slave_adap->slv_algo->slave_stop(priv->slave_adap, + is_buffer_clear); +} +EXPORT_SYMBOL(i2c_slave_stop); + +/** + * i2c_slave_flush_buffer - Flush the receive and transmit buffer. + * @client: Handle to i2c-slave client. + * @is_flush_tx_buffer: Reset the tx slave buffer or not. + * @is_flush_rx_buffer: Reset the rx slave buffer or not. + * + * Returns negative errno, or else 0 for success. + */ +int i2c_slave_flush_buffer(struct i2c_client *client, + int is_flush_tx_buffer, int is_flush_rx_buffer) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_slave_priv *priv = adap->algo_data; + + if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT)) + BUG(); + + if (priv->slave_adap->slv_algo->slave_flush_buffer) + return priv->slave_adap->slv_algo->slave_flush_buffer( + priv->slave_adap, is_flush_tx_buffer, + is_flush_rx_buffer); + + return -ENODEV; +} +EXPORT_SYMBOL(i2c_slave_flush_buffer); + +/** + * i2c_slave_get_nack_cycle - Get the number of master read cycle on which + * dummy char sent. This is the way to find that how much cycle slave sent the + * NACK packet. + * + * @client: Handle to i2c-slave client. + * @is_cout_reset: Reset the nack count or not. + * + * Returns negative errno, or else 0 for success. + */ +int i2c_slave_get_nack_cycle(struct i2c_client *client, + int is_cout_reset) +{ + struct i2c_adapter *adap = client->adapter; + struct i2c_slave_priv *priv = adap->algo_data; + + if (!(adap->algo->functionality(adap) & I2C_FUNC_I2C_SLAVE_SUPPORT)) + BUG(); + + if (priv->slave_adap->slv_algo->slave_get_nack_cycle) + return priv->slave_adap->slv_algo->slave_get_nack_cycle( + priv->slave_adap, is_cout_reset); + + return -ENODEV; +} +EXPORT_SYMBOL(i2c_slave_get_nack_cycle); + +static u32 i2c_slave_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C_SLAVE_SUPPORT; +} + +int i2c_add_slave_adapter(struct i2c_slave_adapter *slv_adap, bool force_nr) +{ + struct i2c_slave_priv *priv; + int ret; + + priv = kzalloc(sizeof(struct i2c_slave_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Set up private adapter data */ + priv->slave_adap = slv_adap; + slv_adap->parent_data = priv; + + priv->master_algo.functionality = i2c_slave_func; + + /* Now fill out new adapter structure */ + snprintf(priv->master_adap.name, sizeof(priv->master_adap.name), + "i2c-%d-slave", slv_adap->nr); + priv->master_adap.owner = THIS_MODULE; + priv->master_adap.class = slv_adap->class; + priv->master_adap.algo = &priv->master_algo; + priv->master_adap.algo_data = priv; + priv->master_adap.dev.parent = slv_adap->parent_dev; + + if (force_nr) { + priv->master_adap.nr = slv_adap->nr; + ret = i2c_add_numbered_adapter(&priv->master_adap); + } else { + ret = i2c_add_adapter(&priv->master_adap); + } + if (ret < 0) { + dev_err(slv_adap->parent_dev, + "failed to add slave-adapter (error=%d)\n", ret); + kfree(priv); + return ret; + } + slv_adap->dev = &priv->master_adap.dev; + dev_info(slv_adap->parent_dev, "Added slave i2c bus %d\n", + i2c_adapter_id(&priv->master_adap)); + + return 0; +} +EXPORT_SYMBOL_GPL(i2c_add_slave_adapter); + +int i2c_del_slave_adapter(struct i2c_slave_adapter *slv_adap) +{ + struct i2c_slave_priv *priv = slv_adap->parent_data; + int ret; + + ret = i2c_del_adapter(&priv->master_adap); + if (ret < 0) + return ret; + kfree(priv); + return 0; +} +EXPORT_SYMBOL_GPL(i2c_del_slave_adapter); diff --git a/drivers/i2c/muxes/gpio-i2cmux.c b/drivers/i2c/muxes/gpio-i2cmux.c new file mode 100644 index 00000000000..7b6ce624cd6 --- /dev/null +++ b/drivers/i2c/muxes/gpio-i2cmux.c @@ -0,0 +1,184 @@ +/* + * I2C multiplexer using GPIO API + * + * Peter Korsgaard + * + * 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 +#include + +struct gpiomux { + struct i2c_adapter *parent; + struct i2c_adapter **adap; /* child busses */ + struct gpio_i2cmux_platform_data data; +}; + +static void gpiomux_set(const struct gpiomux *mux, unsigned val) +{ + int i; + + for (i = 0; i < mux->data.n_gpios; i++) + gpio_set_value(mux->data.gpios[i], val & (1 << i)); +} + +static int gpiomux_select(struct i2c_adapter *adap, void *data, u32 chan) +{ + struct gpiomux *mux = data; + + gpiomux_set(mux, mux->data.values[chan]); + + return 0; +} + +static int gpiomux_deselect(struct i2c_adapter *adap, void *data, u32 chan) +{ + struct gpiomux *mux = data; + + gpiomux_set(mux, mux->data.idle); + + return 0; +} + +static int __devinit gpiomux_probe(struct platform_device *pdev) +{ + struct gpiomux *mux; + struct gpio_i2cmux_platform_data *pdata; + struct i2c_adapter *parent; + int (*deselect) (struct i2c_adapter *, void *, u32); + unsigned initial_state; + int i, ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + + parent = i2c_get_adapter(pdata->parent); + if (!parent) { + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", + pdata->parent); + return -ENODEV; + } + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) { + ret = -ENOMEM; + goto alloc_failed; + } + + mux->parent = parent; + mux->data = *pdata; + mux->adap = kzalloc(sizeof(struct i2c_adapter *) * pdata->n_values, + GFP_KERNEL); + if (!mux->adap) { + ret = -ENOMEM; + goto alloc_failed2; + } + + if (pdata->idle != GPIO_I2CMUX_NO_IDLE) { + initial_state = pdata->idle; + deselect = gpiomux_deselect; + } else { + initial_state = pdata->values[0]; + deselect = NULL; + } + + for (i = 0; i < pdata->n_gpios; i++) { + ret = gpio_request(pdata->gpios[i], "gpio-i2cmux"); + if (ret) + goto err_request_gpio; + gpio_direction_output(pdata->gpios[i], + initial_state & (1 << i)); + } + + for (i = 0; i < pdata->n_values; i++) { + u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0; + + mux->adap[i] = i2c_add_mux_adapter(parent, mux, nr, i, + gpiomux_select, deselect); + if (!mux->adap[i]) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to add adapter %d\n", i); + goto add_adapter_failed; + } + } + + dev_info(&pdev->dev, "%d port mux on %s adapter\n", + pdata->n_values, parent->name); + + platform_set_drvdata(pdev, mux); + + return 0; + +add_adapter_failed: + for (; i > 0; i--) + i2c_del_mux_adapter(mux->adap[i - 1]); + i = pdata->n_gpios; +err_request_gpio: + for (; i > 0; i--) + gpio_free(pdata->gpios[i - 1]); + kfree(mux->adap); +alloc_failed2: + kfree(mux); +alloc_failed: + i2c_put_adapter(parent); + + return ret; +} + +static int __devexit gpiomux_remove(struct platform_device *pdev) +{ + struct gpiomux *mux = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < mux->data.n_values; i++) + i2c_del_mux_adapter(mux->adap[i]); + + for (i = 0; i < mux->data.n_gpios; i++) + gpio_free(mux->data.gpios[i]); + + platform_set_drvdata(pdev, NULL); + i2c_put_adapter(mux->parent); + kfree(mux->adap); + kfree(mux); + + return 0; +} + +static struct platform_driver gpiomux_driver = { + .probe = gpiomux_probe, + .remove = __devexit_p(gpiomux_remove), + .driver = { + .owner = THIS_MODULE, + .name = "gpio-i2cmux", + }, +}; + +static int __init gpiomux_init(void) +{ + return platform_driver_register(&gpiomux_driver); +} + +static void __exit gpiomux_exit(void) +{ + platform_driver_unregister(&gpiomux_driver); +} + +module_init(gpiomux_init); +module_exit(gpiomux_exit); + +MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver"); +MODULE_AUTHOR("Peter Korsgaard "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-i2cmux"); diff --git a/drivers/i2c/muxes/pca9541.c b/drivers/i2c/muxes/pca9541.c new file mode 100644 index 00000000000..ed699c5aa79 --- /dev/null +++ b/drivers/i2c/muxes/pca9541.c @@ -0,0 +1,411 @@ +/* + * I2C multiplexer driver for PCA9541 bus master selector + * + * Copyright (c) 2010 Ericsson AB. + * + * Author: Guenter Roeck + * + * Derived from: + * pca954x.c + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * The PCA9541 is a bus master selector. It supports two I2C masters connected + * to a single slave bus. + * + * Before each bus transaction, a master has to acquire bus ownership. After the + * transaction is complete, bus ownership has to be released. This fits well + * into the I2C multiplexer framework, which provides select and release + * functions for this purpose. For this reason, this driver is modeled as + * single-channel I2C bus multiplexer. + * + * This driver assumes that the two bus masters are controlled by two different + * hosts. If a single host controls both masters, platform code has to ensure + * that only one of the masters is instantiated at any given time. + */ + +#define PCA9541_CONTROL 0x01 +#define PCA9541_ISTAT 0x02 + +#define PCA9541_CTL_MYBUS (1 << 0) +#define PCA9541_CTL_NMYBUS (1 << 1) +#define PCA9541_CTL_BUSON (1 << 2) +#define PCA9541_CTL_NBUSON (1 << 3) +#define PCA9541_CTL_BUSINIT (1 << 4) +#define PCA9541_CTL_TESTON (1 << 6) +#define PCA9541_CTL_NTESTON (1 << 7) + +#define PCA9541_ISTAT_INTIN (1 << 0) +#define PCA9541_ISTAT_BUSINIT (1 << 1) +#define PCA9541_ISTAT_BUSOK (1 << 2) +#define PCA9541_ISTAT_BUSLOST (1 << 3) +#define PCA9541_ISTAT_MYTEST (1 << 6) +#define PCA9541_ISTAT_NMYTEST (1 << 7) + +#define BUSON (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON) +#define MYBUS (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS) +#define mybus(x) (!((x) & MYBUS) || ((x) & MYBUS) == MYBUS) +#define busoff(x) (!((x) & BUSON) || ((x) & BUSON) == BUSON) + +/* arbitration timeouts, in jiffies */ +#define ARB_TIMEOUT (HZ / 8) /* 125 ms until forcing bus ownership */ +#define ARB2_TIMEOUT (HZ / 4) /* 250 ms until acquisition failure */ + +/* arbitration retry delays, in us */ +#define SELECT_DELAY_SHORT 50 +#define SELECT_DELAY_LONG 1000 + +struct pca9541 { + struct i2c_adapter *mux_adap; + unsigned long select_timeout; + unsigned long arb_timeout; +}; + +static const struct i2c_device_id pca9541_id[] = { + {"pca9541", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pca9541_id); + +/* + * Write to chip register. Don't use i2c_transfer()/i2c_smbus_xfer() + * as they will try to lock the adapter a second time. + */ +static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val) +{ + struct i2c_adapter *adap = client->adapter; + int ret; + + if (adap->algo->master_xfer) { + struct i2c_msg msg; + char buf[2]; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + buf[0] = command; + buf[1] = val; + msg.buf = buf; + ret = adap->algo->master_xfer(adap, &msg, 1); + } else { + union i2c_smbus_data data; + + data.byte = val; + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_WRITE, + command, + I2C_SMBUS_BYTE_DATA, &data); + } + + return ret; +} + +/* + * Read from chip register. Don't use i2c_transfer()/i2c_smbus_xfer() + * as they will try to lock adapter a second time. + */ +static int pca9541_reg_read(struct i2c_client *client, u8 command) +{ + struct i2c_adapter *adap = client->adapter; + int ret; + u8 val; + + if (adap->algo->master_xfer) { + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &command + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &val + } + }; + ret = adap->algo->master_xfer(adap, msg, 2); + if (ret == 2) + ret = val; + else if (ret >= 0) + ret = -EIO; + } else { + union i2c_smbus_data data; + + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_READ, + command, + I2C_SMBUS_BYTE_DATA, &data); + if (!ret) + ret = data.byte; + } + return ret; +} + +/* + * Arbitration management functions + */ + +/* Release bus. Also reset NTESTON and BUSINIT if it was set. */ +static void pca9541_release_bus(struct i2c_client *client) +{ + int reg; + + reg = pca9541_reg_read(client, PCA9541_CONTROL); + if (reg >= 0 && !busoff(reg) && mybus(reg)) + pca9541_reg_write(client, PCA9541_CONTROL, + (reg & PCA9541_CTL_NBUSON) >> 1); +} + +/* + * Arbitration is defined as a two-step process. A bus master can only activate + * the slave bus if it owns it; otherwise it has to request ownership first. + * This multi-step process ensures that access contention is resolved + * gracefully. + * + * Bus Ownership Other master Action + * state requested access + * ---------------------------------------------------- + * off - yes wait for arbitration timeout or + * for other master to drop request + * off no no take ownership + * off yes no turn on bus + * on yes - done + * on no - wait for arbitration timeout or + * for other master to release bus + * + * The main contention point occurs if the slave bus is off and both masters + * request ownership at the same time. In this case, one master will turn on + * the slave bus, believing that it owns it. The other master will request + * bus ownership. Result is that the bus is turned on, and master which did + * _not_ own the slave bus before ends up owning it. + */ + +/* Control commands per PCA9541 datasheet */ +static const u8 pca9541_control[16] = { + 4, 0, 1, 5, 4, 4, 5, 5, 0, 0, 1, 1, 0, 4, 5, 1 +}; + +/* + * Channel arbitration + * + * Return values: + * <0: error + * 0 : bus not acquired + * 1 : bus acquired + */ +static int pca9541_arbitrate(struct i2c_client *client) +{ + struct pca9541 *data = i2c_get_clientdata(client); + int reg; + + reg = pca9541_reg_read(client, PCA9541_CONTROL); + if (reg < 0) + return reg; + + if (busoff(reg)) { + int istat; + /* + * Bus is off. Request ownership or turn it on unless + * other master requested ownership. + */ + istat = pca9541_reg_read(client, PCA9541_ISTAT); + if (!(istat & PCA9541_ISTAT_NMYTEST) + || time_is_before_eq_jiffies(data->arb_timeout)) { + /* + * Other master did not request ownership, + * or arbitration timeout expired. Take the bus. + */ + pca9541_reg_write(client, + PCA9541_CONTROL, + pca9541_control[reg & 0x0f] + | PCA9541_CTL_NTESTON); + data->select_timeout = SELECT_DELAY_SHORT; + } else { + /* + * Other master requested ownership. + * Set extra long timeout to give it time to acquire it. + */ + data->select_timeout = SELECT_DELAY_LONG * 2; + } + } else if (mybus(reg)) { + /* + * Bus is on, and we own it. We are done with acquisition. + * Reset NTESTON and BUSINIT, then return success. + */ + if (reg & (PCA9541_CTL_NTESTON | PCA9541_CTL_BUSINIT)) + pca9541_reg_write(client, + PCA9541_CONTROL, + reg & ~(PCA9541_CTL_NTESTON + | PCA9541_CTL_BUSINIT)); + return 1; + } else { + /* + * Other master owns the bus. + * If arbitration timeout has expired, force ownership. + * Otherwise request it. + */ + data->select_timeout = SELECT_DELAY_LONG; + if (time_is_before_eq_jiffies(data->arb_timeout)) { + /* Time is up, take the bus and reset it. */ + pca9541_reg_write(client, + PCA9541_CONTROL, + pca9541_control[reg & 0x0f] + | PCA9541_CTL_BUSINIT + | PCA9541_CTL_NTESTON); + } else { + /* Request bus ownership if needed */ + if (!(reg & PCA9541_CTL_NTESTON)) + pca9541_reg_write(client, + PCA9541_CONTROL, + reg | PCA9541_CTL_NTESTON); + } + } + return 0; +} + +static int pca9541_select_chan(struct i2c_adapter *adap, void *client, u32 chan) +{ + struct pca9541 *data = i2c_get_clientdata(client); + int ret; + unsigned long timeout = jiffies + ARB2_TIMEOUT; + /* give up after this time */ + + data->arb_timeout = jiffies + ARB_TIMEOUT; + /* force bus ownership after this time */ + + do { + ret = pca9541_arbitrate(client); + if (ret) + return ret < 0 ? ret : 0; + + if (data->select_timeout == SELECT_DELAY_SHORT) + udelay(data->select_timeout); + else + msleep(data->select_timeout / 1000); + } while (time_is_after_eq_jiffies(timeout)); + + return -ETIMEDOUT; +} + +static int pca9541_release_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + pca9541_release_bus(client); + return 0; +} + +/* + * I2C init/probing/exit functions + */ +static int pca9541_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = client->adapter; + struct pca954x_platform_data *pdata = client->dev.platform_data; + struct pca9541 *data; + int force; + int ret = -ENODEV; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) + goto err; + + data = kzalloc(sizeof(struct pca9541), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + + i2c_set_clientdata(client, data); + + /* + * I2C accesses are unprotected here. + * We have to lock the adapter before releasing the bus. + */ + i2c_lock_adapter(adap); + pca9541_release_bus(client); + i2c_unlock_adapter(adap); + + /* Create mux adapter */ + + force = 0; + if (pdata) + force = pdata->modes[0].adap_id; + data->mux_adap = i2c_add_mux_adapter(adap, client, force, 0, + pca9541_select_chan, + pca9541_release_chan); + + if (data->mux_adap == NULL) { + dev_err(&client->dev, "failed to register master selector\n"); + goto exit_free; + } + + dev_info(&client->dev, "registered master selector for I2C %s\n", + client->name); + + return 0; + +exit_free: + kfree(data); +err: + return ret; +} + +static int pca9541_remove(struct i2c_client *client) +{ + struct pca9541 *data = i2c_get_clientdata(client); + + i2c_del_mux_adapter(data->mux_adap); + + kfree(data); + return 0; +} + +static struct i2c_driver pca9541_driver = { + .driver = { + .name = "pca9541", + .owner = THIS_MODULE, + }, + .probe = pca9541_probe, + .remove = pca9541_remove, + .id_table = pca9541_id, +}; + +static int __init pca9541_init(void) +{ + return i2c_add_driver(&pca9541_driver); +} + +static void __exit pca9541_exit(void) +{ + i2c_del_driver(&pca9541_driver); +} + +module_init(pca9541_init); +module_exit(pca9541_exit); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("PCA9541 I2C master selector driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c new file mode 100644 index 00000000000..dd14ae38d3e --- /dev/null +++ b/drivers/i2c/muxes/pca954x.c @@ -0,0 +1,404 @@ +/* + * I2C multiplexer + * + * Copyright (c) 2008-2009 Rodolfo Giometti + * Copyright (c) 2008-2009 Eurotech S.p.A. + * + * This module supports the PCA954x series of I2C multiplexer/switch chips + * made by Philips Semiconductors. + * This includes the: + * PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547 + * and PCA9548. + * + * These chips are all controlled via the I2C bus itself, and all have a + * single 8-bit register. The upstream "parent" bus fans out to two, + * four, or eight downstream busses or channels; which of these + * are selected is determined by the chip type and register contents. A + * mux can select only one sub-bus at a time; a switch can select any + * combination simultaneously. + * + * Based on: + * pca954x.c from Kumar Gala + * Copyright (C) 2006 + * + * Based on: + * pca954x.c from Ken Harrenstien + * Copyright (C) 2004 Google, Inc. (Ken Harrenstien) + * + * Based on: + * i2c-virtual_cb.c from Brian Kuschak + * and + * pca9540.c from Jean Delvare . + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PCA954X_MAX_NCHANS 8 + +enum pca_type { + pca_9540, + pca_9542, + pca_9543, + pca_9544, + pca_9545, + pca_9546, + pca_9547, + pca_9548, +}; + +struct pca954x { + enum pca_type type; + struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS]; + + u8 last_chan; /* last register value */ + struct regulator *vcc_reg; + struct regulator *i2c_reg; +}; + +struct chip_desc { + u8 nchans; + u8 enable; /* used for muxes only */ + enum muxtype { + pca954x_ismux = 0, + pca954x_isswi + } muxtype; +}; + +/* Provide specs for the PCA954x types we know about */ +static const struct chip_desc chips[] = { + [pca_9540] = { + .nchans = 2, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9543] = { + .nchans = 2, + .muxtype = pca954x_isswi, + }, + [pca_9544] = { + .nchans = 4, + .enable = 0x4, + .muxtype = pca954x_ismux, + }, + [pca_9545] = { + .nchans = 4, + .muxtype = pca954x_isswi, + }, + [pca_9547] = { + .nchans = 8, + .enable = 0x8, + .muxtype = pca954x_ismux, + }, + [pca_9548] = { + .nchans = 8, + .muxtype = pca954x_isswi, + }, +}; + +static const struct i2c_device_id pca954x_id[] = { + { "pca9540", pca_9540 }, + { "pca9542", pca_9540 }, + { "pca9543", pca_9543 }, + { "pca9544", pca_9544 }, + { "pca9545", pca_9545 }, + { "pca9546", pca_9545 }, + { "pca9547", pca_9547 }, + { "pca9548", pca_9548 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca954x_id); + +/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() + for this as they will try to lock adapter a second time */ +static int pca954x_reg_write(struct i2c_adapter *adap, + struct i2c_client *client, u8 val) +{ + int ret = -ENODEV; + struct pca954x *data = i2c_get_clientdata(client); + + /* Increase ref count for pca954x vcc */ + if (data->vcc_reg) { + ret = regulator_enable(data->vcc_reg); + if (ret) { + dev_err(&client->dev, "%s: failed to enable vcc\n", + __func__); + goto vcc_regulator_failed; + } + } + /* Increase ref count for pca954x vcc_i2c */ + if (data->i2c_reg) { + ret = regulator_enable(data->i2c_reg); + if (ret) { + dev_err(&client->dev, "%s: failed to enable vcc_i2c\n", + __func__); + goto i2c_regulator_failed; + } + } + + if (adap->algo->master_xfer) { + struct i2c_msg msg; + char buf[1]; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + buf[0] = val; + msg.buf = buf; + ret = adap->algo->master_xfer(adap, &msg, 1); + } else { + union i2c_smbus_data data; + ret = adap->algo->smbus_xfer(adap, client->addr, + client->flags, + I2C_SMBUS_WRITE, + val, I2C_SMBUS_BYTE, &data); + } + + /* Decrease ref count for pca954x vcc_i2c */ + if (data->i2c_reg) + regulator_disable(data->i2c_reg); + +i2c_regulator_failed: + /* Decrease ref count for pca954x vcc */ + if (data->vcc_reg) + regulator_disable(data->vcc_reg); +vcc_regulator_failed: + return ret; +} + +static int pca954x_select_chan(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + const struct chip_desc *chip = &chips[data->type]; + u8 regval; + int ret = 0; + + /* we make switches look like muxes, not sure how to be smarter */ + if (chip->muxtype == pca954x_ismux) + regval = chan | chip->enable; + else + regval = 1 << chan; + + /* Only select the channel if its different from the last channel */ + if (data->last_chan != regval) { + ret = pca954x_reg_write(adap, client, regval); + data->last_chan = regval; + } + + return ret; +} + +static int pca954x_deselect_mux(struct i2c_adapter *adap, + void *client, u32 chan) +{ + struct pca954x *data = i2c_get_clientdata(client); + + /* Deselect active channel */ + data->last_chan = 0; + return pca954x_reg_write(adap, client, data->last_chan); +} + +/* + * I2C init/probing/exit functions + */ +static int pca954x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + struct pca954x_platform_data *pdata = client->dev.platform_data; + int num, force; + struct pca954x *data; + int ret = -ENODEV; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) + goto err; + + data = kzalloc(sizeof(struct pca954x), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + + i2c_set_clientdata(client, data); + + /* Get regulator pointer for pca954x vcc */ + data->vcc_reg = regulator_get(&client->dev, "vcc"); + if (PTR_ERR(data->vcc_reg) == -ENODEV) + data->vcc_reg = NULL; + else if (IS_ERR(data->vcc_reg)) { + dev_err(&client->dev, "%s: failed to get vcc\n", + __func__); + ret = PTR_ERR(data->vcc_reg); + goto exit_free; + } + /* Get regulator pointer for pca954x vcc_i2c */ + data->i2c_reg = regulator_get(&client->dev, "vcc_i2c"); + if (PTR_ERR(data->i2c_reg) == -ENODEV) + data->i2c_reg = NULL; + else if (IS_ERR(data->i2c_reg)) { + dev_err(&client->dev, "%s: failed to get vcc_i2c\n", + __func__); + ret = PTR_ERR(data->i2c_reg); + regulator_put(data->vcc_reg); + goto exit_free; + } + + /* Increase ref count for pca954x vcc */ + if (data->vcc_reg) { + pr_info("%s: enable vcc\n", __func__); + ret = regulator_enable(data->vcc_reg); + if (ret) { + dev_err(&client->dev, "%s: failed to enable vcc\n", + __func__); + goto exit_regulator_put; + } + } + /* Increase ref count for pca954x vcc_i2c */ + if (data->i2c_reg) { + pr_info("%s: enable vcc_i2c\n", __func__); + ret = regulator_enable(data->i2c_reg); + if (ret) { + dev_err(&client->dev, "%s: failed to enable vcc_i2c\n", + __func__); + goto exit_vcc_regulator_disable; + } + } + + /* + * Power-On Reset takes time. + * I2C is ready after Power-On Reset. + */ + msleep(1); + + /* Write the mux register at addr to verify + * that the mux is in fact present. This also + * initializes the mux to disconnected state. + */ + if (i2c_smbus_write_byte(client, 0) < 0) { + dev_warn(&client->dev, "probe failed\n"); + goto exit_regulator_disable; + } + + /* Decrease ref count for pca954x vcc */ + if (data->vcc_reg) + regulator_disable(data->vcc_reg); + /* Decrease ref count for pca954x vcc_i2c */ + if (data->i2c_reg) + regulator_disable(data->i2c_reg); + + data->type = id->driver_data; + data->last_chan = 0; /* force the first selection */ + + /* Now create an adapter for each channel */ + for (num = 0; num < chips[data->type].nchans; num++) { + force = 0; /* dynamic adap number */ + if (pdata) { + if (num < pdata->num_modes) + /* force static number */ + force = pdata->modes[num].adap_id; + else + /* discard unconfigured channels */ + break; + } + + data->virt_adaps[num] = + i2c_add_mux_adapter(adap, client, + force, num, pca954x_select_chan, + (pdata && pdata->modes[num].deselect_on_exit) + ? pca954x_deselect_mux : NULL); + + if (data->virt_adaps[num] == NULL) { + ret = -ENODEV; + dev_err(&client->dev, + "failed to register multiplexed adapter" + " %d as bus %d\n", num, force); + goto virt_reg_failed; + } + } + + dev_info(&client->dev, + "registered %d multiplexed busses for I2C %s %s\n", + num, chips[data->type].muxtype == pca954x_ismux + ? "mux" : "switch", client->name); + + return 0; + +virt_reg_failed: + for (num--; num >= 0; num--) + i2c_del_mux_adapter(data->virt_adaps[num]); +exit_regulator_disable: + if (data->i2c_reg) + regulator_disable(data->i2c_reg); +exit_vcc_regulator_disable: + if (data->vcc_reg) + regulator_disable(data->vcc_reg); +exit_regulator_put: + regulator_put(data->i2c_reg); + regulator_put(data->vcc_reg); +exit_free: + kfree(data); +err: + return ret; +} + +static int pca954x_remove(struct i2c_client *client) +{ + struct pca954x *data = i2c_get_clientdata(client); + const struct chip_desc *chip = &chips[data->type]; + int i, err; + + for (i = 0; i < chip->nchans; ++i) + if (data->virt_adaps[i]) { + err = i2c_del_mux_adapter(data->virt_adaps[i]); + if (err) + return err; + data->virt_adaps[i] = NULL; + } + + regulator_put(data->i2c_reg); + regulator_put(data->vcc_reg); + + kfree(data); + return 0; +} + +static struct i2c_driver pca954x_driver = { + .driver = { + .name = "pca954x", + .owner = THIS_MODULE, + }, + .probe = pca954x_probe, + .remove = pca954x_remove, + .id_table = pca954x_id, +}; + +static int __init pca954x_init(void) +{ + return i2c_add_driver(&pca954x_driver); +} + +static void __exit pca954x_exit(void) +{ + i2c_del_driver(&pca954x_driver); +} + +module_init(pca954x_init); +module_exit(pca954x_exit); + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("PCA954x I2C mux/switch driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.2