Re: [PATCH RFC 3/4] i2c: at91: added slave mode support
On Wed, Nov 01, 2017 at 02:04:18PM +0100, Juergen Fitschen wrote: > Helle Ludovic, > > while going through this patch a question related to the Atmel / Microchip HW > came into mind: > > On Fri, Oct 27, 2017 at 05:12:00PM +0200, Juergen Fitschen wrote: > > diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h > > (...) > > #defineAT91_TWI_INT_MASK \ > > - (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK) > > + (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK \ > > + | AT91_TWI_SVACC | AT91_TWI_EOSACC) > > The AT91_TWI_INT_MASK is used to disable all interrputs in the > at91_disable_twi_interrupts function by writing the mask to the interrupt > disable register (IDR). I wonder what happens on MPUs that don't have > AT91_TWI_SVACC and AT91_TWI_EOSACC implemented, like the AT91RM9200? Do you > think we should revise this and write specific masks depending on the current > moude the I2C HW is in? > > Something like this: > > void at91_disable_twi_interrupts(struct at91_twi_dev *dev) > { > if (dev->slave_detected) > at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK_SLAVE); > else > at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK_MASTER); > } > I don't think it's necessary. Usually, writing a bit which is unused don't cause weird behaviors. Regards Ludovic
Re: [PATCH RFC 3/4] i2c: at91: added slave mode support
On Wed, Nov 01, 2017 at 02:04:18PM +0100, Juergen Fitschen wrote: > Helle Ludovic, > > while going through this patch a question related to the Atmel / Microchip HW > came into mind: > > On Fri, Oct 27, 2017 at 05:12:00PM +0200, Juergen Fitschen wrote: > > diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h > > (...) > > #defineAT91_TWI_INT_MASK \ > > - (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK) > > + (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK \ > > + | AT91_TWI_SVACC | AT91_TWI_EOSACC) > > The AT91_TWI_INT_MASK is used to disable all interrputs in the > at91_disable_twi_interrupts function by writing the mask to the interrupt > disable register (IDR). I wonder what happens on MPUs that don't have > AT91_TWI_SVACC and AT91_TWI_EOSACC implemented, like the AT91RM9200? Do you > think we should revise this and write specific masks depending on the current > moude the I2C HW is in? > > Something like this: > > void at91_disable_twi_interrupts(struct at91_twi_dev *dev) > { > if (dev->slave_detected) > at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK_SLAVE); > else > at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK_MASTER); > } > I don't think it's necessary. Usually, writing a bit which is unused don't cause weird behaviors. Regards Ludovic
Re: [PATCH RFC 3/4] i2c: at91: added slave mode support
Helle Ludovic, while going through this patch a question related to the Atmel / Microchip HW came into mind: On Fri, Oct 27, 2017 at 05:12:00PM +0200, Juergen Fitschen wrote: > diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h > (...) > #define AT91_TWI_INT_MASK \ > - (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK) > + (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK \ > + | AT91_TWI_SVACC | AT91_TWI_EOSACC) The AT91_TWI_INT_MASK is used to disable all interrputs in the at91_disable_twi_interrupts function by writing the mask to the interrupt disable register (IDR). I wonder what happens on MPUs that don't have AT91_TWI_SVACC and AT91_TWI_EOSACC implemented, like the AT91RM9200? Do you think we should revise this and write specific masks depending on the current moude the I2C HW is in? Something like this: void at91_disable_twi_interrupts(struct at91_twi_dev *dev) { if (dev->slave_detected) at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK_SLAVE); else at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK_MASTER); } Best regards Juergen
Re: [PATCH RFC 3/4] i2c: at91: added slave mode support
Helle Ludovic, while going through this patch a question related to the Atmel / Microchip HW came into mind: On Fri, Oct 27, 2017 at 05:12:00PM +0200, Juergen Fitschen wrote: > diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h > (...) > #define AT91_TWI_INT_MASK \ > - (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK) > + (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK \ > + | AT91_TWI_SVACC | AT91_TWI_EOSACC) The AT91_TWI_INT_MASK is used to disable all interrputs in the at91_disable_twi_interrupts function by writing the mask to the interrupt disable register (IDR). I wonder what happens on MPUs that don't have AT91_TWI_SVACC and AT91_TWI_EOSACC implemented, like the AT91RM9200? Do you think we should revise this and write specific masks depending on the current moude the I2C HW is in? Something like this: void at91_disable_twi_interrupts(struct at91_twi_dev *dev) { if (dev->slave_detected) at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK_SLAVE); else at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK_MASTER); } Best regards Juergen
Re: [PATCH RFC 3/4] i2c: at91: added slave mode support
Hello Ludovic, On Tue, Oct 31, 2017 at 04:55:42PM +0100, Ludovic Desroches wrote: > On Fri, Oct 27, 2017 at 05:12:00PM +0200, Juergen Fitschen wrote: > > Slave mode driver is based on the concept of i2c-designware driver. > > > > Signed-off-by: Juergen Fitschen> > --- > > drivers/i2c/busses/Makefile | 3 + > > drivers/i2c/busses/i2c-at91-core.c | 13 +++- > > drivers/i2c/busses/i2c-at91-slave.c | 147 > > > > drivers/i2c/busses/i2c-at91.h | 30 +++- > > 4 files changed, 189 insertions(+), 4 deletions(-) > > create mode 100644 drivers/i2c/busses/i2c-at91-slave.c > > > > Adding an example in Documentation/devicetree/bindings/i2c/i2c-at91.txt > could be useful. I will add this. > > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile > > index 2a79c3d..b38fb8e9 100644 > > --- a/drivers/i2c/busses/Makefile > > +++ b/drivers/i2c/busses/Makefile > > @@ -34,6 +34,9 @@ obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o > > obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o > > obj-$(CONFIG_I2C_AT91) += i2c-at91.o > > i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o > > +ifeq ($(CONFIG_I2C_SLAVE),y) > > + i2c-at91-objs += i2c-at91-slave.o > > +endif > > As Designware driver, I would add an entry in 'I2C Hardware Bus > Support'. > > If a user wants to use the I2C GPIO driver as the I2C slave (once it has > the slave support), it's not useful to embed i2c-at91-slave code. Good point. > > +void at91_init_twi_bus_slave(struct at91_twi_dev *dev) > > +{ > > + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS); > > + if (dev->slave_detected && dev->smr) { > > slave_detected has been checked in the caller. smr has been set just > before calling at91_init_twi_bus(). Whoops! Better safe than sorry ;) I will remove that check. Thank you for testing everything! I am curious about how the other MPUs will perform with the patchset. I have a SAMA5D35 at hand and can test everything on this MPU in the next days. Best regards Juergen
Re: [PATCH RFC 3/4] i2c: at91: added slave mode support
Hello Ludovic, On Tue, Oct 31, 2017 at 04:55:42PM +0100, Ludovic Desroches wrote: > On Fri, Oct 27, 2017 at 05:12:00PM +0200, Juergen Fitschen wrote: > > Slave mode driver is based on the concept of i2c-designware driver. > > > > Signed-off-by: Juergen Fitschen > > --- > > drivers/i2c/busses/Makefile | 3 + > > drivers/i2c/busses/i2c-at91-core.c | 13 +++- > > drivers/i2c/busses/i2c-at91-slave.c | 147 > > > > drivers/i2c/busses/i2c-at91.h | 30 +++- > > 4 files changed, 189 insertions(+), 4 deletions(-) > > create mode 100644 drivers/i2c/busses/i2c-at91-slave.c > > > > Adding an example in Documentation/devicetree/bindings/i2c/i2c-at91.txt > could be useful. I will add this. > > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile > > index 2a79c3d..b38fb8e9 100644 > > --- a/drivers/i2c/busses/Makefile > > +++ b/drivers/i2c/busses/Makefile > > @@ -34,6 +34,9 @@ obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o > > obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o > > obj-$(CONFIG_I2C_AT91) += i2c-at91.o > > i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o > > +ifeq ($(CONFIG_I2C_SLAVE),y) > > + i2c-at91-objs += i2c-at91-slave.o > > +endif > > As Designware driver, I would add an entry in 'I2C Hardware Bus > Support'. > > If a user wants to use the I2C GPIO driver as the I2C slave (once it has > the slave support), it's not useful to embed i2c-at91-slave code. Good point. > > +void at91_init_twi_bus_slave(struct at91_twi_dev *dev) > > +{ > > + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS); > > + if (dev->slave_detected && dev->smr) { > > slave_detected has been checked in the caller. smr has been set just > before calling at91_init_twi_bus(). Whoops! Better safe than sorry ;) I will remove that check. Thank you for testing everything! I am curious about how the other MPUs will perform with the patchset. I have a SAMA5D35 at hand and can test everything on this MPU in the next days. Best regards Juergen
Re: [PATCH RFC 3/4] i2c: at91: added slave mode support
On Fri, Oct 27, 2017 at 05:12:00PM +0200, Juergen Fitschen wrote: > Slave mode driver is based on the concept of i2c-designware driver. > > Signed-off-by: Juergen Fitschen> --- > drivers/i2c/busses/Makefile | 3 + > drivers/i2c/busses/i2c-at91-core.c | 13 +++- > drivers/i2c/busses/i2c-at91-slave.c | 147 > > drivers/i2c/busses/i2c-at91.h | 30 +++- > 4 files changed, 189 insertions(+), 4 deletions(-) > create mode 100644 drivers/i2c/busses/i2c-at91-slave.c > Adding an example in Documentation/devicetree/bindings/i2c/i2c-at91.txt could be useful. > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile > index 2a79c3d..b38fb8e9 100644 > --- a/drivers/i2c/busses/Makefile > +++ b/drivers/i2c/busses/Makefile > @@ -34,6 +34,9 @@ obj-$(CONFIG_I2C_ALTERA)+= i2c-altera.o > obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o > obj-$(CONFIG_I2C_AT91) += i2c-at91.o > i2c-at91-objs:= i2c-at91-core.o i2c-at91-master.o > +ifeq ($(CONFIG_I2C_SLAVE),y) > + i2c-at91-objs += i2c-at91-slave.o > +endif As Designware driver, I would add an entry in 'I2C Hardware Bus Support'. If a user wants to use the I2C GPIO driver as the I2C slave (once it has the slave support), it's not useful to embed i2c-at91-slave code. > obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o > obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o > obj-$(CONFIG_I2C_BCM2835)+= i2c-bcm2835.o > diff --git a/drivers/i2c/busses/i2c-at91-core.c > b/drivers/i2c/busses/i2c-at91-core.c > index 4fed72d..3d7287c 100644 > --- a/drivers/i2c/busses/i2c-at91-core.c > +++ b/drivers/i2c/busses/i2c-at91-core.c > @@ -60,8 +60,10 @@ void at91_init_twi_bus(struct at91_twi_dev *dev) > { > at91_disable_twi_interrupts(dev); > at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST); > - > - at91_init_twi_bus_master(dev); > + if (dev->slave_detected) > + at91_init_twi_bus_slave(dev); > + else > + at91_init_twi_bus_master(dev); > } > > static struct at91_twi_pdata at91rm9200_config = { > @@ -243,7 +245,12 @@ static int at91_twi_probe(struct platform_device *pdev) > dev->adapter.timeout = AT91_I2C_TIMEOUT; > dev->adapter.dev.of_node = pdev->dev.of_node; > > - rc = at91_twi_probe_master(pdev, phy_addr, dev); > + dev->slave_detected = i2c_detect_slave_mode(>dev); > + > + if (dev->slave_detected) > + rc = at91_twi_probe_slave(pdev, phy_addr, dev); > + else > + rc = at91_twi_probe_master(pdev, phy_addr, dev); > if (rc) > return rc; > > diff --git a/drivers/i2c/busses/i2c-at91-slave.c > b/drivers/i2c/busses/i2c-at91-slave.c I need to have a look to the slave framework and to test this patch before giving you more feedback. Some minor comments below. > new file mode 100644 > index 000..4b4808e > --- /dev/null > +++ b/drivers/i2c/busses/i2c-at91-slave.c > @@ -0,0 +1,147 @@ > +/* > + * i2c slave support for Atmel's AT91 Two-Wire Interface (TWI) > + * > + * Copyright (C) 2017 Juergen Fitschen > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + */ > + > +#include > +#include > +#include > +#include > + > +#include "i2c-at91.h" > + > +static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id) > +{ > + struct at91_twi_dev *dev = dev_id; > + const unsigned status = at91_twi_read(dev, AT91_TWI_SR); > + const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR); > + u8 value; > + > + if (!irqstatus) > + return IRQ_NONE; > + > + /* slave address has been detected on I2C bus */ > + if (irqstatus & AT91_TWI_SVACC) { > + if (status & AT91_TWI_SVREAD) { > + i2c_slave_event(dev->slave, > + I2C_SLAVE_READ_REQUESTED, ); > + writeb_relaxed(value, dev->base + AT91_TWI_THR); > + at91_twi_write(dev, AT91_TWI_IER, > +AT91_TWI_TXRDY | AT91_TWI_EOSACC); > + } else { > + i2c_slave_event(dev->slave, > + I2C_SLAVE_WRITE_REQUESTED, ); > + at91_twi_write(dev, AT91_TWI_IER, > +AT91_TWI_RXRDY | AT91_TWI_EOSACC); > + } > + at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC); > + } > + > + /* byte transmitted to remote master */ > + if (irqstatus & AT91_TWI_TXRDY) { > + i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, ); > + writeb_relaxed(value, dev->base + AT91_TWI_THR); > + } > + > + /* byte received from
Re: [PATCH RFC 3/4] i2c: at91: added slave mode support
On Fri, Oct 27, 2017 at 05:12:00PM +0200, Juergen Fitschen wrote: > Slave mode driver is based on the concept of i2c-designware driver. > > Signed-off-by: Juergen Fitschen > --- > drivers/i2c/busses/Makefile | 3 + > drivers/i2c/busses/i2c-at91-core.c | 13 +++- > drivers/i2c/busses/i2c-at91-slave.c | 147 > > drivers/i2c/busses/i2c-at91.h | 30 +++- > 4 files changed, 189 insertions(+), 4 deletions(-) > create mode 100644 drivers/i2c/busses/i2c-at91-slave.c > Adding an example in Documentation/devicetree/bindings/i2c/i2c-at91.txt could be useful. > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile > index 2a79c3d..b38fb8e9 100644 > --- a/drivers/i2c/busses/Makefile > +++ b/drivers/i2c/busses/Makefile > @@ -34,6 +34,9 @@ obj-$(CONFIG_I2C_ALTERA)+= i2c-altera.o > obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o > obj-$(CONFIG_I2C_AT91) += i2c-at91.o > i2c-at91-objs:= i2c-at91-core.o i2c-at91-master.o > +ifeq ($(CONFIG_I2C_SLAVE),y) > + i2c-at91-objs += i2c-at91-slave.o > +endif As Designware driver, I would add an entry in 'I2C Hardware Bus Support'. If a user wants to use the I2C GPIO driver as the I2C slave (once it has the slave support), it's not useful to embed i2c-at91-slave code. > obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o > obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o > obj-$(CONFIG_I2C_BCM2835)+= i2c-bcm2835.o > diff --git a/drivers/i2c/busses/i2c-at91-core.c > b/drivers/i2c/busses/i2c-at91-core.c > index 4fed72d..3d7287c 100644 > --- a/drivers/i2c/busses/i2c-at91-core.c > +++ b/drivers/i2c/busses/i2c-at91-core.c > @@ -60,8 +60,10 @@ void at91_init_twi_bus(struct at91_twi_dev *dev) > { > at91_disable_twi_interrupts(dev); > at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST); > - > - at91_init_twi_bus_master(dev); > + if (dev->slave_detected) > + at91_init_twi_bus_slave(dev); > + else > + at91_init_twi_bus_master(dev); > } > > static struct at91_twi_pdata at91rm9200_config = { > @@ -243,7 +245,12 @@ static int at91_twi_probe(struct platform_device *pdev) > dev->adapter.timeout = AT91_I2C_TIMEOUT; > dev->adapter.dev.of_node = pdev->dev.of_node; > > - rc = at91_twi_probe_master(pdev, phy_addr, dev); > + dev->slave_detected = i2c_detect_slave_mode(>dev); > + > + if (dev->slave_detected) > + rc = at91_twi_probe_slave(pdev, phy_addr, dev); > + else > + rc = at91_twi_probe_master(pdev, phy_addr, dev); > if (rc) > return rc; > > diff --git a/drivers/i2c/busses/i2c-at91-slave.c > b/drivers/i2c/busses/i2c-at91-slave.c I need to have a look to the slave framework and to test this patch before giving you more feedback. Some minor comments below. > new file mode 100644 > index 000..4b4808e > --- /dev/null > +++ b/drivers/i2c/busses/i2c-at91-slave.c > @@ -0,0 +1,147 @@ > +/* > + * i2c slave support for Atmel's AT91 Two-Wire Interface (TWI) > + * > + * Copyright (C) 2017 Juergen Fitschen > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + */ > + > +#include > +#include > +#include > +#include > + > +#include "i2c-at91.h" > + > +static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id) > +{ > + struct at91_twi_dev *dev = dev_id; > + const unsigned status = at91_twi_read(dev, AT91_TWI_SR); > + const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR); > + u8 value; > + > + if (!irqstatus) > + return IRQ_NONE; > + > + /* slave address has been detected on I2C bus */ > + if (irqstatus & AT91_TWI_SVACC) { > + if (status & AT91_TWI_SVREAD) { > + i2c_slave_event(dev->slave, > + I2C_SLAVE_READ_REQUESTED, ); > + writeb_relaxed(value, dev->base + AT91_TWI_THR); > + at91_twi_write(dev, AT91_TWI_IER, > +AT91_TWI_TXRDY | AT91_TWI_EOSACC); > + } else { > + i2c_slave_event(dev->slave, > + I2C_SLAVE_WRITE_REQUESTED, ); > + at91_twi_write(dev, AT91_TWI_IER, > +AT91_TWI_RXRDY | AT91_TWI_EOSACC); > + } > + at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC); > + } > + > + /* byte transmitted to remote master */ > + if (irqstatus & AT91_TWI_TXRDY) { > + i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, ); > + writeb_relaxed(value, dev->base + AT91_TWI_THR); > + } > + > + /* byte received from remote master */ > +
[PATCH RFC 3/4] i2c: at91: added slave mode support
Slave mode driver is based on the concept of i2c-designware driver. Signed-off-by: Juergen Fitschen--- drivers/i2c/busses/Makefile | 3 + drivers/i2c/busses/i2c-at91-core.c | 13 +++- drivers/i2c/busses/i2c-at91-slave.c | 147 drivers/i2c/busses/i2c-at91.h | 30 +++- 4 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 drivers/i2c/busses/i2c-at91-slave.c diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 2a79c3d..b38fb8e9 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -34,6 +34,9 @@ obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o +ifeq ($(CONFIG_I2C_SLAVE),y) + i2c-at91-objs += i2c-at91-slave.o +endif obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA)+= i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o diff --git a/drivers/i2c/busses/i2c-at91-core.c b/drivers/i2c/busses/i2c-at91-core.c index 4fed72d..3d7287c 100644 --- a/drivers/i2c/busses/i2c-at91-core.c +++ b/drivers/i2c/busses/i2c-at91-core.c @@ -60,8 +60,10 @@ void at91_init_twi_bus(struct at91_twi_dev *dev) { at91_disable_twi_interrupts(dev); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST); - - at91_init_twi_bus_master(dev); + if (dev->slave_detected) + at91_init_twi_bus_slave(dev); + else + at91_init_twi_bus_master(dev); } static struct at91_twi_pdata at91rm9200_config = { @@ -243,7 +245,12 @@ static int at91_twi_probe(struct platform_device *pdev) dev->adapter.timeout = AT91_I2C_TIMEOUT; dev->adapter.dev.of_node = pdev->dev.of_node; - rc = at91_twi_probe_master(pdev, phy_addr, dev); + dev->slave_detected = i2c_detect_slave_mode(>dev); + + if (dev->slave_detected) + rc = at91_twi_probe_slave(pdev, phy_addr, dev); + else + rc = at91_twi_probe_master(pdev, phy_addr, dev); if (rc) return rc; diff --git a/drivers/i2c/busses/i2c-at91-slave.c b/drivers/i2c/busses/i2c-at91-slave.c new file mode 100644 index 000..4b4808e --- /dev/null +++ b/drivers/i2c/busses/i2c-at91-slave.c @@ -0,0 +1,147 @@ +/* + * i2c slave support for Atmel's AT91 Two-Wire Interface (TWI) + * + * Copyright (C) 2017 Juergen Fitschen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include "i2c-at91.h" + +static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id) +{ + struct at91_twi_dev *dev = dev_id; + const unsigned status = at91_twi_read(dev, AT91_TWI_SR); + const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR); + u8 value; + + if (!irqstatus) + return IRQ_NONE; + + /* slave address has been detected on I2C bus */ + if (irqstatus & AT91_TWI_SVACC) { + if (status & AT91_TWI_SVREAD) { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_REQUESTED, ); + writeb_relaxed(value, dev->base + AT91_TWI_THR); + at91_twi_write(dev, AT91_TWI_IER, + AT91_TWI_TXRDY | AT91_TWI_EOSACC); + } else { + i2c_slave_event(dev->slave, + I2C_SLAVE_WRITE_REQUESTED, ); + at91_twi_write(dev, AT91_TWI_IER, + AT91_TWI_RXRDY | AT91_TWI_EOSACC); + } + at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC); + } + + /* byte transmitted to remote master */ + if (irqstatus & AT91_TWI_TXRDY) { + i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, ); + writeb_relaxed(value, dev->base + AT91_TWI_THR); + } + + /* byte received from remote master */ + if (irqstatus & AT91_TWI_RXRDY) { + value = readb_relaxed(dev->base + AT91_TWI_RHR); + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, ); + } + + /* master sent stop */ + if (irqstatus & AT91_TWI_EOSACC) { + at91_twi_write(dev, AT91_TWI_IDR, + AT91_TWI_TXRDY | AT91_TWI_RXRDY | AT91_TWI_EOSACC); + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC); + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, ); + } + + return IRQ_HANDLED; +} + +static int at91_reg_slave(struct i2c_client *slave) +{ +
[PATCH RFC 3/4] i2c: at91: added slave mode support
Slave mode driver is based on the concept of i2c-designware driver. Signed-off-by: Juergen Fitschen --- drivers/i2c/busses/Makefile | 3 + drivers/i2c/busses/i2c-at91-core.c | 13 +++- drivers/i2c/busses/i2c-at91-slave.c | 147 drivers/i2c/busses/i2c-at91.h | 30 +++- 4 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 drivers/i2c/busses/i2c-at91-slave.c diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 2a79c3d..b38fb8e9 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -34,6 +34,9 @@ obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o +ifeq ($(CONFIG_I2C_SLAVE),y) + i2c-at91-objs += i2c-at91-slave.o +endif obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA)+= i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o diff --git a/drivers/i2c/busses/i2c-at91-core.c b/drivers/i2c/busses/i2c-at91-core.c index 4fed72d..3d7287c 100644 --- a/drivers/i2c/busses/i2c-at91-core.c +++ b/drivers/i2c/busses/i2c-at91-core.c @@ -60,8 +60,10 @@ void at91_init_twi_bus(struct at91_twi_dev *dev) { at91_disable_twi_interrupts(dev); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST); - - at91_init_twi_bus_master(dev); + if (dev->slave_detected) + at91_init_twi_bus_slave(dev); + else + at91_init_twi_bus_master(dev); } static struct at91_twi_pdata at91rm9200_config = { @@ -243,7 +245,12 @@ static int at91_twi_probe(struct platform_device *pdev) dev->adapter.timeout = AT91_I2C_TIMEOUT; dev->adapter.dev.of_node = pdev->dev.of_node; - rc = at91_twi_probe_master(pdev, phy_addr, dev); + dev->slave_detected = i2c_detect_slave_mode(>dev); + + if (dev->slave_detected) + rc = at91_twi_probe_slave(pdev, phy_addr, dev); + else + rc = at91_twi_probe_master(pdev, phy_addr, dev); if (rc) return rc; diff --git a/drivers/i2c/busses/i2c-at91-slave.c b/drivers/i2c/busses/i2c-at91-slave.c new file mode 100644 index 000..4b4808e --- /dev/null +++ b/drivers/i2c/busses/i2c-at91-slave.c @@ -0,0 +1,147 @@ +/* + * i2c slave support for Atmel's AT91 Two-Wire Interface (TWI) + * + * Copyright (C) 2017 Juergen Fitschen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include "i2c-at91.h" + +static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id) +{ + struct at91_twi_dev *dev = dev_id; + const unsigned status = at91_twi_read(dev, AT91_TWI_SR); + const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR); + u8 value; + + if (!irqstatus) + return IRQ_NONE; + + /* slave address has been detected on I2C bus */ + if (irqstatus & AT91_TWI_SVACC) { + if (status & AT91_TWI_SVREAD) { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_REQUESTED, ); + writeb_relaxed(value, dev->base + AT91_TWI_THR); + at91_twi_write(dev, AT91_TWI_IER, + AT91_TWI_TXRDY | AT91_TWI_EOSACC); + } else { + i2c_slave_event(dev->slave, + I2C_SLAVE_WRITE_REQUESTED, ); + at91_twi_write(dev, AT91_TWI_IER, + AT91_TWI_RXRDY | AT91_TWI_EOSACC); + } + at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC); + } + + /* byte transmitted to remote master */ + if (irqstatus & AT91_TWI_TXRDY) { + i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, ); + writeb_relaxed(value, dev->base + AT91_TWI_THR); + } + + /* byte received from remote master */ + if (irqstatus & AT91_TWI_RXRDY) { + value = readb_relaxed(dev->base + AT91_TWI_RHR); + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, ); + } + + /* master sent stop */ + if (irqstatus & AT91_TWI_EOSACC) { + at91_twi_write(dev, AT91_TWI_IDR, + AT91_TWI_TXRDY | AT91_TWI_RXRDY | AT91_TWI_EOSACC); + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC); + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, ); + } + + return IRQ_HANDLED; +} + +static int at91_reg_slave(struct i2c_client *slave) +{ + struct at91_twi_dev