All of lore.kernel.org
 help / color / mirror / Atom feed
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

  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.