From: Andrey Danin <danindrey@mail.ru>
To: linux-i2c@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org,
ac100@lists.launchpad.net
Cc: Alexandre Courbot <gnurou@gmail.com>,
Stephen Warren <swarren@wwwdotorg.org>,
Wolfram Sang <wsa@the-dreams.de>, Marc Dietrich <marvin24@gmx.de>,
Thierry Reding <thierry.reding@gmail.com>,
Laxman Dewangan <ldewangan@nvidia.com>,
Andrey Danin <danindrey@mail.ru>
Subject: [PATCH 1/3] i2c: tegra: implement slave mode
Date: Thu, 29 Jan 2015 10:20:20 +0300 [thread overview]
Message-ID: <1422516022-27161-2-git-send-email-danindrey@mail.ru> (raw)
In-Reply-To: <1422516022-27161-1-git-send-email-danindrey@mail.ru>
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@mail.ru>
---
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;
+ }
+
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;
+}
+
+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;
+}
+
+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 */
--
1.9.1
WARNING: multiple messages have this Message-ID (diff)
From: Andrey Danin <danindrey@mail.ru>
To: linux-i2c@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org,
ac100@lists.launchpad.net
Cc: Andrey Danin <danindrey@mail.ru>,
Laxman Dewangan <ldewangan@nvidia.com>,
Wolfram Sang <wsa@the-dreams.de>,
Stephen Warren <swarren@wwwdotorg.org>,
Thierry Reding <thierry.reding@gmail.com>,
Alexandre Courbot <gnurou@gmail.com>,
Marc Dietrich <marvin24@gmx.de>
Subject: [PATCH 1/3] i2c: tegra: implement slave mode
Date: Thu, 29 Jan 2015 10:20:20 +0300 [thread overview]
Message-ID: <1422516022-27161-2-git-send-email-danindrey@mail.ru> (raw)
In-Reply-To: <1422516022-27161-1-git-send-email-danindrey@mail.ru>
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@mail.ru>
---
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;
+ }
+
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;
+}
+
+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;
+}
+
+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 */
--
1.9.1
WARNING: multiple messages have this Message-ID (diff)
From: danindrey@mail.ru (Andrey Danin)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 1/3] i2c: tegra: implement slave mode
Date: Thu, 29 Jan 2015 10:20:20 +0300 [thread overview]
Message-ID: <1422516022-27161-2-git-send-email-danindrey@mail.ru> (raw)
In-Reply-To: <1422516022-27161-1-git-send-email-danindrey@mail.ru>
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@mail.ru>
---
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;
+ }
+
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;
+}
+
+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;
+}
+
+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 */
--
1.9.1
next prev parent reply other threads:[~2015-01-29 7:20 UTC|newest]
Thread overview: 45+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-01-29 7:20 [PATCH 0/3] arm: tegra: implement NVEC driver using tegra i2c Andrey Danin
2015-01-29 7:20 ` Andrey Danin
2015-01-29 7:20 ` Andrey Danin
2015-01-29 7:20 ` Andrey Danin [this message]
2015-01-29 7:20 ` [PATCH 1/3] i2c: tegra: implement slave mode Andrey Danin
2015-01-29 7:20 ` Andrey Danin
2015-01-29 9:40 ` Marc Dietrich
2015-01-29 9:40 ` Marc Dietrich
2015-01-29 11:41 ` Wolfram Sang
2015-01-29 11:41 ` Wolfram Sang
2015-03-31 6:25 ` Andrey Danin
2015-03-31 6:25 ` Andrey Danin
2015-03-31 6:25 ` Andrey Danin
2015-01-29 7:20 ` [PATCH 2/3] staging/nvec: reimplement on top of tegra i2c driver Andrey Danin
2015-01-29 7:20 ` Andrey Danin
2015-01-29 9:53 ` Marc Dietrich
2015-01-29 9:53 ` Marc Dietrich
2015-01-29 7:20 ` [PATCH 3/3] dt: paz00: define nvec as child of i2c bus Andrey Danin
2015-01-29 7:20 ` Andrey Danin
2015-01-29 7:20 ` Andrey Danin
2015-01-29 10:01 ` Marc Dietrich
2015-01-29 10:01 ` Marc Dietrich
[not found] ` <1422516022-27161-4-git-send-email-danindrey-JGs/UdohzUI@public.gmane.org>
2015-02-02 21:20 ` Stephen Warren
2015-02-02 21:20 ` Stephen Warren
2015-02-02 21:20 ` Stephen Warren
[not found] ` <54CFEA2F.8040701-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-03-31 6:40 ` Andrey Danin
2015-03-31 6:40 ` Andrey Danin
2015-03-31 6:40 ` Andrey Danin
2015-03-31 14:09 ` Stephen Warren
2015-03-31 14:09 ` Stephen Warren
[not found] ` <551AAA9B.6070607-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-03-31 15:46 ` Andrey Danin
2015-03-31 15:46 ` Andrey Danin
2015-03-31 15:46 ` Andrey Danin
[not found] ` <551AC153.7060103-JGs/UdohzUI@public.gmane.org>
2015-03-31 16:04 ` Andrey Danin
2015-03-31 16:04 ` Andrey Danin
2015-03-31 16:04 ` Andrey Danin
2015-04-01 17:28 ` Stephen Warren
2015-04-01 17:28 ` Stephen Warren
2015-04-01 17:28 ` Stephen Warren
[not found] ` <551C2AC0.9030304-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2015-04-02 9:37 ` Marc Dietrich
2015-04-02 9:37 ` Marc Dietrich
2015-04-02 9:37 ` Marc Dietrich
2015-04-02 14:50 ` Stephen Warren
2015-04-02 14:50 ` Stephen Warren
2015-04-02 14:50 ` Stephen Warren
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1422516022-27161-2-git-send-email-danindrey@mail.ru \
--to=danindrey@mail.ru \
--cc=ac100@lists.launchpad.net \
--cc=gnurou@gmail.com \
--cc=ldewangan@nvidia.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=marvin24@gmx.de \
--cc=swarren@wwwdotorg.org \
--cc=thierry.reding@gmail.com \
--cc=wsa@the-dreams.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.