From patchwork Tue Sep 17 17:29:40 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Burr X-Patchwork-Id: 275517 X-Patchwork-Delegate: hs@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id ADEF52C00CF for ; Wed, 18 Sep 2013 04:31:38 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 7220C4A11C; Tue, 17 Sep 2013 20:31:33 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id e8k0MYe3E+dY; Tue, 17 Sep 2013 20:31:33 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 9F1E34A0FC; Tue, 17 Sep 2013 20:31:27 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id EA43E4A0F9 for ; Tue, 17 Sep 2013 19:30:04 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id c5tqzWPw1auc for ; Tue, 17 Sep 2013 19:29:59 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from barracuda.logicpd.com (smtp.logicpd.com [174.46.170.145]) by theia.denx.de (Postfix) with SMTP id 400E74A0F5 for ; Tue, 17 Sep 2013 19:29:52 +0200 (CEST) X-ASG-Debug-ID: 1379438981-0388000df93b4d50001-4l7tJC Received: from EDPR-EX01.logicpd.com (edpr-ex01.logicpd.com [10.1.18.124]) by barracuda.logicpd.com with ESMTP id ocZaCb6CLW1yzdVe (version=TLSv1 cipher=AES128-SHA bits=128 verify=NO) for ; Tue, 17 Sep 2013 12:29:41 -0500 (CDT) X-Barracuda-Envelope-From: michael.burr@logicpd.com Received: from EDPR-EX01.logicpd.com ([169.254.2.63]) by EDPR-EX01.logicpd.com ([169.254.2.35]) with mapi id 14.02.0342.003; Tue, 17 Sep 2013 12:29:40 -0500 From: Michael Burr To: "u-boot@lists.denx.de" Thread-Topic: [PATCH] i2c:zynq: I2C multi-bus support on Zynq X-ASG-Orig-Subj: [PATCH] i2c:zynq: I2C multi-bus support on Zynq Thread-Index: Ac6zv2ZbQ3VShCKaS3mYh4/BcpfLdA== Date: Tue, 17 Sep 2013 17:29:40 +0000 Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.0.5.74] MIME-Version: 1.0 X-Barracuda-Connect: edpr-ex01.logicpd.com[10.1.18.124] X-Barracuda-Start-Time: 1379438981 X-Barracuda-Encrypted: AES128-SHA X-Barracuda-URL: http://ep-cuda.logicpd.com:8000/cgi-mod/mark.cgi X-Virus-Scanned: by bsmtpd at logicpd.com X-Barracuda-BRTS-Status: 1 X-Barracuda-Spam-Score: 0.00 X-Barracuda-Spam-Status: No, SCORE=0.00 using global scores of TAG_LEVEL=1000.0 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=9.0 tests= X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.2.140698 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- X-Mailman-Approved-At: Tue, 17 Sep 2013 20:31:26 +0200 Subject: [U-Boot] [PATCH] i2c:zynq: I2C multi-bus support on Zynq X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de 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 Cc: Albert Aribaud Cc: Heiko Schocher Cc: Michal Simek --- 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 @@ -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 }