[U-Boot] [PATCH v3 0/2] i2c: atmel: add driver for Atmel i2c platform

The i2c driver includes two parts. 1) Driver code to implement the i2c function. 2) Device tree binding documentation, it describes how to add the i2c in device tree.
Changes in v3: - Update the clk API.
Changes in v2: - Add code to get and enable clock. - Add phandles to input clocks
Songjun Wu (2): i2c: atmel: add i2c driver i2c: atmel: DT binding for i2c driver
doc/device-tree-bindings/i2c/i2c-at91.txt | 26 +++ drivers/i2c/Kconfig | 10 + drivers/i2c/Makefile | 1 + drivers/i2c/at91_i2c.c | 338 ++++++++++++++++++++++++++++++ drivers/i2c/at91_i2c.h | 77 +++++++ 5 files changed, 452 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-at91.txt create mode 100644 drivers/i2c/at91_i2c.c create mode 100644 drivers/i2c/at91_i2c.h

Add i2c driver.
Signed-off-by: Songjun Wu songjun.wu@atmel.com ---
Changes in v3: - Update the clk API.
Changes in v2: - Add code to get and enable clock.
drivers/i2c/Kconfig | 10 ++ drivers/i2c/Makefile | 1 + drivers/i2c/at91_i2c.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/at91_i2c.h | 77 +++++++++++ 4 files changed, 426 insertions(+) create mode 100644 drivers/i2c/at91_i2c.c create mode 100644 drivers/i2c/at91_i2c.h
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 6e22bba..f6a613c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -58,6 +58,16 @@ config DM_I2C_GPIO bindings are supported. Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
+config SYS_I2C_AT91 + bool "Atmel I2C driver" + depends on DM_I2C && ARCH_AT91 + help + Add support for the Atmel I2C driver. 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 this limitation. + Binding info: doc/device-tree-bindings/i2c/i2c-at91.txt + config SYS_I2C_FSL bool "Freescale I2C bus driver" depends on DM_I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 167424d..c5362a5 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o obj-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o obj-$(CONFIG_SYS_I2C) += i2c_core.o +obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o diff --git a/drivers/i2c/at91_i2c.c b/drivers/i2c/at91_i2c.c new file mode 100644 index 0000000..8e9c3ad --- /dev/null +++ b/drivers/i2c/at91_i2c.c @@ -0,0 +1,338 @@ +/* + * Atmel I2C driver. + * + * (C) Copyright 2016 Songjun Wu songjun.wu@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/io.h> +#include <common.h> +#include <clk_client.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> +#include <linux/bitops.h> +#include <mach/clk.h> + +#include "at91_i2c.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define I2C_TIMEOUT_MS 100 + +static int at91_wait_for_xfer(struct at91_i2c_bus *bus, u32 status) +{ + struct at91_i2c_regs *reg = bus->regs; + ulong start_time = get_timer(0); + u32 sr; + + bus->status = 0; + + do { + sr = readl(®->sr); + bus->status |= sr; + + if (sr & TWI_SR_NACK) + return -EREMOTEIO; + else if (sr & status) + return 0; + } while (get_timer(start_time) < I2C_TIMEOUT_MS); + + return -ETIMEDOUT; +} + +static int at91_i2c_xfer_msg(struct at91_i2c_bus *bus, struct i2c_msg *msg) +{ + struct at91_i2c_regs *reg = bus->regs; + bool is_read = msg->flags & I2C_M_RD; + u32 i; + int ret = 0; + + readl(®->sr); + if (is_read) { + writel(TWI_CR_START, ®->cr); + + for (i = 0; !ret && i < (msg->len - 1); i++) { + ret = at91_wait_for_xfer(bus, TWI_SR_RXRDY); + msg->buf[i] = readl(®->rhr); + } + + if (ret) + goto error; + + writel(TWI_CR_STOP, ®->cr); + + ret = at91_wait_for_xfer(bus, TWI_SR_RXRDY); + if (ret) + goto error; + + msg->buf[i] = readl(®->rhr); + + } else { + writel(msg->buf[0], ®->thr); + for (i = 1; !ret && (i < msg->len); i++) { + writel(msg->buf[i], ®->thr); + ret = at91_wait_for_xfer(bus, TWI_SR_TXRDY); + } + + if (ret) + goto error; + + writel(TWI_CR_STOP, ®->cr); + } + + if (!ret) + ret = at91_wait_for_xfer(bus, TWI_SR_TXCOMP); + + if (ret) + goto error; + + if (bus->status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_LOCK)) { + ret = -EIO; + goto error; + } + + return 0; + +error: + if (bus->status & TWI_SR_LOCK) + writel(TWI_CR_LOCKCLR, ®->cr); + + return ret; +} + +static int at91_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + struct at91_i2c_bus *bus = dev_get_priv(dev); + struct at91_i2c_regs *reg = bus->regs; + struct i2c_msg *m_start = msg; + bool is_read; + u32 int_addr_flag = 0; + int ret = 0; + + if (nmsgs == 2) { + int internal_address = 0; + int i; + + /* 1st msg is put into the internal address, start with 2nd */ + m_start = &msg[1]; + + /* the max length of internal address is 3 bytes */ + if (msg->len > 3) + return -EFAULT; + + for (i = 0; i < msg->len; ++i) { + const unsigned addr = msg->buf[msg->len - 1 - i]; + + internal_address |= addr << (8 * i); + int_addr_flag += TWI_MMR_IADRSZ_1; + } + + writel(internal_address, ®->iadr); + } + + is_read = m_start->flags & I2C_M_RD; + + writel((m_start->addr << 16) | int_addr_flag | + (is_read ? TWI_MMR_MREAD : 0), ®->mmr); + + ret = at91_i2c_xfer_msg(bus, m_start); + + return ret; +} + +/* + * Calculate symmetric clock as stated in datasheet: + * twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset)) + */ +static void at91_calc_i2c_clock(struct udevice *dev, int i2c_clk) +{ + struct at91_i2c_bus *bus = dev_get_priv(dev); + const struct at91_i2c_pdata *pdata = bus->pdata; + int offset = pdata->clk_offset; + int max_ckdiv = pdata->clk_max_div; + int ckdiv, cdiv, div; + unsigned long src_rate; + + src_rate = bus->bus_clk_rate; + + div = max(0, (int)DIV_ROUND_UP(src_rate, 2 * i2c_clk) - offset); + ckdiv = fls(div >> 8); + cdiv = div >> ckdiv; + + if (ckdiv > max_ckdiv) { + ckdiv = max_ckdiv; + cdiv = 255; + } + + bus->speed = DIV_ROUND_UP(src_rate, + (cdiv * (1 << ckdiv) + offset) * 2); + + bus->cwgr_val = (ckdiv << 16) | (cdiv << 8) | cdiv; +} + +static int at91_i2c_enable_clk(struct udevice *dev) +{ + struct at91_i2c_bus *bus = dev_get_priv(dev); + struct udevice *dev_clk; + struct clk clk; + ulong clk_rate; + int periph; + int ret; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) + return -EINVAL; + + periph = fdtdec_get_uint(gd->fdt_blob, clk.dev->of_offset, "reg", -1); + if (periph < 0) + return -EINVAL; + + dev_clk = dev_get_parent(clk.dev); + ret = clk_request(dev_clk, &clk); + if (ret) + return ret; + + clk.id = periph; + ret = clk_enable(&clk); + if (ret) + return ret; + + ret = clk_get_by_index(dev_clk, 0, &clk); + if (ret) + return ret; + + clk_rate = clk_get_rate(&clk); + if (!clk_rate) + return -ENODEV; + + bus->bus_clk_rate = clk_rate; + + clk_free(&clk); + + return 0; +} + +static int at91_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) +{ + struct at91_i2c_bus *bus = dev_get_priv(dev); + struct at91_i2c_regs *reg = bus->regs; + int ret; + + ret = at91_i2c_enable_clk(dev); + if (ret) + return ret; + + writel(TWI_CR_SWRST, ®->cr); + + at91_calc_i2c_clock(dev, bus->clock_frequency); + + writel(bus->cwgr_val, ®->cwgr); + writel(TWI_CR_MSEN, ®->cr); + writel(TWI_CR_SVDIS, ®->cr); + + return 0; +} + +static int at91_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +{ + struct at91_i2c_bus *bus = dev_get_priv(dev); + + at91_calc_i2c_clock(dev, speed); + + writel(bus->cwgr_val, &bus->regs->cwgr); + + return 0; +} + +int at91_i2c_get_bus_speed(struct udevice *dev) +{ + struct at91_i2c_bus *bus = dev_get_priv(dev); + + return bus->speed; +} + +static int at91_i2c_ofdata_to_platdata(struct udevice *dev) +{ + const void *blob = gd->fdt_blob; + struct at91_i2c_bus *bus = dev_get_priv(dev); + int node = dev->of_offset; + + bus->regs = (struct at91_i2c_regs *)dev_get_addr(dev); + bus->pdata = (struct at91_i2c_pdata *)dev_get_driver_data(dev); + bus->clock_frequency = fdtdec_get_int(blob, node, + "clock-frequency", 100000); + + return 0; +} + +static const struct dm_i2c_ops at91_i2c_ops = { + .xfer = at91_i2c_xfer, + .probe_chip = at91_i2c_probe, + .set_bus_speed = at91_i2c_set_bus_speed, + .get_bus_speed = at91_i2c_get_bus_speed, +}; + +static const struct at91_i2c_pdata at91rm9200_config = { + .clk_max_div = 5, + .clk_offset = 3, +}; + +static const struct at91_i2c_pdata at91sam9261_config = { + .clk_max_div = 5, + .clk_offset = 4, +}; + +static const struct at91_i2c_pdata at91sam9260_config = { + .clk_max_div = 7, + .clk_offset = 4, +}; + +static const struct at91_i2c_pdata at91sam9g20_config = { + .clk_max_div = 7, + .clk_offset = 4, +}; + +static const struct at91_i2c_pdata at91sam9g10_config = { + .clk_max_div = 7, + .clk_offset = 4, +}; + +static const struct at91_i2c_pdata at91sam9x5_config = { + .clk_max_div = 7, + .clk_offset = 4, +}; + +static const struct at91_i2c_pdata sama5d4_config = { + .clk_max_div = 7, + .clk_offset = 4, +}; + +static const struct at91_i2c_pdata sama5d2_config = { + .clk_max_div = 7, + .clk_offset = 3, +}; + +static const struct udevice_id at91_i2c_ids[] = { +{ .compatible = "atmel,at91rm9200-i2c", .data = (long)&at91rm9200_config }, +{ .compatible = "atmel,at91sam9260-i2c", .data = (long)&at91sam9260_config }, +{ .compatible = "atmel,at91sam9261-i2c", .data = (long)&at91sam9261_config }, +{ .compatible = "atmel,at91sam9g20-i2c", .data = (long)&at91sam9g20_config }, +{ .compatible = "atmel,at91sam9g10-i2c", .data = (long)&at91sam9g10_config }, +{ .compatible = "atmel,at91sam9x5-i2c", .data = (long)&at91sam9x5_config }, +{ .compatible = "atmel,sama5d4-i2c", .data = (long)&sama5d4_config }, +{ .compatible = "atmel,sama5d2-i2c", .data = (long)&sama5d2_config }, +{ } +}; + +U_BOOT_DRIVER(i2c_at91) = { + .name = "i2c_at91", + .id = UCLASS_I2C, + .of_match = at91_i2c_ids, + .ofdata_to_platdata = at91_i2c_ofdata_to_platdata, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .priv_auto_alloc_size = sizeof(struct at91_i2c_bus), + .ops = &at91_i2c_ops, +}; diff --git a/drivers/i2c/at91_i2c.h b/drivers/i2c/at91_i2c.h new file mode 100644 index 0000000..87f02bf --- /dev/null +++ b/drivers/i2c/at91_i2c.h @@ -0,0 +1,77 @@ +#ifndef _AT91_I2C_H +#define _AT91_I2C_H + +#define TWI_CR_START BIT(0) /* Send a Start Condition */ +#define TWI_CR_MSEN BIT(2) /* Master Transfer Enable */ +#define TWI_CR_STOP BIT(1) /* Send a Stop Condition */ +#define TWI_CR_SVDIS BIT(5) /* Slave Transfer Disable */ +#define TWI_CR_SWRST BIT(7) /* Software Reset */ +#define TWI_CR_ACMEN BIT(16) /* Alternative Command Mode Enable */ +#define TWI_CR_ACMDIS BIT(17) /* Alternative Command Mode Disable */ +#define TWI_CR_LOCKCLR BIT(26) /* Lock Clear */ + +#define TWI_MMR_MREAD BIT(12) /* Master Read Direction */ +#define TWI_MMR_IADRSZ_1 BIT(8) /* Internal Device Address Size */ + +#define TWI_SR_TXCOMP BIT(0) /* Transmission Complete */ +#define TWI_SR_RXRDY BIT(1) /* Receive Holding Register Ready */ +#define TWI_SR_TXRDY BIT(2) /* Transmit Holding Register Ready */ +#define TWI_SR_OVRE BIT(6) /* Overrun Error */ +#define TWI_SR_UNRE BIT(7) /* Underrun Error */ +#define TWI_SR_NACK BIT(8) /* Not Acknowledged */ +#define TWI_SR_LOCK BIT(23) /* TWI Lock due to Frame Errors */ + +#define TWI_ACR_DATAL(len) ((len) & 0xff) +#define TWI_ACR_DIR_READ BIT(8) + +#define TWI_CWGR_HOLD_MAX 0x1f +#define TWI_CWGR_HOLD(x) (((x) & TWI_CWGR_HOLD_MAX) << 24) + +struct at91_i2c_regs { + u32 cr; + u32 mmr; + u32 smr; + u32 iadr; + u32 cwgr; + u32 rev_0[3]; + u32 sr; + u32 ier; + u32 idr; + u32 imr; + u32 rhr; + u32 thr; + u32 smbtr; + u32 rev_1; + u32 acr; + u32 filtr; + u32 rev_2; + u32 swmr; + u32 fmr; + u32 flr; + u32 rev_3; + u32 fsr; + u32 fier; + u32 fidr; + u32 fimr; + u32 rev_4[29]; + u32 wpmr; + u32 wpsr; + u32 rev_5[6]; +}; + +struct at91_i2c_pdata { + unsigned clk_max_div; + unsigned clk_offset; +}; + +struct at91_i2c_bus { + struct at91_i2c_regs *regs; + u32 status; + ulong bus_clk_rate; + u32 clock_frequency; + u32 speed; + u32 cwgr_val; + const struct at91_i2c_pdata *pdata; +}; + +#endif

