Commit fac368a0 authored by Nikolaus Voss's avatar Nikolaus Voss Committed by Wolfram Sang

i2c: at91: add new driver

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: default avatarNikolaus Voss <n.voss@weinmann.de>
Reviewed-by: default avatarFelipe Balbi <balbi@ti.com>
Tested-by: default avatarHubert Feurstein <h.feurstein@gmail.com>
Tested-by: default avatarLudovic Desroches <ludovic.desroches@atmel.com>
Reviewed-by: default avatarNicolas 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: default avatarLudovic Desroches <ludovic.desroches@atmel.com>
Signed-off-by: default avatarWolfram Sang <w.sang@pengutronix.de>
parent a879e9c3
...@@ -187,7 +187,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -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.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_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 */ /* fake hclk clock */
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
CLKDEV_CON_ID("pioA", &pioA_clk), CLKDEV_CON_ID("pioA", &pioA_clk),
......
...@@ -495,7 +495,7 @@ static struct resource twi_resources[] = { ...@@ -495,7 +495,7 @@ static struct resource twi_resources[] = {
}; };
static struct platform_device at91rm9200_twi_device = { static struct platform_device at91rm9200_twi_device = {
.name = "at91_i2c", .name = "i2c-at91rm9200",
.id = -1, .id = -1,
.resource = twi_resources, .resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources), .num_resources = ARRAY_SIZE(twi_resources),
......
...@@ -211,7 +211,8 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -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("t1_clk", "atmel_tcb.1", &tc4_clk),
CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_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("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 */ /* more usart lookup table for DT entries */
CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck), CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck),
CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk), CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk),
......
...@@ -503,7 +503,6 @@ static struct resource twi_resources[] = { ...@@ -503,7 +503,6 @@ static struct resource twi_resources[] = {
}; };
static struct platform_device at91sam9260_twi_device = { static struct platform_device at91sam9260_twi_device = {
.name = "at91_i2c",
.id = -1, .id = -1,
.resource = twi_resources, .resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources), .num_resources = ARRAY_SIZE(twi_resources),
...@@ -511,6 +510,13 @@ static struct platform_device at91sam9260_twi_device = { ...@@ -511,6 +510,13 @@ static struct platform_device at91sam9260_twi_device = {
void __init at91_add_device_i2c(struct i2c_board_info *devices, int nr_devices) 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 */ /* pins used for TWI interface */
at91_set_A_periph(AT91_PIN_PA23, 0); /* TWD */ at91_set_A_periph(AT91_PIN_PA23, 0); /* TWD */
at91_set_multi_drive(AT91_PIN_PA23, 1); at91_set_multi_drive(AT91_PIN_PA23, 1);
......
...@@ -178,7 +178,8 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -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.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk), CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk),
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0), 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("pioA", &pioA_clk),
CLKDEV_CON_ID("pioB", &pioB_clk), CLKDEV_CON_ID("pioB", &pioB_clk),
CLKDEV_CON_ID("pioC", &pioC_clk), CLKDEV_CON_ID("pioC", &pioC_clk),
......
...@@ -317,7 +317,6 @@ static struct resource twi_resources[] = { ...@@ -317,7 +317,6 @@ static struct resource twi_resources[] = {
}; };
static struct platform_device at91sam9261_twi_device = { static struct platform_device at91sam9261_twi_device = {
.name = "at91_i2c",
.id = -1, .id = -1,
.resource = twi_resources, .resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources), .num_resources = ARRAY_SIZE(twi_resources),
...@@ -325,6 +324,13 @@ static struct platform_device at91sam9261_twi_device = { ...@@ -325,6 +324,13 @@ static struct platform_device at91sam9261_twi_device = {
void __init at91_add_device_i2c(struct i2c_board_info *devices, int nr_devices) 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 */ /* pins used for TWI interface */
at91_set_A_periph(AT91_PIN_PA7, 0); /* TWD */ at91_set_A_periph(AT91_PIN_PA7, 0); /* TWD */
at91_set_multi_drive(AT91_PIN_PA7, 1); at91_set_multi_drive(AT91_PIN_PA7, 1);
......
...@@ -193,7 +193,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -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.0", &spi0_clk),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_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("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 */ /* fake hclk clock */
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
CLKDEV_CON_ID("pioA", &pioA_clk), CLKDEV_CON_ID("pioA", &pioA_clk),
......
...@@ -574,7 +574,7 @@ static struct resource twi_resources[] = { ...@@ -574,7 +574,7 @@ static struct resource twi_resources[] = {
}; };
static struct platform_device at91sam9263_twi_device = { static struct platform_device at91sam9263_twi_device = {
.name = "at91_i2c", .name = "i2c-at91sam9260",
.id = -1, .id = -1,
.resource = twi_resources, .resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources), .num_resources = ARRAY_SIZE(twi_resources),
......
...@@ -237,8 +237,8 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -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("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.0", &tcb0_clk),
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &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, "i2c-at91sam9g10.0", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c.1", &twi1_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.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk), CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk),
......
...@@ -653,7 +653,7 @@ static struct resource twi0_resources[] = { ...@@ -653,7 +653,7 @@ static struct resource twi0_resources[] = {
}; };
static struct platform_device at91sam9g45_twi0_device = { static struct platform_device at91sam9g45_twi0_device = {
.name = "at91_i2c", .name = "i2c-at91sam9g10",
.id = 0, .id = 0,
.resource = twi0_resources, .resource = twi0_resources,
.num_resources = ARRAY_SIZE(twi0_resources), .num_resources = ARRAY_SIZE(twi0_resources),
...@@ -673,7 +673,7 @@ static struct resource twi1_resources[] = { ...@@ -673,7 +673,7 @@ static struct resource twi1_resources[] = {
}; };
static struct platform_device at91sam9g45_twi1_device = { static struct platform_device at91sam9g45_twi1_device = {
.name = "at91_i2c", .name = "i2c-at91sam9g10",
.id = 1, .id = 1,
.resource = twi1_resources, .resource = twi1_resources,
.num_resources = ARRAY_SIZE(twi1_resources), .num_resources = ARRAY_SIZE(twi1_resources),
......
...@@ -186,8 +186,8 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -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("t2_clk", "atmel_tcb.0", &tc2_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_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, "i2c-at91sam9g20.0", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "at91_i2c.1", &twi1_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk),
CLKDEV_CON_ID("pioA", &pioA_clk), CLKDEV_CON_ID("pioA", &pioA_clk),
CLKDEV_CON_ID("pioB", &pioB_clk), CLKDEV_CON_ID("pioB", &pioB_clk),
CLKDEV_CON_ID("pioC", &pioC_clk), CLKDEV_CON_ID("pioC", &pioC_clk),
......
...@@ -346,7 +346,7 @@ static struct resource twi_resources[] = { ...@@ -346,7 +346,7 @@ static struct resource twi_resources[] = {
}; };
static struct platform_device at91sam9rl_twi_device = { static struct platform_device at91sam9rl_twi_device = {
.name = "at91_i2c", .name = "i2c-at91sam9g20",
.id = -1, .id = -1,
.resource = twi_resources, .resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources), .num_resources = ARRAY_SIZE(twi_resources),
......
...@@ -290,18 +290,21 @@ comment "I2C system bus drivers (mostly embedded / system-on-chip)" ...@@ -290,18 +290,21 @@ comment "I2C system bus drivers (mostly embedded / system-on-chip)"
config I2C_AT91 config I2C_AT91
tristate "Atmel AT91 I2C Two-Wire interface (TWI)" tristate "Atmel AT91 I2C Two-Wire interface (TWI)"
depends on ARCH_AT91 && EXPERIMENTAL && BROKEN depends on ARCH_AT91 && EXPERIMENTAL
help help
This supports the use of the I2C interface on Atmel AT91 This supports the use of the I2C interface on Atmel AT91
processors. processors.
This driver is BROKEN because the controller which it uses A serious problem is that there is no documented way to issue
will easily trigger RX overrun and TX underrun errors. Using repeated START conditions for more than two messages, as needed
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
to support combined I2C messages. Use the i2c-gpio driver 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 config I2C_AU1550
tristate "Au1550/Au1200/Au1300 SMBus interface" tristate "Au1550/Au1200/Au1300 SMBus interface"
......
...@@ -28,6 +28,7 @@ obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o ...@@ -28,6 +28,7 @@ obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
# Embedded system I2C/SMBus host controller drivers # Embedded system I2C/SMBus host controller drivers
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment