Skip to content

Commit 9d3ca54

Browse files
Juergen FitschenWolfram Sang
authored andcommitted
i2c: at91: added slave mode support
Slave mode driver is based on the concept of i2c-designware driver. Signed-off-by: Juergen Fitschen <[email protected]> [[email protected]: rework Kconfig and replace IS_ENABLED by defined] Signed-off-by: Ludovic Desroches <[email protected]> Signed-off-by: Wolfram Sang <[email protected]>
1 parent ad7d142 commit 9d3ca54

File tree

5 files changed

+198
-4
lines changed

5 files changed

+198
-4
lines changed

drivers/i2c/busses/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,19 @@ config I2C_AT91
387387
the latency to fill the transmission register is too long. If you
388388
are facing this situation, use the i2c-gpio driver.
389389

390+
config I2C_AT91_SLAVE_EXPERIMENTAL
391+
tristate "Microchip AT91 I2C experimental slave mode"
392+
depends on I2C_AT91
393+
select I2C_SLAVE
394+
help
395+
If you say yes to this option, support for the slave mode will be
396+
added. Caution: do not use it for production. This feature has not
397+
been tested in a heavy way, help wanted.
398+
There are known bugs:
399+
- It can hang, on a SAMA5D4, after several transfers.
400+
- There are some mismtaches with a SAMA5D4 as slave and a SAMA5D2 as
401+
master.
402+
390403
config I2C_AU1550
391404
tristate "Au1550/Au1200/Au1300 SMBus interface"
392405
depends on MIPS_ALCHEMY

drivers/i2c/busses/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o
3636
obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
3737
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
3838
i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o
39+
ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y)
40+
i2c-at91-objs += i2c-at91-slave.o
41+
endif
3942
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
4043
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
4144
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o

drivers/i2c/busses/i2c-at91-core.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ void at91_init_twi_bus(struct at91_twi_dev *dev)
5656
{
5757
at91_disable_twi_interrupts(dev);
5858
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
59-
60-
at91_init_twi_bus_master(dev);
59+
if (dev->slave_detected)
60+
at91_init_twi_bus_slave(dev);
61+
else
62+
at91_init_twi_bus_master(dev);
6163
}
6264

6365
static struct at91_twi_pdata at91rm9200_config = {
@@ -239,7 +241,12 @@ static int at91_twi_probe(struct platform_device *pdev)
239241
dev->adapter.timeout = AT91_I2C_TIMEOUT;
240242
dev->adapter.dev.of_node = pdev->dev.of_node;
241243

242-
rc = at91_twi_probe_master(pdev, phy_addr, dev);
244+
dev->slave_detected = i2c_detect_slave_mode(&pdev->dev);
245+
246+
if (dev->slave_detected)
247+
rc = at91_twi_probe_slave(pdev, phy_addr, dev);
248+
else
249+
rc = at91_twi_probe_master(pdev, phy_addr, dev);
243250
if (rc)
244251
return rc;
245252

drivers/i2c/busses/i2c-at91-slave.c

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* i2c slave support for Atmel's AT91 Two-Wire Interface (TWI)
4+
*
5+
* Copyright (C) 2017 Juergen Fitschen <[email protected]>
6+
*/
7+
8+
#include <linux/err.h>
9+
#include <linux/i2c.h>
10+
#include <linux/interrupt.h>
11+
#include <linux/pm_runtime.h>
12+
13+
#include "i2c-at91.h"
14+
15+
static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id)
16+
{
17+
struct at91_twi_dev *dev = dev_id;
18+
const unsigned status = at91_twi_read(dev, AT91_TWI_SR);
19+
const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR);
20+
u8 value;
21+
22+
if (!irqstatus)
23+
return IRQ_NONE;
24+
25+
/* slave address has been detected on I2C bus */
26+
if (irqstatus & AT91_TWI_SVACC) {
27+
if (status & AT91_TWI_SVREAD) {
28+
i2c_slave_event(dev->slave,
29+
I2C_SLAVE_READ_REQUESTED, &value);
30+
writeb_relaxed(value, dev->base + AT91_TWI_THR);
31+
at91_twi_write(dev, AT91_TWI_IER,
32+
AT91_TWI_TXRDY | AT91_TWI_EOSACC);
33+
} else {
34+
i2c_slave_event(dev->slave,
35+
I2C_SLAVE_WRITE_REQUESTED, &value);
36+
at91_twi_write(dev, AT91_TWI_IER,
37+
AT91_TWI_RXRDY | AT91_TWI_EOSACC);
38+
}
39+
at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC);
40+
}
41+
42+
/* byte transmitted to remote master */
43+
if (irqstatus & AT91_TWI_TXRDY) {
44+
i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &value);
45+
writeb_relaxed(value, dev->base + AT91_TWI_THR);
46+
}
47+
48+
/* byte received from remote master */
49+
if (irqstatus & AT91_TWI_RXRDY) {
50+
value = readb_relaxed(dev->base + AT91_TWI_RHR);
51+
i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
52+
}
53+
54+
/* master sent stop */
55+
if (irqstatus & AT91_TWI_EOSACC) {
56+
at91_twi_write(dev, AT91_TWI_IDR,
57+
AT91_TWI_TXRDY | AT91_TWI_RXRDY | AT91_TWI_EOSACC);
58+
at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
59+
i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &value);
60+
}
61+
62+
return IRQ_HANDLED;
63+
}
64+
65+
static int at91_reg_slave(struct i2c_client *slave)
66+
{
67+
struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
68+
69+
if (dev->slave)
70+
return -EBUSY;
71+
72+
if (slave->flags & I2C_CLIENT_TEN)
73+
return -EAFNOSUPPORT;
74+
75+
/* Make sure twi_clk doesn't get turned off! */
76+
pm_runtime_get_sync(dev->dev);
77+
78+
dev->slave = slave;
79+
dev->smr = AT91_TWI_SMR_SADR(slave->addr);
80+
81+
at91_init_twi_bus(dev);
82+
at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
83+
84+
dev_info(dev->dev, "entered slave mode (ADR=%d)\n", slave->addr);
85+
86+
return 0;
87+
}
88+
89+
static int at91_unreg_slave(struct i2c_client *slave)
90+
{
91+
struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
92+
93+
WARN_ON(!dev->slave);
94+
95+
dev_info(dev->dev, "leaving slave mode\n");
96+
97+
dev->slave = NULL;
98+
dev->smr = 0;
99+
100+
at91_init_twi_bus(dev);
101+
102+
pm_runtime_put(dev->dev);
103+
104+
return 0;
105+
}
106+
107+
static u32 at91_twi_func(struct i2c_adapter *adapter)
108+
{
109+
return I2C_FUNC_SLAVE | I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
110+
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
111+
}
112+
113+
static const struct i2c_algorithm at91_twi_algorithm_slave = {
114+
.reg_slave = at91_reg_slave,
115+
.unreg_slave = at91_unreg_slave,
116+
.functionality = at91_twi_func,
117+
};
118+
119+
int at91_twi_probe_slave(struct platform_device *pdev,
120+
u32 phy_addr, struct at91_twi_dev *dev)
121+
{
122+
int rc;
123+
124+
rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt_slave,
125+
0, dev_name(dev->dev), dev);
126+
if (rc) {
127+
dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
128+
return rc;
129+
}
130+
131+
dev->adapter.algo = &at91_twi_algorithm_slave;
132+
133+
return 0;
134+
}
135+
136+
void at91_init_twi_bus_slave(struct at91_twi_dev *dev)
137+
{
138+
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS);
139+
if (dev->slave_detected && dev->smr) {
140+
at91_twi_write(dev, AT91_TWI_SMR, dev->smr);
141+
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVEN);
142+
}
143+
}