Hello Songjun Wu,
Am 20.06.2016 um 07:22 schrieb Songjun Wu:
Add i2c driver.
Signed-off-by: Songjun Wu songjun.wu@atmel.com
Changes in v3:
- Update the clk API.
Changes in v2:
Add code to get and enable clock.
drivers/i2c/Kconfig | 10 ++ drivers/i2c/Makefile | 1 + drivers/i2c/at91_i2c.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/at91_i2c.h | 77 +++++++++++ 4 files changed, 426 insertions(+) create mode 100644 drivers/i2c/at91_i2c.c create mode 100644 drivers/i2c/at91_i2c.h
Thanks!
Reviewed-by: Heiko Schocher hs@denx.de Acked-by: Heiko Schocher hs@denx.de
bye, Heiko
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 6e22bba..f6a613c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -58,6 +58,16 @@ config DM_I2C_GPIO bindings are supported. Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
+config SYS_I2C_AT91
- bool "Atmel I2C driver"
- depends on DM_I2C && ARCH_AT91
- help
Add support for the Atmel I2C driver. 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 this limitation.
Binding info: doc/device-tree-bindings/i2c/i2c-at91.txt
- config SYS_I2C_FSL bool "Freescale I2C bus driver" depends on DM_I2C
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 167424d..c5362a5 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o obj-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o obj-$(CONFIG_SYS_I2C) += i2c_core.o +obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o diff --git a/drivers/i2c/at91_i2c.c b/drivers/i2c/at91_i2c.c new file mode 100644 index 0000000..8e9c3ad --- /dev/null +++ b/drivers/i2c/at91_i2c.c @@ -0,0 +1,338 @@ +/*
- Atmel I2C driver.
- (C) Copyright 2016 Songjun Wu songjun.wu@atmel.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <asm/io.h> +#include <common.h> +#include <clk_client.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> +#include <linux/bitops.h> +#include <mach/clk.h>
+#include "at91_i2c.h"
+DECLARE_GLOBAL_DATA_PTR;
+#define I2C_TIMEOUT_MS 100
+static int at91_wait_for_xfer(struct at91_i2c_bus *bus, u32 status) +{
- struct at91_i2c_regs *reg = bus->regs;
- ulong start_time = get_timer(0);
- u32 sr;
- bus->status = 0;
- do {
sr = readl(®->sr);
bus->status |= sr;
if (sr & TWI_SR_NACK)
return -EREMOTEIO;
else if (sr & status)
return 0;
- } while (get_timer(start_time) < I2C_TIMEOUT_MS);
- return -ETIMEDOUT;
+}
+static int at91_i2c_xfer_msg(struct at91_i2c_bus *bus, struct i2c_msg *msg) +{
- struct at91_i2c_regs *reg = bus->regs;
- bool is_read = msg->flags & I2C_M_RD;
- u32 i;
- int ret = 0;
- readl(®->sr);
- if (is_read) {
writel(TWI_CR_START, ®->cr);
for (i = 0; !ret && i < (msg->len - 1); i++) {
ret = at91_wait_for_xfer(bus, TWI_SR_RXRDY);
msg->buf[i] = readl(®->rhr);
}
if (ret)
goto error;
writel(TWI_CR_STOP, ®->cr);
ret = at91_wait_for_xfer(bus, TWI_SR_RXRDY);
if (ret)
goto error;
msg->buf[i] = readl(®->rhr);
- } else {
writel(msg->buf[0], ®->thr);
for (i = 1; !ret && (i < msg->len); i++) {
writel(msg->buf[i], ®->thr);
ret = at91_wait_for_xfer(bus, TWI_SR_TXRDY);
}
if (ret)
goto error;
writel(TWI_CR_STOP, ®->cr);
- }
- if (!ret)
ret = at91_wait_for_xfer(bus, TWI_SR_TXCOMP);
- if (ret)
goto error;
- if (bus->status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_LOCK)) {
ret = -EIO;
goto error;
- }
- return 0;
+error:
- if (bus->status & TWI_SR_LOCK)
writel(TWI_CR_LOCKCLR, ®->cr);
- return ret;
+}
+static int at91_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{
- struct at91_i2c_bus *bus = dev_get_priv(dev);
- struct at91_i2c_regs *reg = bus->regs;
- struct i2c_msg *m_start = msg;
- bool is_read;
- u32 int_addr_flag = 0;
- int ret = 0;
- if (nmsgs == 2) {
int internal_address = 0;
int i;
/* 1st msg is put into the internal address, start with 2nd */
m_start = &msg[1];
/* the max length of internal address is 3 bytes */
if (msg->len > 3)
return -EFAULT;
for (i = 0; i < msg->len; ++i) {
const unsigned addr = msg->buf[msg->len - 1 - i];
internal_address |= addr << (8 * i);
int_addr_flag += TWI_MMR_IADRSZ_1;
}
writel(internal_address, ®->iadr);
- }
- is_read = m_start->flags & I2C_M_RD;
- writel((m_start->addr << 16) | int_addr_flag |
(is_read ? TWI_MMR_MREAD : 0), ®->mmr);
- ret = at91_i2c_xfer_msg(bus, m_start);
- return ret;
+}
+/*
- Calculate symmetric clock as stated in datasheet:
- twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset))
- */
+static void at91_calc_i2c_clock(struct udevice *dev, int i2c_clk) +{
- struct at91_i2c_bus *bus = dev_get_priv(dev);
- const struct at91_i2c_pdata *pdata = bus->pdata;
- int offset = pdata->clk_offset;
- int max_ckdiv = pdata->clk_max_div;
- int ckdiv, cdiv, div;
- unsigned long src_rate;
- src_rate = bus->bus_clk_rate;
- div = max(0, (int)DIV_ROUND_UP(src_rate, 2 * i2c_clk) - offset);
- ckdiv = fls(div >> 8);
- cdiv = div >> ckdiv;
- if (ckdiv > max_ckdiv) {
ckdiv = max_ckdiv;
cdiv = 255;
- }
- bus->speed = DIV_ROUND_UP(src_rate,
(cdiv * (1 << ckdiv) + offset) * 2);
- bus->cwgr_val = (ckdiv << 16) | (cdiv << 8) | cdiv;
+}
+static int at91_i2c_enable_clk(struct udevice *dev) +{
- struct at91_i2c_bus *bus = dev_get_priv(dev);
- struct udevice *dev_clk;
- struct clk clk;
- ulong clk_rate;
- int periph;
- int ret;
- ret = clk_get_by_index(dev, 0, &clk);
- if (ret)
return -EINVAL;
- periph = fdtdec_get_uint(gd->fdt_blob, clk.dev->of_offset, "reg", -1);
- if (periph < 0)
return -EINVAL;
- dev_clk = dev_get_parent(clk.dev);
- ret = clk_request(dev_clk, &clk);
- if (ret)
return ret;
- clk.id = periph;
- ret = clk_enable(&clk);
- if (ret)
return ret;
- ret = clk_get_by_index(dev_clk, 0, &clk);
- if (ret)
return ret;
- clk_rate = clk_get_rate(&clk);
- if (!clk_rate)
return -ENODEV;
- bus->bus_clk_rate = clk_rate;
- clk_free(&clk);
- return 0;
+}
+static int at91_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) +{
- struct at91_i2c_bus *bus = dev_get_priv(dev);
- struct at91_i2c_regs *reg = bus->regs;
- int ret;
- ret = at91_i2c_enable_clk(dev);
- if (ret)
return ret;
- writel(TWI_CR_SWRST, ®->cr);
- at91_calc_i2c_clock(dev, bus->clock_frequency);
- writel(bus->cwgr_val, ®->cwgr);
- writel(TWI_CR_MSEN, ®->cr);
- writel(TWI_CR_SVDIS, ®->cr);
- return 0;
+}
+static int at91_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +{
- struct at91_i2c_bus *bus = dev_get_priv(dev);
- at91_calc_i2c_clock(dev, speed);
- writel(bus->cwgr_val, &bus->regs->cwgr);
- return 0;
+}
+int at91_i2c_get_bus_speed(struct udevice *dev) +{
- struct at91_i2c_bus *bus = dev_get_priv(dev);
- return bus->speed;
+}
+static int at91_i2c_ofdata_to_platdata(struct udevice *dev) +{
- const void *blob = gd->fdt_blob;
- struct at91_i2c_bus *bus = dev_get_priv(dev);
- int node = dev->of_offset;
- bus->regs = (struct at91_i2c_regs *)dev_get_addr(dev);
- bus->pdata = (struct at91_i2c_pdata *)dev_get_driver_data(dev);
- bus->clock_frequency = fdtdec_get_int(blob, node,
"clock-frequency", 100000);
- return 0;
+}
+static const struct dm_i2c_ops at91_i2c_ops = {
- .xfer = at91_i2c_xfer,
- .probe_chip = at91_i2c_probe,
- .set_bus_speed = at91_i2c_set_bus_speed,
- .get_bus_speed = at91_i2c_get_bus_speed,
+};
+static const struct at91_i2c_pdata at91rm9200_config = {
- .clk_max_div = 5,
- .clk_offset = 3,
+};
+static const struct at91_i2c_pdata at91sam9261_config = {
- .clk_max_div = 5,
- .clk_offset = 4,
+};
+static const struct at91_i2c_pdata at91sam9260_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
+};
+static const struct at91_i2c_pdata at91sam9g20_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
+};
+static const struct at91_i2c_pdata at91sam9g10_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
+};
+static const struct at91_i2c_pdata at91sam9x5_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
+};
+static const struct at91_i2c_pdata sama5d4_config = {
- .clk_max_div = 7,
- .clk_offset = 4,
+};
+static const struct at91_i2c_pdata sama5d2_config = {
- .clk_max_div = 7,
- .clk_offset = 3,
+};
+static const struct udevice_id at91_i2c_ids[] = { +{ .compatible = "atmel,at91rm9200-i2c", .data = (long)&at91rm9200_config }, +{ .compatible = "atmel,at91sam9260-i2c", .data = (long)&at91sam9260_config }, +{ .compatible = "atmel,at91sam9261-i2c", .data = (long)&at91sam9261_config }, +{ .compatible = "atmel,at91sam9g20-i2c", .data = (long)&at91sam9g20_config }, +{ .compatible = "atmel,at91sam9g10-i2c", .data = (long)&at91sam9g10_config }, +{ .compatible = "atmel,at91sam9x5-i2c", .data = (long)&at91sam9x5_config }, +{ .compatible = "atmel,sama5d4-i2c", .data = (long)&sama5d4_config }, +{ .compatible = "atmel,sama5d2-i2c", .data = (long)&sama5d2_config }, +{ } +};
+U_BOOT_DRIVER(i2c_at91) = {
- .name = "i2c_at91",
- .id = UCLASS_I2C,
- .of_match = at91_i2c_ids,
- .ofdata_to_platdata = at91_i2c_ofdata_to_platdata,
- .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
- .priv_auto_alloc_size = sizeof(struct at91_i2c_bus),
- .ops = &at91_i2c_ops,
+}; diff --git a/drivers/i2c/at91_i2c.h b/drivers/i2c/at91_i2c.h new file mode 100644 index 0000000..87f02bf --- /dev/null +++ b/drivers/i2c/at91_i2c.h @@ -0,0 +1,77 @@ +#ifndef _AT91_I2C_H +#define _AT91_I2C_H
+#define TWI_CR_START BIT(0) /* Send a Start Condition */ +#define TWI_CR_MSEN BIT(2) /* Master Transfer Enable */ +#define TWI_CR_STOP BIT(1) /* Send a Stop Condition */ +#define TWI_CR_SVDIS BIT(5) /* Slave Transfer Disable */ +#define TWI_CR_SWRST BIT(7) /* Software Reset */ +#define TWI_CR_ACMEN BIT(16) /* Alternative Command Mode Enable */ +#define TWI_CR_ACMDIS BIT(17) /* Alternative Command Mode Disable */ +#define TWI_CR_LOCKCLR BIT(26) /* Lock Clear */
+#define TWI_MMR_MREAD BIT(12) /* Master Read Direction */ +#define TWI_MMR_IADRSZ_1 BIT(8) /* Internal Device Address Size */
+#define TWI_SR_TXCOMP BIT(0) /* Transmission Complete */ +#define TWI_SR_RXRDY BIT(1) /* Receive Holding Register Ready */ +#define TWI_SR_TXRDY BIT(2) /* Transmit Holding Register Ready */ +#define TWI_SR_OVRE BIT(6) /* Overrun Error */ +#define TWI_SR_UNRE BIT(7) /* Underrun Error */ +#define TWI_SR_NACK BIT(8) /* Not Acknowledged */ +#define TWI_SR_LOCK BIT(23) /* TWI Lock due to Frame Errors */
+#define TWI_ACR_DATAL(len) ((len) & 0xff) +#define TWI_ACR_DIR_READ BIT(8)
+#define TWI_CWGR_HOLD_MAX 0x1f +#define TWI_CWGR_HOLD(x) (((x) & TWI_CWGR_HOLD_MAX) << 24)
+struct at91_i2c_regs {
- u32 cr;
- u32 mmr;
- u32 smr;
- u32 iadr;
- u32 cwgr;
- u32 rev_0[3];
- u32 sr;
- u32 ier;
- u32 idr;
- u32 imr;
- u32 rhr;
- u32 thr;
- u32 smbtr;
- u32 rev_1;
- u32 acr;
- u32 filtr;
- u32 rev_2;
- u32 swmr;
- u32 fmr;
- u32 flr;
- u32 rev_3;
- u32 fsr;
- u32 fier;
- u32 fidr;
- u32 fimr;
- u32 rev_4[29];
- u32 wpmr;
- u32 wpsr;
- u32 rev_5[6];
+};
+struct at91_i2c_pdata {
- unsigned clk_max_div;
- unsigned clk_offset;
+};
+struct at91_i2c_bus {
- struct at91_i2c_regs *regs;
- u32 status;
- ulong bus_clk_rate;
- u32 clock_frequency;
- u32 speed;
- u32 cwgr_val;
- const struct at91_i2c_pdata *pdata;
+};
+#endif

