[U-Boot] [PATCH] i2c:zynq: I2C multi-bus support on Zynq
Michael Burr
michael.burr at logicpd.com
Tue Sep 17 19:29:40 CEST 2013
Zynq PS has two I2C bus masters (called 'I2C0' and 'I2C1'):
> Implement 'i2c_set_bus_num' routine to select from these.
Support I2C devices which do not use internal addresses:
> Handle case of 'alen == 0' in 'i2c_read', 'i2c_write'.
This supports PCA9548 bus multiplexer on Xilinx ZC702 board.
Tested cases of 'alen == 0' and 'alen == 1' on this board.
Further minor corrections:
> Write 'address' register before 'data' register.
> Write 'transfer_size' register before 'address' register.
Signed-off-by: Michael Burr <michael.burr at logicpd.com>
Cc: Albert Aribaud <albert.u.boot at aribaud.net>
Cc: Heiko Schocher <hs at denx.de>
Cc: Michal Simek <monstr at monstr.eu>
---
drivers/i2c/zynq_i2c.c | 87 ++++++++++++++++++++++++++++++------------------
1 file changed, 55 insertions(+), 32 deletions(-)
diff --git a/drivers/i2c/zynq_i2c.c b/drivers/i2c/zynq_i2c.c
index ce2d23f..1c9ae30 100644
--- a/drivers/i2c/zynq_i2c.c
+++ b/drivers/i2c/zynq_i2c.c
@@ -7,7 +7,7 @@
*
* Copyright (c) 2012-2013 Xilinx, Michal Simek
*
- * SPDX-License-Identifier: GPL-2.0+
+ * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
@@ -64,18 +64,19 @@ struct zynq_i2c_registers {
#define ZYNQ_I2C_FIFO_DEPTH 16
#define ZYNQ_I2C_TRANSFERT_SIZE_MAX 255 /* Controller transfer limit */
-#if defined(CONFIG_ZYNQ_I2C0)
-# define ZYNQ_I2C_BASE ZYNQ_I2C_BASEADDR0
-#else
-# define ZYNQ_I2C_BASE ZYNQ_I2C_BASEADDR1
+#ifdef CONFIG_I2C_MULTI_BUS
+static unsigned int current_bus;
#endif
-
-static struct zynq_i2c_registers *zynq_i2c =
- (struct zynq_i2c_registers *)ZYNQ_I2C_BASE;
+static struct zynq_i2c_registers *zynq_i2c;
/* I2C init called by cmd_i2c when doing 'i2c reset'. */
void i2c_init(int requested_speed, int slaveadd)
{
+#ifdef CONFIG_I2C_MULTI_BUS
+ current_bus = 0;
+#endif
+ zynq_i2c = (struct zynq_i2c_registers *)ZYNQ_I2C_BASEADDR0;
+
/* 111MHz / ( (3 * 17) * 22 ) = ~100KHz */
writel((16 << ZYNQ_I2C_CONTROL_DIV_B_SHIFT) |
(2 << ZYNQ_I2C_CONTROL_DIV_A_SHIFT), &zynq_i2c->control);
@@ -187,26 +188,29 @@ int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length)
* Temporarily disable restart (by clearing hold)
* It doesn't seem to work.
*/
- clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW |
- ZYNQ_I2C_CONTROL_HOLD);
+ clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
+
writel(0xFF, &zynq_i2c->interrupt_status);
- while (alen--)
- writel(addr >> (8*alen), &zynq_i2c->data);
- writel(dev, &zynq_i2c->address);
+ if (alen) {
+ clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW);
+ writel(dev, &zynq_i2c->address);
+ while (alen--)
+ writel(addr >> (8*alen), &zynq_i2c->data);
- /* Wait for the address to be sent */
- if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) {
- /* Release the bus */
- clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
- return -ETIMEDOUT;
+ /* Wait for the address to be sent */
+ if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) {
+ /* Release the bus */
+ clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
+ return -ETIMEDOUT;
+ }
+ debug("Device acked address\n");
}
- debug("Device acked address\n");
setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO |
ZYNQ_I2C_CONTROL_RW);
/* Start reading data */
- writel(dev, &zynq_i2c->address);
writel(length, &zynq_i2c->transfer_size);
+ writel(dev, &zynq_i2c->address);
/* Wait for data */
do {
@@ -244,17 +248,18 @@ int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length)
ZYNQ_I2C_CONTROL_HOLD);
clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW);
writel(0xFF, &zynq_i2c->interrupt_status);
- while (alen--)
- writel(addr >> (8*alen), &zynq_i2c->data);
- /* Start the tranfer */
writel(dev, &zynq_i2c->address);
- if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) {
- /* Release the bus */
- clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
- return -ETIMEDOUT;
+ if (alen) {
+ while (alen--)
+ writel(addr >> (8*alen), &zynq_i2c->data);
+ /* Start the tranfer */
+ if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) {
+ /* Release the bus */
+ clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD);
+ return -ETIMEDOUT;
+ }
+ debug("Device acked address\n");
}
-
- debug("Device acked address\n");
while (length--) {
writel(*(cur_data++), &zynq_i2c->data);
if (readl(&zynq_i2c->transfer_size) == ZYNQ_I2C_FIFO_DEPTH) {
@@ -277,14 +282,32 @@ int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length)
int i2c_set_bus_num(unsigned int bus)
{
- /* Only support bus 0 */
- if (bus > 0)
+ if (bus >= CONFIG_SYS_MAX_I2C_BUS)
return -1;
+
+ switch (bus) {
+ case 0:
+ zynq_i2c = (struct zynq_i2c_registers *)ZYNQ_I2C_BASEADDR0;
+ break;
+#ifdef CONFIG_I2C_MULTI_BUS
+ case 1:
+ zynq_i2c = (struct zynq_i2c_registers *)ZYNQ_I2C_BASEADDR1;
+ break;
+#endif
+ default:
+ return -1;
+ }
+#ifdef CONFIG_I2C_MULTI_BUS
+ current_bus = bus;
+#endif
return 0;
}
unsigned int i2c_get_bus_num(void)
{
- /* Only support bus 0 */
+#ifdef CONFIG_I2C_MULTI_BUS
+ return current_bus;
+#else
return 0;
+#endif
}
--
1.7.9.5
More information about the U-Boot
mailing list