drivers/i2c/busses/i2c-at91.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@
4949
#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */
5050
#define AT91_TWI_MREAD BIT(12) /* Master Read Direction */
5151

52+
#define AT91_TWI_SMR 0x0008 /* Slave Mode Register */
53+
#define AT91_TWI_SMR_SADR_MAX 0x007f
54+
#define AT91_TWI_SMR_SADR(x) (((x) & AT91_TWI_SMR_SADR_MAX) << 16)
55+
5256
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
5357

5458
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
@@ -59,13 +63,17 @@
5963
#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
6064
#define AT91_TWI_RXRDY BIT(1) /* Receive Holding Register Ready */
6165
#define AT91_TWI_TXRDY BIT(2) /* Transmit Holding Register Ready */
66+
#define AT91_TWI_SVREAD BIT(3) /* Slave Read */
67+
#define AT91_TWI_SVACC BIT(4) /* Slave Access */
6268
#define AT91_TWI_OVRE BIT(6) /* Overrun Error */
6369
#define AT91_TWI_UNRE BIT(7) /* Underrun Error */
6470
#define AT91_TWI_NACK BIT(8) /* Not Acknowledged */
71+
#define AT91_TWI_EOSACC BIT(11) /* End Of Slave Access */
6572
#define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */
6673

6774
#define AT91_TWI_INT_MASK \
68-
(AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK)
75+
(AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK \
76+
| AT91_TWI_SVACC | AT91_TWI_EOSACC)
6977

7078
#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */
7179
#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */
@@ -133,6 +141,11 @@ struct at91_twi_dev {
133141
bool recv_len_abort;
134142
u32 fifo_size;
135143
struct at91_twi_dma dma;
144+
bool slave_detected;
145+
#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
146+
unsigned smr;
147+
struct i2c_client *slave;
148+
#endif
136149
};
137150

138151
unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg);
@@ -145,3 +158,18 @@ void at91_init_twi_bus(struct at91_twi_dev *dev);
145158
void at91_init_twi_bus_master(struct at91_twi_dev *dev);
146159
int at91_twi_probe_master(struct platform_device *pdev, u32 phy_addr,
147160
struct at91_twi_dev *dev);
161+
162+
#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
163+
void at91_init_twi_bus_slave(struct at91_twi_dev *dev);
164+
int at91_twi_probe_slave(struct platform_device *pdev, u32 phy_addr,
165+
struct at91_twi_dev *dev);
166+
167+
#else
168+
static inline void at91_init_twi_bus_slave(struct at91_twi_dev *dev) {}
169+
static inline int at91_twi_probe_slave(struct platform_device *pdev,
170+
u32 phy_addr, struct at91_twi_dev *dev)
171+
{
172+
return -EINVAL;
173+
}
174+
175+
#endif

0 commit comments

Comments
 (0)