Hi Andrey, first, thanks for accepting the challenge once more ;-) The driver depends on I2C_SLAVE now, so you need to add this to Kconfig. The amount of code (and additional overhead) is pretty small, so I think it's ok to always enable it. Otherwise we would need lots of ifdefs. Am Donnerstag, 29. Januar 2015, 10:20:20 schrieb Andrey Danin: > Initialization code is based on NVEC driver. > > There is a HW bug in AP20 that was also mentioned in kernel sources > for Toshiba AC100. > > Signed-off-by: Andrey Danin <danindrey@xxxxxxx> > --- > drivers/i2c/busses/i2c-tegra.c | 131 > +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) > > diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c > index 28b87e6..cfc4e67 100644 > --- a/drivers/i2c/busses/i2c-tegra.c > +++ b/drivers/i2c/busses/i2c-tegra.c > @@ -42,8 +42,15 @@ > #define I2C_SL_CNFG 0x020 > #define I2C_SL_CNFG_NACK (1<<1) > #define I2C_SL_CNFG_NEWSL (1<<2) > +#define I2C_SL_RCVD 0x024 > +#define I2C_SL_STATUS 0x028 > +#define I2C_SL_ST_IRQ (1<<3) > +#define I2C_SL_ST_END_TRANS (1<<4) > +#define I2C_SL_ST_RCVD (1<<2) > +#define I2C_SL_ST_RNW (1<<1) > #define I2C_SL_ADDR1 0x02c > #define I2C_SL_ADDR2 0x030 > +#define I2C_SL_DELAY_COUNT 0x03c > #define I2C_TX_FIFO 0x050 > #define I2C_RX_FIFO 0x054 > #define I2C_PACKET_TRANSFER_STATUS 0x058 > @@ -125,6 +132,8 @@ enum msg_end_type { > * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is > * applicable if there is no fast clock source i.e. single clock > * source. > + * @slave_read_start_delay: Workaround for AP20 I2C Slave Controller bug. > Delay + * before writing data byte into register I2C_SL_RCVD. > */ > > struct tegra_i2c_hw_feature { > @@ -133,6 +142,7 @@ struct tegra_i2c_hw_feature { > bool has_single_clk_source; > int clk_divisor_hs_mode; > int clk_divisor_std_fast_mode; > + int slave_read_start_delay; > }; > > /** > @@ -173,6 +183,7 @@ struct tegra_i2c_dev { > int msg_read; > u32 bus_clk_rate; > bool is_suspended; > + struct i2c_client *slave; > }; > > static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned > long reg) @@ -398,6 +409,12 @@ static inline int > tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev) > > static inline void tegra_i2c_clock_disable(struct tegra_i2c_dev *i2c_dev) > { > + if (i2c_dev->slave) { > + dev_warn(i2c_dev->dev, > + "i2c slave is registered, don't disable a clock\n"); > + return; > + } > + is this really required? What are the callers of clock_disable? I think it would be ok to make master or slave operation exclusive for now. We have no way to test this anyway. Maybe some flag which blocks master operation. > clk_disable(i2c_dev->div_clk); > if (!i2c_dev->hw->has_single_clk_source) > clk_disable(i2c_dev->fast_clk); > @@ -459,12 +476,84 @@ static int tegra_i2c_init(struct tegra_i2c_dev > *i2c_dev) return err; > } > > +static inline int is_ready(unsigned long status) > +{ > + return status & I2C_SL_ST_IRQ; > +} is_slave_irq? > + > +static inline int is_write(unsigned long status) > +{ > + return (status & I2C_SL_ST_RNW) == 0; > +} > + > +static inline int is_read(unsigned long status) > +{ > + return !is_write(status); > +} > + > +static inline int is_trans_start(unsigned long status) > +{ > + return status & I2C_SL_ST_RCVD; > +} > + > +static inline int is_trans_end(unsigned long status) > +{ > + return status & I2C_SL_ST_END_TRANS; > +} Following the rest of the files coding style, I think this helpers can be moved to the caller itself. > + > +static bool tegra_i2c_slave_isr(int irq, struct tegra_i2c_dev *i2c_dev) > +{ > + unsigned long status; > + u8 value; > + > + if (!i2c_dev->slave || !i2c_dev->slave->slave_cb) > + return false; > + > + status = i2c_readl(i2c_dev, I2C_SL_STATUS); > + if (!is_ready(status)) > + return false; > + > + /* master sent stop */ > + if (is_trans_end(status)) { > + i2c_slave_event(i2c_dev->slave, I2C_SLAVE_STOP, NULL); > + if (!is_trans_start(status)) > + return true; > + } > + > + /* i2c master sends data to us */ > + if (is_write(status)) { > + i2c_slave_event(i2c_dev->slave, I2C_SLAVE_REQ_WRITE_START, > + NULL); > + value = i2c_readl(i2c_dev, I2C_SL_RCVD); > + if (is_trans_start(status)) > + i2c_writel(i2c_dev, 0, I2C_SL_RCVD); > + i2c_slave_event(i2c_dev->slave, I2C_SLAVE_REQ_WRITE_END, > + &value); > + } > + > + /* i2c master reads data from us */ > + if (is_read(status)) { > + i2c_slave_event(i2c_dev->slave, I2C_SLAVE_REQ_READ_START, > + &value); > + if (is_trans_start(status) > + && i2c_dev->hw->slave_read_start_delay) > + udelay(i2c_dev->hw->slave_read_start_delay); > + i2c_writel(i2c_dev, value, I2C_SL_RCVD); > + i2c_slave_event(i2c_dev->slave, I2C_SLAVE_REQ_READ_END, NULL); > + } > + > + return true; > +} > + > static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) > { > u32 status; > const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; > struct tegra_i2c_dev *i2c_dev = dev_id; > > + if (tegra_i2c_slave_isr(irq, i2c_dev)) > + return IRQ_HANDLED; > + > status = i2c_readl(i2c_dev, I2C_INT_STATUS); > > if (status == 0) { > @@ -660,9 +749,48 @@ static u32 tegra_i2c_func(struct i2c_adapter *adap) > return ret; > } > > +static int tegra_reg_slave(struct i2c_client *slave) > +{ > + struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter); > + > + if (i2c_dev->slave) > + return -EBUSY; > + > + i2c_dev->slave = slave; > + > + tegra_i2c_clock_enable(i2c_dev); > + > + reset_control_assert(i2c_dev->rst); > + udelay(2); > + reset_control_deassert(i2c_dev->rst); > + > + i2c_writel(i2c_dev, I2C_SL_CNFG_NEWSL, I2C_SL_CNFG); > + i2c_writel(i2c_dev, 0x1E, I2C_SL_DELAY_COUNT); > + > + i2c_writel(i2c_dev, slave->addr, I2C_SL_ADDR1); > + i2c_writel(i2c_dev, 0, I2C_SL_ADDR2); > + > + return 0; > +} > + > +static int tegra_unreg_slave(struct i2c_client *slave) > +{ > + struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter); > + > + WARN_ON(!i2c_dev->slave); > + > + i2c_writel(i2c_dev, 0, I2C_SL_CNFG); > + > + i2c_dev->slave = NULL; > + > + return 0; > +} > + > static const struct i2c_algorithm tegra_i2c_algo = { > .master_xfer = tegra_i2c_xfer, > .functionality = tegra_i2c_func, > + .reg_slave = tegra_reg_slave, > + .unreg_slave = tegra_unreg_slave, > }; > > static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { > @@ -671,6 +799,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw > = { .has_single_clk_source = false, > .clk_divisor_hs_mode = 3, > .clk_divisor_std_fast_mode = 0, > + .slave_read_start_delay = 8, > }; > > static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { > @@ -679,6 +808,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw > = { .has_single_clk_source = false, > .clk_divisor_hs_mode = 3, > .clk_divisor_std_fast_mode = 0, > + .slave_read_start_delay = 0, > }; > > static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { > @@ -687,6 +817,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw > = { .has_single_clk_source = true, > .clk_divisor_hs_mode = 1, > .clk_divisor_std_fast_mode = 0x19, > + .slave_read_start_delay = 0, > }; > > /* Match table for of_platform binding */
Attachment:
signature.asc
Description: This is a digitally signed message part.