lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20171203180231.GA2843@rfolt0960.corp.atmel.com>
Date:   Sun, 3 Dec 2017 19:02:31 +0100
From:   Ludovic Desroches <ludovic.desroches@...rochip.com>
To:     Juergen Fitschen <me@....yt>
CC:     Ludovic Desroches <ludovic.desroches@...rochip.com>,
        Wolfram Sang <wsa@...-dreams.de>, <linux-i2c@...r.kernel.org>,
        <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v2 3/3] i2c: at91: added slave mode support

Hi Juergen,

On Thu, Nov 09, 2017 at 06:22:29PM +0100, Juergen Fitschen wrote:
> Slave mode driver is based on the concept of i2c-designware driver.
> 

Sorry to be so long to answer you. I would like to test it before acking
it. Unfortunately, I didn't have the time to perform all the tests I
have in mind but I don't way to delay the inclusion of the slave mode
support. If there are bugs or restrictions, we could fix it later.

I tested it quickly on a sama5d2 xplained board: I used an i2c-gpio
master and the eeprom driver. It works pretty well. I tried to increase
the size of the eeprom by adding:
+ { "slave-24c64", 65536 / 8 },
in i2c-slave-eeprom.c. Unfortunately, it no longer works: I get
different checksums. Looking quickly at i2c-slave-eeprom.c, I don't see
any reason why it may not work if I increase the size but I may have
missed something.

I have seen you described how you tested it thanks. Maybe you have also
tried in the same way as me? Did you experience the same behavior?

Regards

Ludovic

