From patchwork Mon Apr 22 14:52:11 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Simek X-Patchwork-Id: 238560 X-Patchwork-Delegate: trini@ti.com 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 87C642C011E for ; Tue, 23 Apr 2013 00:55:16 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B65F44A529; Mon, 22 Apr 2013 16:54:23 +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 lYfuGVLodmfz; Mon, 22 Apr 2013 16:54:23 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 7BD884A3B0; Mon, 22 Apr 2013 16:53:33 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 097914A295 for ; Mon, 22 Apr 2013 16:53:15 +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 RpUkaj1xIhxg for ; Mon, 22 Apr 2013 16:53:13 +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 mail-wi0-f181.google.com (mail-wi0-f181.google.com [209.85.212.181]) by theia.denx.de (Postfix) with ESMTPS id 0F7724A2AC for ; Mon, 22 Apr 2013 16:52:51 +0200 (CEST) Received: by mail-wi0-f181.google.com with SMTP id c10so4541017wiw.2 for ; Mon, 22 Apr 2013 07:52:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:sender:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:in-reply-to:references:content-type :x-gm-message-state; bh=GE7wBqJxsXTobpef5Q+ITd4Hb9tFdwOezNNGe0Ylff4=; b=bL+8iTv+RnSsDXPhpfw3t7OFKblRK1Sfy9rXWfVJeHcr9FutA2pFCia6IhqsiY2qk9 yiaQLRhJIJufyhlPM6SpsvGEIcwTdKrxe+rMkpk+6heYR9SwkzTP4GA1EG1DpIdfPayt dAlo4qnLiCzMvsWH1EAX1St5T5mTOmtX7HsF2OmjftH3msAln2kmtIqe2keCw+63FeAi LlxwiEQoYL3F//r05P/ZwSXv9CRxddH4eDFJd7GP0ST7H1jFGpn3uj5g+Socyc+5vWCf rcelnJjncQebER//s5TuDKlt4iWTIiV6DDF1BAvU5415y2XAfO/gPxFLWBh2jWBx50QP 4XDg== X-Received: by 10.194.177.225 with SMTP id ct1mr14485547wjc.16.1366642370710; Mon, 22 Apr 2013 07:52:50 -0700 (PDT) Received: from localhost (nat-63.starnet.cz. [178.255.168.63]) by mx.google.com with ESMTPSA id n2sm20493724wiy.6.2013.04.22.07.52.49 for (version=TLSv1.1 cipher=RC4-SHA bits=128/128); Mon, 22 Apr 2013 07:52:50 -0700 (PDT) From: Michal Simek To: u-boot@lists.denx.de, Albert Aribaud Date: Mon, 22 Apr 2013 16:52:11 +0200 Message-Id: X-Mailer: git-send-email 1.8.2.1 In-Reply-To: <1366642332-11185-1-git-send-email-michal.simek@xilinx.com> References: <1366642332-11185-1-git-send-email-michal.simek@xilinx.com> In-Reply-To: <98a6e4d33e61c56c76b9bd78ef5d8e74ebd1510c.1366641836.git.michal.simek@xilinx.com> References: <98a6e4d33e61c56c76b9bd78ef5d8e74ebd1510c.1366641836.git.michal.simek@xilinx.com> X-Gm-Message-State: ALoCoQk5Kf8KtubDdIZVjO13qy2oTtv5iTWZi7QVXP+1zxdyRYONdhFgv+2Ccl+F19dzgcU2dtpv Cc: Peter Crosthwaite , Jagannadha Sutradharudu Teki Subject: [U-Boot] [PATCH 13/14] i2c: zynq: Add support for Xilinx 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: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Support Xilinx Zynq i2c controller. Signed-off-by: Joe Hershberger Signed-off-by: Michal Simek --- arch/arm/include/asm/arch-zynq/hardware.h | 2 + drivers/i2c/Makefile | 1 + drivers/i2c/zynq_i2c.c | 304 ++++++++++++++++++++++++++++++ include/configs/zynq.h | 11 ++ 4 files changed, 318 insertions(+) create mode 100644 drivers/i2c/zynq_i2c.c -- 1.8.2.1 diff --git a/arch/arm/include/asm/arch-zynq/hardware.h b/arch/arm/include/asm/arch-zynq/hardware.h index 3fa94d7..901a74c 100644 --- a/arch/arm/include/asm/arch-zynq/hardware.h +++ b/arch/arm/include/asm/arch-zynq/hardware.h @@ -31,6 +31,8 @@ #define CONFIG_ZYNQ_GEM_BASEADDR1 0xE000C000 #define CONFIG_ZYNQ_SDHCI_BASEADDR0 0xE0100000 #define CONFIG_ZYNQ_SDHCI_BASEADDR1 0xE0101000 +#define CONFIG_ZYNQ_I2C_BASEADDR0 0xE0004000 +#define CONFIG_ZYNQ_I2C_BASEADDR1 0xE0005000 /* Reflect slcr offsets */ struct slcr_regs { diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 5dbdbe3..72e85a3 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -46,6 +46,7 @@ COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o COBJS-$(CONFIG_SH_I2C) += sh_i2c.o COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o +COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/i2c/zynq_i2c.c b/drivers/i2c/zynq_i2c.c new file mode 100644 index 0000000..6ccf41f --- /dev/null +++ b/drivers/i2c/zynq_i2c.c @@ -0,0 +1,304 @@ +/* + * Driver for the Zynq-7000 PS I2C controller + * IP from Cadence (ID T-CS-PE-0007-100, Version R1p10f2) + * + * Author: Joe Hershberger + * Copyright (c) 2012 Joe Hershberger. + * + * Copyright (c) 2012-2013 Xilinx, Michal Simek + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +/* i2c register set */ +struct zynq_i2c_registers { + u32 control; + u32 status; + u32 address; + u32 data; + u32 interrupt_status; + u32 transfer_size; + u32 slave_mon_pause; + u32 time_out; + u32 interrupt_mask; + u32 interrupt_enable; + u32 interrupt_disable; +}; + +/* Control register fields */ +#define ZYNQ_I2C_CONTROL_RW 0x00000001 +#define ZYNQ_I2C_CONTROL_MS 0x00000002 +#define ZYNQ_I2C_CONTROL_NEA 0x00000004 +#define ZYNQ_I2C_CONTROL_ACKEN 0x00000008 +#define ZYNQ_I2C_CONTROL_HOLD 0x00000010 +#define ZYNQ_I2C_CONTROL_SLVMON 0x00000020 +#define ZYNQ_I2C_CONTROL_CLR_FIFO 0x00000040 +#define ZYNQ_I2C_CONTROL_DIV_B_SHIFT 8 +#define ZYNQ_I2C_CONTROL_DIV_B_MASK 0x00003F00 +#define ZYNQ_I2C_CONTROL_DIV_A_SHIFT 14 +#define ZYNQ_I2C_CONTROL_DIV_A_MASK 0x0000C000 + +/* Status register values */ +#define ZYNQ_I2C_STATUS_RXDV 0x00000020 +#define ZYNQ_I2C_STATUS_TXDV 0x00000040 +#define ZYNQ_I2C_STATUS_RXOVF 0x00000080 +#define ZYNQ_I2C_STATUS_BA 0x00000100 + +/* Interrupt register fields */ +#define ZYNQ_I2C_INTERRUPT_COMP 0x00000001 +#define ZYNQ_I2C_INTERRUPT_DATA 0x00000002 +#define ZYNQ_I2C_INTERRUPT_NACK 0x00000004 +#define ZYNQ_I2C_INTERRUPT_TO 0x00000008 +#define ZYNQ_I2C_INTERRUPT_SLVRDY 0x00000010 +#define ZYNQ_I2C_INTERRUPT_RXOVF 0x00000020 +#define ZYNQ_I2C_INTERRUPT_TXOVF 0x00000040 +#define ZYNQ_I2C_INTERRUPT_RXUNF 0x00000080 +#define ZYNQ_I2C_INTERRUPT_ARBLOST 0x00000200 + +#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 CONFIG_ZYNQ_I2C_BASEADDR0 +#else +# define ZYNQ_I2C_BASE CONFIG_ZYNQ_I2C_BASEADDR1 +#endif + +static struct zynq_i2c_registers *zynq_i2c = + (struct zynq_i2c_registers *) ZYNQ_I2C_BASE; + +/* I2C init called by cmd_i2c when doing 'i2c reset'. */ +void i2c_init(int requested_speed, int slaveadd) +{ + /* 111MHz / ( (3 * 17) * 22 ) = ~100KHz */ + writel((16 << ZYNQ_I2C_CONTROL_DIV_B_SHIFT) | + (2 << ZYNQ_I2C_CONTROL_DIV_A_SHIFT), &zynq_i2c->control); + + /* Enable master mode, ack, and 7-bit addressing */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_MS | + ZYNQ_I2C_CONTROL_ACKEN | ZYNQ_I2C_CONTROL_NEA); +} + +#ifdef DEBUG +static void zynq_i2c_debug_status(void) +{ + int int_status; + int status; + int_status = readl(&zynq_i2c->interrupt_status); + + status = readl(&zynq_i2c->status); + if (int_status || status) { + debug("Status: "); + if (int_status & ZYNQ_I2C_INTERRUPT_COMP) + debug("COMP "); + if (int_status & ZYNQ_I2C_INTERRUPT_DATA) + debug("DATA "); + if (int_status & ZYNQ_I2C_INTERRUPT_NACK) + debug("NACK "); + if (int_status & ZYNQ_I2C_INTERRUPT_TO) + debug("TO "); + if (int_status & ZYNQ_I2C_INTERRUPT_SLVRDY) + debug("SLVRDY "); + if (int_status & ZYNQ_I2C_INTERRUPT_RXOVF) + debug("RXOVF "); + if (int_status & ZYNQ_I2C_INTERRUPT_TXOVF) + debug("TXOVF "); + if (int_status & ZYNQ_I2C_INTERRUPT_RXUNF) + debug("RXUNF "); + if (int_status & ZYNQ_I2C_INTERRUPT_ARBLOST) + debug("ARBLOST "); + if (status & ZYNQ_I2C_STATUS_RXDV) + debug("RXDV "); + if (status & ZYNQ_I2C_STATUS_TXDV) + debug("TXDV "); + if (status & ZYNQ_I2C_STATUS_RXOVF) + debug("RXOVF "); + if (status & ZYNQ_I2C_STATUS_BA) + debug("BA "); + debug("TS%d ", readl(&zynq_i2c->transfer_size)); + debug("\n"); + } +} +#endif + +/* Wait for an interrupt */ +static u32 zynq_i2c_wait(u32 mask) +{ + int timeout, int_status; + + for (timeout = 0; timeout < 100; timeout++) { + udelay(100); + int_status = readl(&zynq_i2c->interrupt_status); + if (int_status & mask) + break; + } +#ifdef DEBUG + zynq_i2c_debug_status(); +#endif + /* Clear interrupt status flags */ + writel(int_status & mask, &zynq_i2c->interrupt_status); + + return int_status & mask; +} + +/* + * I2C probe called by cmd_i2c when doing 'i2c probe'. + * Begin read, nak data byte, end. + */ +int i2c_probe(u8 dev) +{ + /* Attempt to read a byte */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_RW); + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + writel(0xFF, &zynq_i2c->interrupt_status); + writel(dev, &zynq_i2c->address); + writel(1, &zynq_i2c->transfer_size); + + return (zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP | + ZYNQ_I2C_INTERRUPT_NACK) & + ZYNQ_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT; +} + +/* + * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c + * Begin write, send address byte(s), begin read, receive data bytes, end. + */ +int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) +{ + u32 status; + u32 i = 0; + u8 *cur_data = data; + + /* check the hardware can handle the requested bytes */ + if ((length < 0) || (length > ZYNQ_I2C_TRANSFERT_SIZE_MAX)) + return -EINVAL; + + /* Write the register address */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_HOLD); + /* 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); + writel(0xFF, &zynq_i2c->interrupt_status); + while (alen--) + writel(addr >> (8*alen), &zynq_i2c->data); + writel(dev, &zynq_i2c->address); + + /* 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"); + + 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); + + /* wait for data */ + do { + status = zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP | + ZYNQ_I2C_INTERRUPT_DATA); + if (!status) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + debug("Read %d bytes\n", + length - readl(&zynq_i2c->transfer_size)); + for (; i < length - readl(&zynq_i2c->transfer_size); i++) + *(cur_data++) = readl(&zynq_i2c->data); + } while (readl(&zynq_i2c->transfer_size) != 0); + /* All done... release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + +#ifdef DEBUG + zynq_i2c_debug_status(); +#endif + return 0; +} + +/* + * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c + * Begin write, send address byte(s), send data bytes, end. + */ +int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) +{ + u8 *cur_data = data; + + /* Write the register address */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + 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; + } + + debug("Device acked address\n"); + while (length--) { + writel(*(cur_data++), &zynq_i2c->data); + if (readl(&zynq_i2c->transfer_size) == ZYNQ_I2C_FIFO_DEPTH) { + if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, + ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + } + } + + /* All done... release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + /* wait for the address and data to be sent */ + if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) + return -ETIMEDOUT; + return 0; +} + +int i2c_set_bus_num(unsigned int bus) +{ + /* Only support bus 0 */ + if (bus > 0) + return -1; + return 0; +} + +unsigned int i2c_get_bus_num(void) +{ + /* Only support bus 0 */ + return 0; +} diff --git a/include/configs/zynq.h b/include/configs/zynq.h index 2ed88a7..6877a68 100644 --- a/include/configs/zynq.h +++ b/include/configs/zynq.h @@ -72,6 +72,17 @@ # define CONFIG_DOS_PARTITION #endif +#define CONFIG_ZYNQ_I2C0 + +/* I2C */ +#if defined(CONFIG_ZYNQ_I2C0) || defined(CONFIG_ZYNQ_I2C1) +# define CONFIG_CMD_I2C +# define CONFIG_ZYNQ_I2C +# define CONFIG_HARD_I2C 1 +# define CONFIG_SYS_I2C_SPEED 100000 +# define CONFIG_SYS_I2C_SLAVE 1 +#endif + #if defined(CONFIG_ZYNQ_DCC) # define CONFIG_ARM_DCC # define CONFIG_CPU_V6 /* Required by CONFIG_ARM_DCC */