Skip to content

Commit

Permalink
i2c: at91: add new driver
Browse files Browse the repository at this point in the history
This driver has the following properties compared to the old driver:
1. Support for multiple interfaces.
2. Interrupt driven I/O as opposed to polling/busy waiting.
3. Support for _one_ repeated start (Sr) condition, which is enough
   for most real-world applications including all SMBus transfer types.
   (The hardware does not support issuing arbitrary Sr conditions on the
    bus.)

testing: SoC: at91sam9g45
	 - BQ20Z80 battery SMBus client.
	 - on a 2.6.38 kernel with several i2c clients (temp-sensor,
	   audio-codec, touchscreen-controller, w1-bridge, io-expanders)

Signed-off-by: Nikolaus Voss <n.voss@weinmann.de>
Reviewed-by: Felipe Balbi <balbi@ti.com>
Tested-by: Hubert Feurstein <h.feurstein@gmail.com>
Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Reviewed-by: Nicolas Ferre <nicolas.ferre@atmel.com>

[wsa: squashed with the following patches from Ludovic to have some flaws
fixed:
        i2c: at91: use managed resources
        i2c: at91: add warning about transmission issues for some devices
        i2c: at91: use an id table for SoC dependent parameters
]

Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
  • Loading branch information
Nikolaus Voss authored and Wolfram Sang committed Sep 12, 2012
1 parent a879e9c commit fac368a
Show file tree
Hide file tree
Showing 15 changed files with 545 additions and 22 deletions.
2 changes: 1 addition & 1 deletion arch/arm/mach-at91/at91rm9200.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200", &twi_clk),
/* fake hclk clock */
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
CLKDEV_CON_ID("pioA", &pioA_clk),
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mach-at91/at91rm9200_devices.c
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ static struct resource twi_resources[] = {
};

static struct platform_device at91rm9200_twi_device = {
.name = "at91_i2c",
.name = "i2c-at91rm9200",
.id = -1,
.resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources),
Expand Down
3 changes: 2 additions & 1 deletion arch/arm/mach-at91/at91sam9260.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk),
CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc_clk),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20", &twi_clk),
/* more usart lookup table for DT entries */
CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck),
CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk),
Expand Down
8 changes: 7 additions & 1 deletion arch/arm/mach-at91/at91sam9260_devices.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,14 +503,20 @@ static struct resource twi_resources[] = {
};

static struct platform_device at91sam9260_twi_device = {
.name = "at91_i2c",
.id = -1,
.resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources),
};

void __init at91_add_device_i2c(struct i2c_board_info *devices, int nr_devices)
{
/* IP version is not the same on 9260 and g20 */
if (cpu_is_at91sam9g20()) {
at91sam9260_twi_device.name = "i2c-at91sam9g20";
} else {
at91sam9260_twi_device.name = "i2c-at91sam9260";
}

/* pins used for TWI interface */
at91_set_A_periph(AT91_PIN_PA23, 0); /* TWD */
at91_set_multi_drive(AT91_PIN_PA23, 1);
Expand Down
3 changes: 2 additions & 1 deletion arch/arm/mach-at91/at91sam9261.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk),
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10", &twi_clk),
CLKDEV_CON_ID("pioA", &pioA_clk),
CLKDEV_CON_ID("pioB", &pioB_clk),
CLKDEV_CON_ID("pioC", &pioC_clk),
Expand Down
8 changes: 7 additions & 1 deletion arch/arm/mach-at91/at91sam9261_devices.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,14 +317,20 @@ static struct resource twi_resources[] = {
};

static struct platform_device at91sam9261_twi_device = {
.name = "at91_i2c",
.id = -1,
.resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources),
};

