Add I2C slave provider using the generic slave interface. It also supports master transactions when the slave in the idle mode. Signed-off-by: Maxim Syrchin <msyrchin@xxxxxxxxxxxxx> Signed-off-by: Joshua Frkuska <joshua_frkuska@xxxxxxxxxx> --- drivers/i2c/busses/i2c-imx.c | 724 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 694 insertions(+), 30 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 47fc1f1..7b2aeb8 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -53,6 +53,7 @@ #include <linux/pm_runtime.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/kthread.h> /* This will be the driver name the kernel reports */ #define DRIVER_NAME "imx-i2c" @@ -60,6 +61,13 @@ /* Default value */ #define IMX_I2C_BIT_RATE 100000 /* 100kHz */ +/* Wait delays */ +#define STATE_MIN_WAIT_TIME 1 /* 1 jiffy */ +#define STATE_WAIT_TIME (HZ / 10) + +#define MAX_EVENTS (1<<4) +#define EUNDEFINED 4000 + /* * Enable DMA if transfer byte size is bigger than this threshold. * As the hardware request, it must bigger than 4 bytes.\ @@ -171,6 +179,30 @@ enum imx_i2c_type { VF610_I2C, }; +enum i2c_imx_state { + STATE_IDLE = 0, + STATE_SLAVE, + STATE_MASTER, + STATE_SP +}; + +enum i2c_imx_events { + EVT_AL = 0, + EVT_SI, + EVT_START, + EVT_STOP, + EVT_POLL, + EVT_INVALID, + EVT_ENTRY +}; + +struct evt_queue { + atomic_t count; + atomic_t ir; + atomic_t iw; + unsigned int evt[MAX_EVENTS]; +}; + struct imx_i2c_hwdata { enum imx_i2c_type devtype; unsigned regshift; @@ -193,6 +225,7 @@ struct imx_i2c_dma { struct imx_i2c_struct { struct i2c_adapter adapter; + struct i2c_client *slave; struct clk *clk; void __iomem *base; wait_queue_head_t queue; @@ -210,6 +243,18 @@ struct imx_i2c_struct { struct pinctrl_state *pinctrl_pins_gpio; struct imx_i2c_dma *dma; + + unsigned int state; + struct evt_queue events; + atomic_t last_error; + + int master_interrupt; + int start_retry_cnt; + int slave_poll_cnt; + + struct task_struct *slave_task; + wait_queue_head_t state_queue; + }; static const struct imx_i2c_hwdata imx1_i2c_hwdata = { @@ -414,17 +459,29 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx) static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) { unsigned long orig_jiffies = jiffies; - unsigned int temp; + unsigned int temp, ctl; + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); while (1) { temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + + /* + * Check for arbitration lost. Datasheet recommends to + * clean IAL bit in interrupt handler before any other + * action. + * + * We can detect if controller resets MSTA bit, because + * hardware is switched to slave mode if arbitration was + * lost. + */ - /* check for arbitration lost */ - if (temp & I2SR_IAL) { - temp &= ~I2SR_IAL; - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); + if (for_busy && !(ctl & I2CR_MSTA)) { + dev_dbg(&i2c_imx->adapter.dev, + "<%s> Lost arbitration (SR = %02x, CR = %02x)\n", + __func__, temp, ctl); return -EAGAIN; } @@ -443,16 +500,501 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) return 0; } +#ifdef CONFIG_I2C_SLAVE + +static void i2c_imx_enable_i2c_controller(struct imx_i2c_struct *i2c_imx); +static void set_state(struct imx_i2c_struct *i2c_imx, unsigned int new); +static int i2c_imx_hw_start(struct imx_i2c_struct *i2c_imx); +static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx); + +static inline int evt_find_next_idx(atomic_t *v) +{ + return atomic_inc_return(v) & (MAX_EVENTS - 1); +} + +static unsigned int evt_put(struct evt_queue *stk, unsigned int evt) +{ + int count = atomic_inc_return(&stk->count); + int idx; + + if (count < MAX_EVENTS) { + idx = evt_find_next_idx(&stk->iw); + stk->evt[idx] = evt; + } else { + atomic_dec(&stk->count); + return EVT_INVALID; + } + + return 0; +} + +static unsigned int evt_get(struct evt_queue *stk) +{ + int count = atomic_dec_return(&stk->count); + int idx; + + if (count >= 0) { + idx = evt_find_next_idx(&stk->ir); + } else { + atomic_inc(&stk->count); + return EVT_INVALID; + } + + return stk->evt[idx]; +} + +static unsigned int evt_is_empty(struct evt_queue *stk) +{ + int ret = atomic_read(&stk->count); + + return (ret <= 0); +} + +static void evt_init(struct evt_queue *stk) +{ + atomic_set(&stk->count, 0); + atomic_set(&stk->iw, 0); + atomic_set(&stk->ir, 0); +} + + +static void i2c_imx_clear_ial_bit(struct imx_i2c_struct *i2c_imx) +{ + unsigned int status; + + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + status &= ~I2SR_IAL; + imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR); +} + +static void i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx) +{ + unsigned int temp; + + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + + i2c_imx_enable_i2c_controller(i2c_imx); + + /* Set Slave mode with interrupt enable */ + temp = i2c_imx->hwdata->i2cr_ien_opcode | I2CR_IIEN; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); +} + + +static void i2c_imx_slave_process_irq(struct imx_i2c_struct *i2c_imx) +{ + unsigned int status, ctl; + u8 data; + + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + + if (status & I2SR_IAAS) { + if (status & I2SR_SRW) { + /* master wants to read from us */ + i2c_slave_event(i2c_imx->slave, + I2C_SLAVE_READ_REQUESTED, &data); + ctl |= I2CR_MTX; + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR); + + /*send data */ + imx_i2c_write_reg(data, i2c_imx, IMX_I2C_I2DR); + } else { + dev_dbg(&i2c_imx->adapter.dev, "write requested"); + i2c_slave_event(i2c_imx->slave, + I2C_SLAVE_WRITE_REQUESTED, &data); + /*slave receive */ + ctl &= ~I2CR_MTX; + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR); + + /*dummy read */ + data = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + } + } else { + /* slave send */ + if (ctl & I2CR_MTX) { + if (!(status & I2SR_RXAK)) { /*ACK received */ + i2c_slave_event(i2c_imx->slave, + I2C_SLAVE_READ_PROCESSED, &data); + ctl |= I2CR_MTX; + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR); + /*send data */ + imx_i2c_write_reg(data, i2c_imx, IMX_I2C_I2DR); + } else { + /*no ACK. */ + /*dummy read */ + dev_dbg(&i2c_imx->adapter.dev, "read requested"); + i2c_slave_event(i2c_imx->slave, + I2C_SLAVE_READ_REQUESTED, &data); + + ctl &= ~I2CR_MTX; + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR); + imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + } + } else { /*read */ + ctl &= ~I2CR_MTX; + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR); + + /*read */ + data = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + dev_dbg(&i2c_imx->adapter.dev, "received %x", + (unsigned int) data); + i2c_slave_event(i2c_imx->slave, + I2C_SLAVE_WRITE_RECEIVED, &data); + } + } +} + + +static int idle_evt_handler(struct imx_i2c_struct *i2c_imx, unsigned int event) +{ + u8 reg; + + switch (event) { + case EVT_ENTRY: + imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, + i2c_imx, IMX_I2C_I2CR); + i2c_imx_enable_i2c_controller(i2c_imx); + imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode | I2CR_IIEN, + i2c_imx, IMX_I2C_I2CR); + if (atomic_read(&i2c_imx->last_error) == -EUNDEFINED) { + dev_dbg(&i2c_imx->adapter.dev, "Reset lost START event\n"); + atomic_set(&i2c_imx->last_error, -EBUSY); + } + i2c_imx->start_retry_cnt = 0; + return 0; + case EVT_AL: + i2c_imx_clear_ial_bit(i2c_imx); + break; + case EVT_SI: + set_state(i2c_imx, STATE_SLAVE); + i2c_imx_slave_process_irq(i2c_imx); + break; + case EVT_START: + reg = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + if ((reg & I2SR_IBB) != 0) { + atomic_set(&i2c_imx->last_error, -EBUSY); + break; + } + + reg = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + reg |= I2CR_MSTA; + imx_i2c_write_reg(reg, i2c_imx, IMX_I2C_I2CR); + set_state(i2c_imx, STATE_SP); + i2c_imx->start_retry_cnt = 0; + return 0; + case EVT_STOP: + case EVT_POLL: + default: + break; + } + + return STATE_WAIT_TIME; +} + +static int master_evt_handler(struct imx_i2c_struct *i2c_imx, + unsigned int event) +{ + switch (event) { + case EVT_ENTRY: + i2c_imx->start_retry_cnt = 0; + return 0; + case EVT_AL: + set_state(i2c_imx, STATE_IDLE); + break; + case EVT_SI: + break; + case EVT_START: + atomic_set(&i2c_imx->last_error, -EBUSY); + break; + case EVT_STOP: + imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, + IMX_I2C_I2SR); + imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode | I2CR_IIEN, + i2c_imx, IMX_I2C_I2CR); + + i2c_imx->stopped = 1; + udelay(50); + set_state(i2c_imx, STATE_IDLE); + return 0; + case EVT_POLL: + default: + break; + } + + return STATE_WAIT_TIME; +} + +static int slave_evt_handler (struct imx_i2c_struct *i2c_imx, + unsigned int event) +{ + u8 reg, data; + + switch (event) { + case EVT_ENTRY: + if (atomic_read(&i2c_imx->last_error) == -EUNDEFINED) { + dev_dbg(&i2c_imx->adapter.dev, "Reset lost START event\n"); + atomic_set(&i2c_imx->last_error, -EBUSY); + } + i2c_imx->start_retry_cnt = 0; + i2c_imx->slave_poll_cnt = 0; + return 0; + case EVT_AL: + set_state(i2c_imx, STATE_IDLE); + break; + case EVT_START: + reg = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + atomic_set(&i2c_imx->last_error, -EBUSY); + break; + case EVT_STOP: + break; + case EVT_SI: + i2c_imx_slave_process_irq(i2c_imx); + reg = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + if ((reg & I2SR_IBB) == 0) { + data = 0; + set_state(i2c_imx, STATE_IDLE); + dev_dbg(&i2c_imx->adapter.dev, "end of package"); + i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &data); + } + if (i2c_imx->slave_poll_cnt > 10) { + dev_err(&i2c_imx->adapter.dev, + "Too much slave loops (%i)\n", + i2c_imx->slave_poll_cnt); + } + + i2c_imx->slave_poll_cnt = 0; + break; + case EVT_POLL: + reg = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + if ((reg & I2SR_IBB) == 0) { + data = 0; + set_state(i2c_imx, STATE_IDLE); + dev_dbg(&i2c_imx->adapter.dev, "end of package"); + i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &data); + if (i2c_imx->slave_poll_cnt > 10) { + dev_err(&i2c_imx->adapter.dev, + "Too much slave loops (%i)\n", + i2c_imx->slave_poll_cnt); + } + + i2c_imx->slave_poll_cnt = 0; + } + + /* + * TODO: do "dummy read" if no interrupts or stop condition + * for more then 10 wait loops + */ + i2c_imx->slave_poll_cnt += 1; + if (i2c_imx->slave_poll_cnt % 10 == 0) + imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + break; + default: + break; + } + + return STATE_MIN_WAIT_TIME; +} + +static int sp_evt_handler (struct imx_i2c_struct *i2c_imx, unsigned int event) +{ + u8 reg; + + switch (event) { + case EVT_AL: + dev_dbg(&i2c_imx->adapter.dev, "Lost arbitration on START"); + atomic_set(&i2c_imx->last_error, -EAGAIN); + set_state(i2c_imx, STATE_IDLE); + return 0; + case EVT_SI: + set_state(i2c_imx, STATE_IDLE); + evt_put(&i2c_imx->events, EVT_SI); + case EVT_START: + atomic_set(&i2c_imx->last_error, -EBUSY); + break; + case EVT_STOP: + break; + case EVT_ENTRY: + return 0; + case EVT_POLL: + reg = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + if ((reg & I2SR_IBB) && !(reg & I2SR_IAL)) { + + set_state(i2c_imx, STATE_MASTER); + reg = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + + i2c_imx->stopped = 0; + reg |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; + reg &= ~I2CR_DMAEN; + imx_i2c_write_reg(reg, i2c_imx, IMX_I2C_I2CR); + atomic_set(&i2c_imx->last_error, 0); + i2c_imx->start_retry_cnt = 0; + return 0; + } + break; + default: + break; + + } + + if (i2c_imx->start_retry_cnt++ < 100) { + dev_dbg(&i2c_imx->adapter.dev, + "wait for busy cnt = %i evt = %i", + i2c_imx->start_retry_cnt, event); + } else { + + dev_dbg(&i2c_imx->adapter.dev, "start timeout"); + i2c_imx->start_retry_cnt = 0; + atomic_set(&i2c_imx->last_error, -ETIMEDOUT); + set_state(i2c_imx, STATE_IDLE); + return STATE_WAIT_TIME; + } + + return STATE_MIN_WAIT_TIME; +} + +static void set_state(struct imx_i2c_struct *i2c_imx, unsigned int new) +{ + i2c_imx->state = new; + + switch (new) { + case STATE_IDLE: + idle_evt_handler(i2c_imx, EVT_ENTRY); + break; + case STATE_SLAVE: + slave_evt_handler(i2c_imx, EVT_ENTRY); + break; + case STATE_SP: + sp_evt_handler(i2c_imx, EVT_ENTRY); + break; + case STATE_MASTER: + master_evt_handler(i2c_imx, EVT_ENTRY); + break; + } +} + + +static int i2c_imx_slave_threadfn(void *pdata) +{ + unsigned int event, delay; + struct imx_i2c_struct *i2c_imx = (struct imx_i2c_struct *) pdata; + + do { + + event = evt_get(&i2c_imx->events); + if (event == EVT_INVALID) + event = EVT_POLL; + + switch (i2c_imx->state) { + case STATE_IDLE: + delay = idle_evt_handler(i2c_imx, event); + break; + case STATE_SLAVE: + delay = slave_evt_handler(i2c_imx, event); + break; + case STATE_SP: + delay = sp_evt_handler(i2c_imx, event); + break; + case STATE_MASTER: + delay = master_evt_handler(i2c_imx, event); + break; + default: + delay = 0; + break; + } + + if (delay) + wait_event_interruptible_timeout(i2c_imx->state_queue, + (evt_is_empty(&i2c_imx->events) == 0), + delay); + + } while (kthread_should_stop() == 0); + + return 0; +} + +static int i2c_imx_reg_slave(struct i2c_client *slave) +{ + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(slave->adapter); + int result; + struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; + + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + + if (i2c_imx->slave) + return -EBUSY; + + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + + i2c_imx->slave = slave; + + /* Set the Slave address */ + imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR); + + result = i2c_imx_hw_start(i2c_imx); + if (result != 0) + return result; + + i2c_imx->slave_task = kthread_run(i2c_imx_slave_threadfn, + (void *)i2c_imx, "i2c-slave-%s", i2c_imx->adapter.name); + + sched_setscheduler(i2c_imx->slave_task, SCHED_FIFO, ¶m); + + if (IS_ERR(i2c_imx->slave_task)) + return PTR_ERR(i2c_imx->slave_task); + + i2c_imx_slave_init(i2c_imx); + + return 0; + +} + +static int i2c_imx_unreg_slave(struct i2c_client *slave) +{ + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(slave->adapter); + + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + if (i2c_imx->slave_task != NULL) + kthread_stop(i2c_imx->slave_task); + + wake_up(&i2c_imx->state_queue); + + i2c_imx->slave_task = NULL; + + i2c_imx->slave = NULL; + + i2c_imx_stop(i2c_imx); + + return 0; +} + + +#else + +static void evt_init(struct evt_queue *stk) +{ + return; +} + +static unsigned int evt_put(struct evt_queue *stk, unsigned int evt) +{ + return 0; +} + +#endif + static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx) { - wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10); + wait_event_timeout(i2c_imx->queue, i2c_imx->master_interrupt, + STATE_WAIT_TIME); - if (unlikely(!(i2c_imx->i2csr & I2SR_IIF))) { + if (unlikely(!(i2c_imx->master_interrupt))) { dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__); return -ETIMEDOUT; } dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__); - i2c_imx->i2csr = 0; + i2c_imx->master_interrupt = 0; return 0; } @@ -510,27 +1052,92 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx) #endif } +static int i2c_imx_configure_clock(struct imx_i2c_struct *i2c_imx) +{ + int result; + + i2c_imx_set_clk(i2c_imx); + + result = clk_prepare_enable(i2c_imx->clk); + if (result == 0) + imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR); + + return result; +} + +static void i2c_imx_enable_i2c_controller(struct imx_i2c_struct *i2c_imx) +{ + imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, + IMX_I2C_I2SR); + imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, + IMX_I2C_I2CR); + + /* Wait controller to be stable */ + udelay(50); +} + +static int i2c_imx_hw_start(struct imx_i2c_struct *i2c_imx) +{ + int result; + + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + + result = i2c_imx_configure_clock(i2c_imx); + if (result != 0) + return result; + i2c_imx_enable_i2c_controller(i2c_imx); + return 0; +} + + static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) { unsigned int temp = 0; - int result; + int result, cnt = 0; dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - i2c_imx_set_clk(i2c_imx); + if (i2c_imx->slave_task != NULL) { - imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR); - /* Enable I2C controller */ - imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR); - imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR); + atomic_set(&i2c_imx->last_error, -EUNDEFINED); + if (evt_put(&i2c_imx->events, EVT_START) != 0) { + dev_err(&i2c_imx->adapter.dev, + "Event buffer overflow\n"); + return -EBUSY; + } - /* Wait controller to be stable */ - usleep_range(50, 150); + wake_up(&i2c_imx->state_queue); + + while ((result = atomic_read(&i2c_imx->last_error)) == -EUNDEFINED) { + schedule(); + + /* TODO: debug workaround - start hung monitoring */ + cnt++; + if (cnt == 500000) { + dev_err(&i2c_imx->adapter.dev, + "Too many start loops\n"); + imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, + i2c_imx, IMX_I2C_I2CR); + i2c_imx_enable_i2c_controller(i2c_imx); + imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode | I2CR_IIEN, + i2c_imx, IMX_I2C_I2CR); + + return -ETIMEDOUT; + } + + }; + return result; + } + + result = i2c_imx_hw_start(i2c_imx); + if (result != 0) + return result; /* Start I2C transaction */ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp |= I2CR_MSTA; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + result = i2c_imx_bus_busy(i2c_imx, 1); if (result) return result; @@ -542,10 +1149,9 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) return result; } -static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) +static void i2c_imx_hw_stop(struct imx_i2c_struct *i2c_imx) { unsigned int temp = 0; - if (!i2c_imx->stopped) { /* Stop I2C transaction */ dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); @@ -555,6 +1161,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) temp &= ~I2CR_DMAEN; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); } + if (is_imx1_i2c(i2c_imx)) { /* * This delay caused by an i.MXL hardware bug. @@ -568,24 +1175,58 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) i2c_imx->stopped = 1; } - /* Disable I2C controller */ - temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, + temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + clk_disable_unprepare(i2c_imx->clk); + +} + +static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) +{ + if (i2c_imx->slave == NULL) { + i2c_imx_hw_stop(i2c_imx); + } else { + + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + + evt_put(&i2c_imx->events, EVT_STOP); + wake_up(&i2c_imx->state_queue); + } +} + +static void i2c_imx_clear_isr_bit(struct imx_i2c_struct *i2c_imx, + unsigned int status) +{ + status &= ~I2SR_IIF; + status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF); + imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR); } + + static irqreturn_t i2c_imx_isr(int irq, void *dev_id) { struct imx_i2c_struct *i2c_imx = dev_id; - unsigned int temp; + unsigned int status, ctl; + + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + if (status & I2SR_IIF) { + i2c_imx_clear_isr_bit(i2c_imx, status); + + if (ctl & I2CR_MSTA) { + dev_dbg(&i2c_imx->adapter.dev, "master interrupt"); + i2c_imx->master_interrupt = 1; + wake_up(&i2c_imx->queue); + } else if (status & I2SR_IAL) { + evt_put(&i2c_imx->events, EVT_AL); + } else { + dev_dbg(&i2c_imx->adapter.dev, "slave interrupt"); + evt_put(&i2c_imx->events, EVT_SI); + wake_up(&i2c_imx->state_queue); + } - temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); - if (temp & I2SR_IIF) { - /* save status register */ - i2c_imx->i2csr = temp; - temp &= ~I2SR_IIF; - temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF); - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); - wake_up(&i2c_imx->queue); return IRQ_HANDLED; } @@ -895,7 +1536,13 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, /* Start I2C transfer */ result = i2c_imx_start(i2c_imx); - if (result) { + if (result == -ETIMEDOUT) { + /* + * Recovery is not started on arbitration lost, since it can + * break slave transfer. But in case of "bus timeout" recovery + * it could be useful to bring controller out of "strange state". + */ + dev_dbg(&i2c_imx->adapter.dev, "call bus recovery"); if (i2c_imx->adapter.bus_recovery_info) { i2c_recover_bus(&i2c_imx->adapter); result = i2c_imx_start(i2c_imx); @@ -1040,6 +1687,10 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter) static struct i2c_algorithm i2c_imx_algo = { .master_xfer = i2c_imx_xfer, .functionality = i2c_imx_func, +#ifdef CONFIG_I2C_SLAVE + .reg_slave = i2c_imx_reg_slave, + .unreg_slave = i2c_imx_unreg_slave, +#endif }; static int i2c_imx_probe(struct platform_device *pdev) @@ -1099,6 +1750,12 @@ static int i2c_imx_probe(struct platform_device *pdev) return ret; } + i2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(i2c_imx->pinctrl)) { + ret = PTR_ERR(i2c_imx->pinctrl); + goto clk_disable; + } + /* Request IRQ */ ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0, pdev->name, i2c_imx); @@ -1109,6 +1766,7 @@ static int i2c_imx_probe(struct platform_device *pdev) /* Init queue */ init_waitqueue_head(&i2c_imx->queue); + init_waitqueue_head(&i2c_imx->state_queue); /* Set up adapter data */ i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); @@ -1160,6 +1818,9 @@ static int i2c_imx_probe(struct platform_device *pdev) /* Init DMA config if supported */ i2c_imx_dma_request(i2c_imx, phy_addr); + /* init slave_state to IDLE */ + i2c_imx->state = STATE_IDLE; + evt_init(&i2c_imx->events); return 0; /* Return OK */ rpm_disable: @@ -1186,6 +1847,9 @@ static int i2c_imx_remove(struct platform_device *pdev) dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); i2c_del_adapter(&i2c_imx->adapter); + if (i2c_imx->slave_task != NULL) + kthread_stop(i2c_imx->slave_task); + if (i2c_imx->dma) i2c_imx_dma_free(i2c_imx); -- 2.5.5 -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html