> Signed-off-by: Juergen Fitschen <me@....yt>
> ---
> Changes in v2:
>  - Squashed commit "take slave mode capabilities of hardware into
>    account" into this commit
>  - Removed unused feature bit AT91_TWI_SM_HAS_FIFO
>  - Added NACK support
>  - Reworked interrupt handler:
>    - The interrupt handler takes into account that several events can
>      occur between two handler calls and thus must be handled in one
>      handler run. It caanot be assumed that every events triggers the
>      handler individually.
>    - Instead of using the states register of the I2c interface, a state
>      machine is held in the at91_twi_dev struct. This ensures that no
>      state transitions are missed due ti interrupt latency.
>  - Added Kconfig option for selection whether slave mode suppurt should
>    be included in the driver or not
>  - Added example in Documentation/devicetree/bindings/i2c/i2c-at91.txt
> ---
>  Documentation/devicetree/bindings/i2c/i2c-at91.txt |  14 ++
>  drivers/i2c/busses/Kconfig                         |  10 +
>  drivers/i2c/busses/Makefile                        |   3 +
>  drivers/i2c/busses/i2c-at91-core.c                 |  23 ++-
>  drivers/i2c/busses/i2c-at91-slave.c                | 216 +++++++++++++++++++++
>  drivers/i2c/busses/i2c-at91.h                      |  42 +++-
>  6 files changed, 304 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/i2c/busses/i2c-at91-slave.c
> 
> diff --git a/Documentation/devicetree/bindings/i2c/i2c-at91.txt b/Documentation/devicetree/bindings/i2c/i2c-at91.txt
> index ef973a0..95c79a8 100644
> --- a/Documentation/devicetree/bindings/i2c/i2c-at91.txt
> +++ b/Documentation/devicetree/bindings/i2c/i2c-at91.txt
> @@ -61,3 +61,17 @@ i2c0: i2c@...34600 {
>  		reg = <0x1a>;
>  	};
>  };
> +
> +i2c0: i2c@...28000 {
> +	compatible = "atmel,sama5d2-i2c";
> +	reg = <0xf8028000 0x100>;
> +	interrupts = <29 4 7>;
> +	#address-cells = <1>;
> +	#size-cells = <0>;
> +	clocks = <&twi0_clk>;
> +
> +	eeprom@64 {
> +		compatible = "linux,slave-24c02";
> +		reg = <0x40000064>;
> +	};
> +};
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 009345d..41338e8 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -380,6 +380,16 @@ config I2C_AT91
>  	  the latency to fill the transmission register is too long. If you
>  	  are facing this situation, use the i2c-gpio driver.
>  
> +config I2C_AT91_SLAVE
> +	bool "Atmel AT91 I2C Two-Wire interface (TWI) Slave"
> +	depends on I2C_AT91 && I2C_SLAVE
> +	help
> +	  This adds slave mode support to the I2C interface on Atmel AT91
> +	  processors.
> +
> +	  This is not a standalone module and must be compiled together with the
> +	  master mode driver.
> +
>  config I2C_AU1550
>  	tristate "Au1550/Au1200/Au1300 SMBus interface"
>  	depends on MIPS_ALCHEMY
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 2a79c3d..daa2ea4 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_AT91_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..cba447e 100644
> --- a/drivers/i2c/busses/i2c-at91-core.c
> +++ b/drivers/i2c/busses/i2c-at91-core.c
> @@ -60,13 +60,16 @@ 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 = {
>  	.clk_max_div = 5,
>  	.clk_offset = 3,
> +	.slave_mode_features = 0,
>  	.has_unre_flag = true,
>  	.has_alt_cmd = false,
>  	.has_hold_field = false,
> @@ -75,6 +78,7 @@ static struct at91_twi_pdata at91rm9200_config = {
>  static struct at91_twi_pdata at91sam9261_config = {
>  	.clk_max_div = 5,
>  	.clk_offset = 4,
> +	.slave_mode_features = AT91_TWI_SM_AVAILABLE,
>  	.has_unre_flag = false,
>  	.has_alt_cmd = false,
>  	.has_hold_field = false,
> @@ -83,6 +87,7 @@ static struct at91_twi_pdata at91sam9261_config = {
>  static struct at91_twi_pdata at91sam9260_config = {
>  	.clk_max_div = 7,
>  	.clk_offset = 4,
> +	.slave_mode_features = AT91_TWI_SM_AVAILABLE,
>  	.has_unre_flag = false,
>  	.has_alt_cmd = false,
>  	.has_hold_field = false,
> @@ -91,6 +96,7 @@ static struct at91_twi_pdata at91sam9260_config = {
>  static struct at91_twi_pdata at91sam9g20_config = {
>  	.clk_max_div = 7,
>  	.clk_offset = 4,
> +	.slave_mode_features = AT91_TWI_SM_AVAILABLE,
>  	.has_unre_flag = false,
>  	.has_alt_cmd = false,
>  	.has_hold_field = false,
> @@ -99,6 +105,7 @@ static struct at91_twi_pdata at91sam9g20_config = {
>  static struct at91_twi_pdata at91sam9g10_config = {
>  	.clk_max_div = 7,
>  	.clk_offset = 4,
> +	.slave_mode_features = AT91_TWI_SM_AVAILABLE,
>  	.has_unre_flag = false,
>  	.has_alt_cmd = false,
>  	.has_hold_field = false,
> @@ -129,6 +136,7 @@ static const struct platform_device_id at91_twi_devtypes[] = {
>  static struct at91_twi_pdata at91sam9x5_config = {
>  	.clk_max_div = 7,
>  	.clk_offset = 4,
> +	.slave_mode_features = AT91_TWI_SM_AVAILABLE,
>  	.has_unre_flag = false,
>  	.has_alt_cmd = false,
>  	.has_hold_field = false,
> @@ -137,6 +145,7 @@ static struct at91_twi_pdata at91sam9x5_config = {
>  static struct at91_twi_pdata sama5d4_config = {
>  	.clk_max_div = 7,
>  	.clk_offset = 4,
> +	.slave_mode_features = AT91_TWI_SM_AVAILABLE,
>  	.has_unre_flag = false,
>  	.has_alt_cmd = false,
>  	.has_hold_field = true,
> @@ -145,6 +154,7 @@ static struct at91_twi_pdata sama5d4_config = {
>  static struct at91_twi_pdata sama5d2_config = {
>  	.clk_max_div = 7,
>  	.clk_offset = 4,
> +	.slave_mode_features = AT91_TWI_SM_AVAILABLE | AT91_TWI_SM_CAN_NACK,
>  	.has_unre_flag = true,
>  	.has_alt_cmd = true,
>  	.has_hold_field = true,
> @@ -217,6 +227,10 @@ static int at91_twi_probe(struct platform_device *pdev)
>  	if (!dev->pdata)
>  		return -ENODEV;
>  
> +	dev->slave_detected = i2c_detect_slave_mode(&pdev->dev);
> +	if (dev->slave_detected && dev->pdata->slave_mode_features == 0)
> +		return -EPFNOSUPPORT;
> +
>  	dev->base = devm_ioremap_resource(&pdev->dev, mem);
>  	if (IS_ERR(dev->base))
>  		return PTR_ERR(dev->base);
> @@ -243,7 +257,10 @@ 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);
> +	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 0000000..a5881e9
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-at91-slave.c
> @@ -0,0 +1,216 @@
> +/*
> + *  i2c slave support for Atmel's AT91 Two-Wire Interface (TWI)
> + *
> + *  Copyright (C) 2017 Juergen Fitschen <me@....yt>
> + *
> + *  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 <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/pm_runtime.h>
> +
> +#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;
> +
> +	/*
> +	 * The order of processing AT91_TWI_TXRDY [a], AT91_TWI_SVACC [b] and
> +	 * AT91_TWI_RXRDY [c] status bit is important:
> +	 *  + Remote master wants to read data:
> +	 *    - AT91_TWI_SVACC IRQ with AT91_TWI_SVREAD unset is raised
> +	 *    - I2C_SLAVE_READ_REQUESTED slave event is fired and first byte
> +	 *      received from the I2C slave backend
> +	 *    - Byte is written to AT91_TWI_THR
> +	 *    - AT91_TWI_TXRDY is still set since AT91_TWI_SR isn't reread but
> +	 *      AT91_TWI_THR must not be written a second time!
> +	 *    --> Check AT91_TWI_TXRDY before AT91_TWI_SVACC
> +	 *  + Remote master wants to write data:
> +	 *    - AT91_TWI_SVACC IRQ with AT91_TWI_SVREAD set is raised
> +	 *    - If the first byte already has been received due to interrupt
> +	 *      latency, AT91_TWI_RXRDY is set and AT91_TWI_RHR has to be read
> +	 *      in the same IRQ handler run!
> +	 *    --> Check AT91_TWI_RXRDY after AT91_TWI_SVACC
> +	 */
> +
> +	/*
> +	 * [a] Next byte can be stored into transmit holding register
> +	 */
> +	if ((dev->state == AT91_TWI_STATE_TX) && (status & AT91_TWI_TXRDY)) {
> +		i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &value);
> +		writeb_relaxed(value, dev->base + AT91_TWI_THR);
> +
> +		dev_dbg(dev->dev, "DATA %02x", value);
> +	}
> +
> +	/*
> +	 * [b] An I2C transmission has been started and the interface detected
> +	 * its slave address.
> +	 */
> +	if ((dev->state == AT91_TWI_STATE_STOP) && (status & AT91_TWI_SVACC)) {
> +		/*
> +		 * AT91_TWI_SVREAD indicates whether data should be read from or
> +		 * written to the slave. This works flawlessly until the
> +		 * transmission has been stopped (i.e. AT91_TWI_EOSACC is set).
> +		 * If the interrupt latency is high, a master can start a write
> +		 * transmission, write one byte and stop the transmission before
> +		 * the IRQ handler is called. In that case AT91_TWI_SVACC,
> +		 * AT91_TWI_RXRDY and AT91_TWI_EOSACC are set, but we cannot
> +		 * rely on AT91_TWI_SVREAD. That's the reason why the following
> +		 * condition looks like it does.
> +		 */
> +		if (!(status & AT91_TWI_SVREAD) ||
> +		    ((status & AT91_TWI_EOSACC) && (status & AT91_TWI_RXRDY))) {
> +			i2c_slave_event(dev->slave,
> +					I2C_SLAVE_WRITE_REQUESTED, &value);
> +
> +			at91_twi_write(dev, AT91_TWI_IER,
> +				       AT91_TWI_RXRDY | AT91_TWI_EOSACC);
> +
> +			dev->state = AT91_TWI_STATE_RX;
> +
> +			dev_dbg(dev->dev, "START LOCAL <- REMOTE");
> +		} else {
> +			i2c_slave_event(dev->slave,
> +					I2C_SLAVE_READ_REQUESTED, &value);
> +			writeb_relaxed(value, dev->base + AT91_TWI_THR);
> +
> +			at91_twi_write(dev, AT91_TWI_IER,
> +				       AT91_TWI_TXRDY | AT91_TWI_EOSACC);
> +
> +			dev->state = AT91_TWI_STATE_TX;
> +
> +			dev_dbg(dev->dev, "START LOCAL -> REMOTE");
> +			dev_dbg(dev->dev, "DATA %02x", value);
> +		}
> +		at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC);
> +	}
> +
> +	/*
> +	 * [c] Byte can be read from receive holding register
> +	 */
> +	if ((dev->state == AT91_TWI_STATE_RX) && (status & AT91_TWI_RXRDY)) {
> +		int rc;
> +
> +		value = readb_relaxed(dev->base + AT91_TWI_RHR);
> +		rc = i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
> +		if ((rc < 0) && (dev->pdata->slave_mode_features & AT91_TWI_SM_CAN_NACK))
> +			at91_twi_write(dev, AT91_TWI_SMR, dev->smr | AT91_TWI_SMR_NACKEN);
> +		else
> +			at91_twi_write(dev, AT91_TWI_SMR, dev->smr);
> +
> +		dev_dbg(dev->dev, "DATA %02x", value);
> +	}
> +
> +	/*
> +	 * Master sent stop
> +	 */
> +	if ((dev->state != AT91_TWI_STATE_STOP) && (status & 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_CR,
> +			       AT91_TWI_THRCLR | AT91_TWI_RHRCLR);
> +		at91_twi_write(dev, AT91_TWI_IER,
> +			       AT91_TWI_SVACC);
> +
> +		i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &value);
> +
> +		dev->state = AT91_TWI_STATE_STOP;
> +		dev_dbg(dev->dev, "STOP");
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91_reg_slave(struct i2c_client *slave)
> +{
> +	struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
> +
> +	if (dev->slave)
> +		return -EBUSY;
> +
> +	if (slave->flags & I2C_CLIENT_TEN)
> +		return -EAFNOSUPPORT;
> +
> +	/* Make sure twi_clk doesn't get turned off! */
> +	pm_runtime_get_sync(dev->dev);
> +
> +	dev->slave = slave;
> +	dev->smr = AT91_TWI_SMR_SADR(slave->addr);
> +
> +	at91_init_twi_bus(dev);
> +
> +	dev_info(dev->dev, "entered slave mode (ADR=%d)\n", slave->addr);
> +
> +	return 0;
> +}
> +
> +static int at91_unreg_slave(struct i2c_client *slave)
> +{
> +	struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
> +
> +	WARN_ON(!dev->slave);
> +
> +	dev_info(dev->dev, "leaving slave mode\n");
> +
> +	dev->slave = NULL;
> +	dev->smr = 0;
> +
> +	at91_init_twi_bus(dev);
> +
> +	pm_runtime_put(dev->dev);
> +
> +	return 0;
> +}
> +
> +static u32 at91_twi_func(struct i2c_adapter *adapter)
> +{
> +	return I2C_FUNC_SLAVE | I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
> +		| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
> +}
> +
> +static const struct i2c_algorithm at91_twi_algorithm_slave = {
> +	.reg_slave	= at91_reg_slave,
> +	.unreg_slave	= at91_unreg_slave,
> +	.functionality	= at91_twi_func,
> +};
> +
> +int at91_twi_probe_slave(struct platform_device *pdev,
> +			 u32 phy_addr, struct at91_twi_dev *dev)
> +{
> +	int rc;
> +
> +	rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt_slave,
> +			      0, dev_name(dev->dev), dev);
> +	if (rc) {
> +		dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
> +		return rc;
> +	}
> +
> +	dev->adapter.algo = &at91_twi_algorithm_slave;
> +
> +	return 0;
> +}
> +
> +void at91_init_twi_bus_slave(struct at91_twi_dev *dev)
> +{
> +	at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS);
> +	if (dev->smr) {
> +		dev->state = AT91_TWI_STATE_STOP;
> +		at91_twi_write(dev, AT91_TWI_SMR, dev->smr);
> +		at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVEN);
> +		at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
> +	}
> +}
> diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h
> index 555b167..e15a99e 100644
> --- a/drivers/i2c/busses/i2c-at91.h
> +++ b/drivers/i2c/busses/i2c-at91.h
> @@ -53,6 +53,11 @@
>  #define	AT91_TWI_IADRSZ_1	0x0100	/* Internal Device Address Size */
>  #define	AT91_TWI_MREAD		BIT(12)	/* Master Read Direction */
>  
> +#define	AT91_TWI_SMR		0x0008	/* Slave Mode Register */
> +#define	AT91_TWI_SMR_NACKEN	BIT(0)	/* NACK value in next ACK cycle */
> +#define	AT91_TWI_SMR_SADR_MAX	0x007f
> +#define	AT91_TWI_SMR_SADR(x)	(((x) & AT91_TWI_SMR_SADR_MAX) << 16)
> +
>  #define	AT91_TWI_IADR		0x000c	/* Internal Address Register */
>  
>  #define	AT91_TWI_CWGR		0x0010	/* Clock Waveform Generator Reg */
> @@ -63,13 +68,17 @@
>  #define	AT91_TWI_TXCOMP		BIT(0)	/* Transmission Complete */
>  #define	AT91_TWI_RXRDY		BIT(1)	/* Receive Holding Register Ready */
>  #define	AT91_TWI_TXRDY		BIT(2)	/* Transmit Holding Register Ready */
> +#define	AT91_TWI_SVREAD		BIT(3)	/* Slave Read */
> +#define	AT91_TWI_SVACC		BIT(4)	/* Slave Access */
>  #define	AT91_TWI_OVRE		BIT(6)	/* Overrun Error */
>  #define	AT91_TWI_UNRE		BIT(7)	/* Underrun Error */
>  #define	AT91_TWI_NACK		BIT(8)	/* Not Acknowledged */
> +#define	AT91_TWI_EOSACC		BIT(11)	/* End Of Slave Access */
>  #define	AT91_TWI_LOCK		BIT(23) /* TWI Lock due to Frame Errors */
>  
>  #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)
>  
>  #define	AT91_TWI_IER		0x0024	/* Interrupt Enable Register */
>  #define	AT91_TWI_IDR		0x0028	/* Interrupt Disable Register */
> @@ -99,9 +108,19 @@
>  
>  #define	AT91_TWI_VER		0x00fc	/* Version Register */
>  
> +#define	AT91_TWI_SM_AVAILABLE	BIT(0)	/* Slave mode supported */
> +#define	AT91_TWI_SM_CAN_NACK	BIT(1)	/* Can send NACKs in slave mode */
> +
> +enum at91_twi_state {
> +	AT91_TWI_STATE_STOP,
> +	AT91_TWI_STATE_TX,
> +	AT91_TWI_STATE_RX
> +};
> +
>  struct at91_twi_pdata {
>  	unsigned clk_max_div;
>  	unsigned clk_offset;
> +	unsigned slave_mode_features;
>  	bool has_unre_flag;
>  	bool has_alt_cmd;
>  	bool has_hold_field;
> @@ -137,6 +156,12 @@ struct at91_twi_dev {
>  	bool recv_len_abort;
>  	u32 fifo_size;
>  	struct at91_twi_dma dma;
> +	bool slave_detected;
> +#if IS_ENABLED(CONFIG_I2C_AT91_SLAVE)
> +	unsigned smr;
> +	enum at91_twi_state state;
> +	struct i2c_client *slave;
> +#endif
>  };
>  
>  unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg);
> @@ -149,3 +174,18 @@ void at91_init_twi_bus(struct at91_twi_dev *dev);
>  void at91_init_twi_bus_master(struct at91_twi_dev *dev);
>  int at91_twi_probe_master(struct platform_device *pdev, u32 phy_addr,
>  			  struct at91_twi_dev *dev);
> +
> +#if IS_ENABLED(CONFIG_I2C_AT91_SLAVE)
> +void at91_init_twi_bus_slave(struct at91_twi_dev *dev);
> +int at91_twi_probe_slave(struct platform_device *pdev, u32 phy_addr,
> +			 struct at91_twi_dev *dev);
> +
> +#else
> +static inline void at91_init_twi_bus_slave(struct at91_twi_dev *dev) {}
> +static inline int at91_twi_probe_slave(struct platform_device *pdev,
> +				       u32 phy_addr, struct at91_twi_dev *dev)
> +{
> +	return -EINVAL;
> +}
> +
> +#endif
> -- 
> 2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