Dear Songjun Wu,
Songjun Wu songjun.wu@atmel.com writes:
Add i2c driver.
Signed-off-by: Songjun Wu songjun.wu@atmel.com Reviewed-by: Heiko Schocher hs@denx.de Acked-by: Heiko Schocher hs@denx.de
Changes in v3:
- Update the clk API.
Changes in v2:
- Add code to get and enable clock.
drivers/i2c/Kconfig | 10 ++ drivers/i2c/Makefile | 1 + drivers/i2c/at91_i2c.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/at91_i2c.h | 77 +++++++++++ 4 files changed, 426 insertions(+) create mode 100644 drivers/i2c/at91_i2c.c create mode 100644 drivers/i2c/at91_i2c.h
applied to u-boot-atmel/master, thanks!
Best regards, Andreas Bießmann

DT binding documentation for atmel i2c driver.
Signed-off-by: Songjun Wu songjun.wu@atmel.com ---
Changes in v3: None Changes in v2: - Add phandles to input clocks
doc/device-tree-bindings/i2c/i2c-at91.txt | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-at91.txt
diff --git a/doc/device-tree-bindings/i2c/i2c-at91.txt b/doc/device-tree-bindings/i2c/i2c-at91.txt new file mode 100644 index 0000000..2065b73 --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c-at91.txt @@ -0,0 +1,26 @@ +I2C for Atmel platforms + +Required properties : +- compatible : Must be "atmel,at91rm9200-i2c", "atmel,at91sam9261-i2c", + "atmel,at91sam9260-i2c", "atmel,at91sam9g20-i2c", "atmel,at91sam9g10-i2c", + "atmel,at91sam9x5-i2c", "atmel,sama5d4-i2c" or "atmel,sama5d2-i2c". +- reg: physical base address of the controller and length of memory mapped + region. +- #address-cells = <1>; +- #size-cells = <0>; +- clocks: phandles to input clocks. + +Optional properties: +- clock-frequency: Desired I2C bus frequency in Hz, default value is 100000. +- Child nodes conforming to i2c bus binding. + +Examples : + +i2c0: i2c@f8028000 { + compatible = "atmel,sama5d2-i2c"; + reg = <0xf8028000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&twi0_clk>; + clock-frequency = <100000>; +};