void __init at91_add_device_i2c(struct i2c_board_info *devices, int nr_devices)
{
/* IP version is not the same on 9261 and g10 */
if (cpu_is_at91sam9g10()) {
at91sam9261_twi_device.name = "i2c-at91sam9g10";
} else {
at91sam9261_twi_device.name = "i2c-at91sam9261";
}

/* pins used for TWI interface */
at91_set_A_periph(AT91_PIN_PA7, 0); /* TWD */
at91_set_multi_drive(AT91_PIN_PA7, 1);
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mach-at91/at91sam9263.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tcb_clk),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260", &twi_clk),
/* fake hclk clock */
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
CLKDEV_CON_ID("pioA", &pioA_clk),
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mach-at91/at91sam9263_devices.c
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ static struct resource twi_resources[] = {
};

static struct platform_device at91sam9263_twi_device = {
.name = "at91_i2c",
.name = "i2c-at91sam9260",
.id = -1,
.resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources),
Expand Down
4 changes: 2 additions & 2 deletions arch/arm/mach-at91/at91sam9g45.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tcb0_clk),
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tcb0_clk),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c.0", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c.1", &twi1_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk),
Expand Down
4 changes: 2 additions & 2 deletions arch/arm/mach-at91/at91sam9g45_devices.c
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ static struct resource twi0_resources[] = {
};

static struct platform_device at91sam9g45_twi0_device = {
.name = "at91_i2c",
.name = "i2c-at91sam9g10",
.id = 0,
.resource = twi0_resources,
.num_resources = ARRAY_SIZE(twi0_resources),
Expand All @@ -673,7 +673,7 @@ static struct resource twi1_resources[] = {
};

static struct platform_device at91sam9g45_twi1_device = {
.name = "at91_i2c",
.name = "i2c-at91sam9g10",
.id = 1,
.resource = twi1_resources,
.num_resources = ARRAY_SIZE(twi1_resources),
Expand Down
4 changes: 2 additions & 2 deletions arch/arm/mach-at91/at91sam9rl.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c.0", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c.1", &twi1_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk),
CLKDEV_CON_ID("pioA", &pioA_clk),
CLKDEV_CON_ID("pioB", &pioB_clk),
CLKDEV_CON_ID("pioC", &pioC_clk),
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mach-at91/at91sam9rl_devices.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ static struct resource twi_resources[] = {
};

static struct platform_device at91sam9rl_twi_device = {
.name = "at91_i2c",
.name = "i2c-at91sam9g20",
.id = -1,
.resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources),
Expand Down
17 changes: 10 additions & 7 deletions drivers/i2c/busses/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -290,18 +290,21 @@ comment "I2C system bus drivers (mostly embedded / system-on-chip)"

config I2C_AT91
tristate "Atmel AT91 I2C Two-Wire interface (TWI)"
depends on ARCH_AT91 && EXPERIMENTAL && BROKEN
depends on ARCH_AT91 && EXPERIMENTAL
help
This supports the use of the I2C interface on Atmel AT91
processors.

This driver is BROKEN because the controller which it uses
will easily trigger RX overrun and TX underrun errors. Using
low I2C clock rates may partially work around those issues
on some systems. Another serious problem is that there is no
documented way to issue repeated START conditions, as needed
A serious problem is that there is no documented way to issue
repeated START conditions for more than two messages, as needed
to support combined I2C messages. Use the i2c-gpio driver
unless your system can cope with those limitations.
unless your system can cope with this limitation.

Caution! at91rm9200, at91sam9261, at91sam9260, at91sam9263 devices
don't have clock stretching in transmission mode. For that reason,
you can encounter underrun issues causing premature stop sendings if
the latency to fill the transmission register is too long. If you
are facing this situation, use the i2c-gpio driver.

config I2C_AU1550
tristate "Au1550/Au1200/Au1300 SMBus interface"
Expand Down
1 change: 1 addition & 0 deletions drivers/i2c/busses/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o

# Embedded system I2C/SMBus host controller drivers
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
Expand Down
Loading

0 comments on commit fac368a

Please sign in to comment.