Hello Songjun Wu,
Am 20.06.2016 um 07:22 schrieb Songjun Wu:
DT binding documentation for atmel i2c driver.
Signed-off-by: Songjun Wu songjun.wu@atmel.com
Changes in v3: None Changes in v2:
Add phandles to input clocks
doc/device-tree-bindings/i2c/i2c-at91.txt | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-at91.txt
Thanks!
Reviewed-by: Heiko Schocher hs@denx.de Acked-by: Heiko Schocher hs@denx.de
bye, Heiko
diff --git a/doc/device-tree-bindings/i2c/i2c-at91.txt b/doc/device-tree-bindings/i2c/i2c-at91.txt new file mode 100644 index 0000000..2065b73 --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c-at91.txt @@ -0,0 +1,26 @@ +I2C for Atmel platforms
+Required properties : +- compatible : Must be "atmel,at91rm9200-i2c", "atmel,at91sam9261-i2c",
"atmel,at91sam9260-i2c", "atmel,at91sam9g20-i2c", "atmel,at91sam9g10-i2c",
"atmel,at91sam9x5-i2c", "atmel,sama5d4-i2c" or "atmel,sama5d2-i2c".
+- reg: physical base address of the controller and length of memory mapped
region.
+- #address-cells = <1>; +- #size-cells = <0>; +- clocks: phandles to input clocks.
+Optional properties: +- clock-frequency: Desired I2C bus frequency in Hz, default value is 100000. +- Child nodes conforming to i2c bus binding.
+Examples :
+i2c0: i2c@f8028000 {
- compatible = "atmel,sama5d2-i2c";
- reg = <0xf8028000 0x100>;
- #address-cells = <1>;
- #size-cells = <0>;
- clocks = <&twi0_clk>;
- clock-frequency = <100000>;
+};

Dear Songjun Wu,
Songjun Wu songjun.wu@atmel.com writes:
DT binding documentation for atmel i2c driver.
Signed-off-by: Songjun Wu songjun.wu@atmel.com Reviewed-by: Heiko Schocher hs@denx.de Acked-by: Heiko Schocher hs@denx.de
Changes in v3: None Changes in v2:
- Add phandles to input clocks
doc/device-tree-bindings/i2c/i2c-at91.txt | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-at91.txt
applied to u-boot-atmel/master, thanks!
Best regards, Andreas Bießmann
participants (3)
-
Andreas Bießmann
-
Heiko Schocher invitel
-
Songjun Wu