From patchwork Tue Jun 19 06:45:17 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rajeshwari Shinde X-Patchwork-Id: 9433 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 2B65823F28 for ; Tue, 19 Jun 2012 06:41:10 +0000 (UTC) Received: from mail-yx0-f180.google.com (mail-yx0-f180.google.com [209.85.213.180]) by fiordland.canonical.com (Postfix) with ESMTP id D8E6AA189DC for ; Tue, 19 Jun 2012 06:41:09 +0000 (UTC) Received: by mail-yx0-f180.google.com with SMTP id q6so4856436yen.11 for ; Mon, 18 Jun 2012 23:41:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf:x-auditid :from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :x-brightmail-tracker:x-tm-as-mml:x-gm-message-state; bh=B3eSTg2fAgloFA2pwx8T2MXvXxV1Pjs4I6GA9C34G50=; b=U9peI8626SLD2Mv/paA7MetpC+E2MG5XyxGur6sDf9XpC3Z26t+DrHWpNWKqPr2d87 lKVYnruU0PxvVbui8kjOKry2Lkdk2FARr44u7SmdawtOusVofGpPjUTE/WxM7Jkr3uJb /Du7SDAJXbznX9JAGHggQIB194otnEz2gywOmyrkWOKyzK7rRBEd9u8cyyKpKCvAHDJ/ WEUYXWrRciBH2E4RMdLuPGPKHpqJYgQevKEOZ+Tf4Ly0O/vJ6Zy/S4bk4BCt6V++EtI9 9I9A7F25kZMCPKa5yx5GJLg29SNluMzkP0BjVKiDm+H8Zv2X0TKpXtA+E4gEOBwd7aWd UQQg== Received: by 10.50.40.193 with SMTP id z1mr181092igk.0.1340088069276; Mon, 18 Jun 2012 23:41:09 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.231.24.148 with SMTP id v20csp109113ibb; Mon, 18 Jun 2012 23:41:08 -0700 (PDT) Received: by 10.68.194.201 with SMTP id hy9mr60514226pbc.69.1340088068462; Mon, 18 Jun 2012 23:41:08 -0700 (PDT) Received: from mailout1.samsung.com (mailout1.samsung.com. [203.254.224.24]) by mx.google.com with ESMTP id hc10si31320656pbc.241.2012.06.18.23.41.07; Mon, 18 Jun 2012 23:41:08 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of rajeshwari.s@samsung.com designates 203.254.224.24 as permitted sender) client-ip=203.254.224.24; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of rajeshwari.s@samsung.com designates 203.254.224.24 as permitted sender) smtp.mail=rajeshwari.s@samsung.com Received: from epcpsbgm1.samsung.com (mailout1.samsung.com [203.254.224.24]) by mailout1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0M5U00M9XQEDLQ00@mailout1.samsung.com>; Tue, 19 Jun 2012 15:41:07 +0900 (KST) X-AuditID: cbfee61a-b7f9f6d0000016a8-ee-4fe01f02fbb6 Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 13.16.05800.20F10EF4; Tue, 19 Jun 2012 15:41:06 +0900 (KST) Received: from rajeshwari-linux.sisodomain.com ([107.108.215.115]) by mmp2.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0M5U00BUEQJL3P50@mmp2.samsung.com>; Tue, 19 Jun 2012 15:41:06 +0900 (KST) From: Rajeshwari Shinde To: u-boot@lists.denx.de Cc: patches@linaro.org, alim.akhtar@samsung.com, sjg@chromium.org, hs@denx.de, mk7.kang@samsung.com, chander.kashyap@linaro.org, dofmind@gmail.com Subject: [PATCH 6/8 V3] I2C: Modify the I2C driver for EXYNOS5 Date: Tue, 19 Jun 2012 12:15:17 +0530 Message-id: <1340088319-10072-7-git-send-email-rajeshwari.s@samsung.com> X-Mailer: git-send-email 1.7.4.4 In-reply-to: <1340088319-10072-1-git-send-email-rajeshwari.s@samsung.com> References: <1340088319-10072-1-git-send-email-rajeshwari.s@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrKJMWRmVeSWpSXmKPExsVy+t9jQV0m+Qf+BtuPm1g8XH+TxWLK4S8s Dkwed67tYQtgjOKySUnNySxLLdK3S+DKeHa8vmB2XsXDmcdZGhhXhXYxcnJICJhINL79wARh i0lcuLeeDcQWEpjOKNE3JwbCnsgkcfOxK4jNJmAksfXkNEYQW0RAQuJX/1Uwm1lgCqPErtVg M4UF7CS+f1vPDmKzCKhKTPl8ggXE5hXwkNh0/hcjxC4FiWNTv7KC2JwCnhKbPixkgtjlIbHn xXn2CYy8CxgZVjGKphYkFxQnpeca6hUn5haX5qXrJefnbmIEe/6Z1A7GlQ0WhxgFOBiVeHgP yj3wF2JNLCuuzD3EKMHBrCTCu+r/fX8h3pTEyqrUovz4otKc1OJDjNIcLErivE3WF/yFBNIT S1KzU1MLUotgskwcnFLA0DFSnhl37O2hjX0mC3m+qGXZSL39F5p7r8N+e9hiywkprmt6Opsm sUzLXyb2dv9BU5OUphsPOpYLKqn3el5LuGaS7bWKY23z2z/Lf858xHOct/6h5JFn7kV/uVu2 Vs7U8tq0Y8IXr/dPbQyeP80Llc74Plc4WXv3gbOhnDNzvu8KmvTB7PmhQiWW4oxEQy3mouJE AC+30ff4AQAA X-TM-AS-MML: No X-Gm-Message-State: ALoCoQnf9/t9F2NDD9iRA0DS6KciviXG+lG65W8sUXue/CQGfhc1DGHo3hPOQuSEHLuk4NTi+c7E This patch modifies the S3C I2C driver to suppport EXYNOS5. The cahnges made to driver are as follows: - I2C base address is passed as a parameter to many functions to avoid multiple #ifdef - I2C init for Exynos5 is made as different function. - Channel initialisation is moved to a commom funation as it is required by both the i2c_init. - Separate functions written to get I2C base address, peripheral id for pinmux support. - Hardcoding for I2CCON_ACKGEN removed. - Replaced printf with debug. - Checkpatch issues resolved. Signed-off-by: Alim Akhtar Signed-off-by: Doug Anderson Signed-off-by: Rajeshwari Shinde Acked-by: Simon Glass --- Changes in V2: - Removed #define for I2C cahnnels from hearder file except for I2C0. - Incorporated review comments from Simon Glass. Changes in V3: - Incorporated review comments from Joonyoung Shim. - Reduced the number of #ifdef by modifying get_i2c_base function. - Removed duplicate code. drivers/i2c/s3c24x0_i2c.c | 243 +++++++++++++++++++++++++++++++-------------- drivers/i2c/s3c24x0_i2c.h | 3 + 2 files changed, 172 insertions(+), 74 deletions(-) diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index ba6f39b..156b5a4 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -27,10 +27,17 @@ */ #include +#ifdef CONFIG_EXYNOS5 +#include +#include +#include +#include +#else #include - +#endif #include #include +#include "s3c24x0_i2c.h" #ifdef CONFIG_HARD_I2C @@ -45,6 +52,7 @@ #define I2CSTAT_BSY 0x20 /* Busy bit */ #define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ #define I2CCON_IRPND 0x10 /* Interrupt pending bit */ #define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ #define I2C_MODE_MR 0x80 /* Master Receive Mode */ @@ -53,6 +61,48 @@ #define I2C_TIMEOUT 1 /* 1 second */ +#ifdef CONFIG_EXYNOS5 +static unsigned int g_current_bus; /* Stores Current I2C Bus */ + +/* We should not rely on any particular ordering of these IDs */ +static enum periph_id periph_for_dev[] = { + PERIPH_ID_I2C0, + PERIPH_ID_I2C1, + PERIPH_ID_I2C2, + PERIPH_ID_I2C3, + PERIPH_ID_I2C4, + PERIPH_ID_I2C5, + PERIPH_ID_I2C6, + PERIPH_ID_I2C7, +}; + +static enum periph_id i2c_get_periph_id(unsigned dev_index) +{ + if (dev_index < ARRAY_SIZE(periph_for_dev)) + return periph_for_dev[dev_index]; + + debug("%s: invalid bus %d", __func__, dev_index); + + return PERIPH_ID_NONE; +} + +static struct s3c24x0_i2c *get_base_i2c(int bus_idx) +{ +#ifdef CONFIG_EXYNOS5 + struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)samsung_get_base_i2c(); + return &i2c[bus_idx]; +#else + return s3c24x0_get_base_i2c(); +#endif + +} + +static inline struct exynos5_gpio_part1 *exynos_get_base_gpio1(void) +{ + return (struct exynos5_gpio_part1 *)(EXYNOS5_GPIO_PART1_BASE); +} + +#else static int GetI2CSDA(void) { struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); @@ -77,16 +127,17 @@ static void SetI2CSCL(int x) struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); #ifdef CONFIG_S3C2410 - writel((readl(&gpio->gpedat) & ~0x4000) | (x & 1) << 14, &gpio->gpedat); + writel((readl(&gpio->gpedat) & ~0x4000) | + (x & 1) << 14, &gpio->gpedat); #endif #ifdef CONFIG_S3C2400 writel((readl(&gpio->pgdat) & ~0x0040) | (x & 1) << 6, &gpio->pgdat); #endif } +#endif -static int WaitForXfer(void) +static int WaitForXfer(struct s3c24x0_i2c *i2c) { - struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); int i; i = I2C_TIMEOUT * 10000; @@ -98,25 +149,79 @@ static int WaitForXfer(void) return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT; } -static int IsACK(void) +static int IsACK(struct s3c24x0_i2c *i2c) { - struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); - return !(readl(&i2c->iicstat) & I2CSTAT_NACK); } -static void ReadWriteByte(void) +static void ReadWriteByte(struct s3c24x0_i2c *i2c) { - struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); - writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); } +static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) +{ + ulong freq, pres = 16, div; +#ifdef CONFIG_EXYNOS5 + freq = get_i2c_clk(); +#else + freq = get_PCLK(); +#endif + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + div = 0; + while ((freq / pres / (div + 1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon); + + /* init to SLAVE REVEIVE and set slaveaddr */ + writel(0, &i2c->iicstat); + writel(slaveadd, &i2c->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); +} + +static void i2c_bus_init(struct s3c24x0_i2c *i2c, unsigned int bus) +{ + int periph_id = i2c_get_periph_id(bus); + + exynos_pinmux_config(periph_id, 0); + + i2c_ch_init(i2c, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +} + +#ifdef CONFIG_EXYNOS5 +void i2c_init(int speed, int slaveadd) +{ + struct s3c24x0_i2c *i2c; + struct exynos5_gpio_part1 *gpio; + int i; + + /* By default i2c channel 0 is the current bus */ + g_current_bus = I2C0; + + i2c = get_base_i2c(g_current_bus); + + i2c_bus_init(i2c, g_current_bus); + + /* wait for some time to give previous transfer a chance to finish */ + i = I2C_TIMEOUT * 1000; + while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) { + udelay(1000); + i--; + } +} + +#else void i2c_init(int speed, int slaveadd) { - struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); + struct s3c24x0_i2c *i2c = get_base_i2c(); struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); - ulong freq, pres = 16, div; int i; /* wait for some time to give previous transfer a chance to finish */ @@ -171,27 +276,9 @@ void i2c_init(int speed, int slaveadd) #endif } - /* calculate prescaler and divisor values */ - freq = get_PCLK(); - if ((freq / pres / (16 + 1)) > speed) - /* set prescaler to 512 */ - pres = 512; - - div = 0; - while ((freq / pres / (div + 1)) > speed) - div++; - - /* set prescaler, divisor according to freq, also set - * ACKGEN, IRQ */ - writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon); - - /* init to SLAVE REVEIVE and set slaveaddr */ - writel(0, &i2c->iicstat); - writel(slaveadd, &i2c->iicadd); - /* program Master Transmit (and implicit STOP) */ - writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); - + i2c_ch_init(i2c, speed, slaveadd); } +#endif /* * cmd_type is 0 for write, 1 for read. @@ -200,19 +287,19 @@ void i2c_init(int speed, int slaveadd) * by the char, we could make it larger if needed. If it is * 0 we skip the address write cycle. */ -static -int i2c_transfer(unsigned char cmd_type, - unsigned char chip, - unsigned char addr[], - unsigned char addr_len, - unsigned char data[], unsigned short data_len) +static int i2c_transfer(struct s3c24x0_i2c *i2c, + unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) { - struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); int i, result; if (data == 0 || data_len == 0) { /*Don't support data transfer of no length or to address 0 */ - printf("i2c_transfer: bad call\n"); + debug("i2c_transfer: bad call\n"); return I2C_NOK; } @@ -226,7 +313,7 @@ int i2c_transfer(unsigned char cmd_type, if (readl(&i2c->iicstat) & I2CSTAT_BSY) return I2C_NOK_TOUT; - writel(readl(&i2c->iiccon) | 0x80, &i2c->iiccon); + writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon); result = I2C_OK; switch (cmd_type) { @@ -238,16 +325,16 @@ int i2c_transfer(unsigned char cmd_type, &i2c->iicstat); i = 0; while ((i < addr_len) && (result == I2C_OK)) { - result = WaitForXfer(); + result = WaitForXfer(i2c); writel(addr[i], &i2c->iicds); - ReadWriteByte(); + ReadWriteByte(i2c); i++; } i = 0; while ((i < data_len) && (result == I2C_OK)) { - result = WaitForXfer(); + result = WaitForXfer(i2c); writel(data[i], &i2c->iicds); - ReadWriteByte(); + ReadWriteByte(i2c); i++; } } else { @@ -257,19 +344,19 @@ int i2c_transfer(unsigned char cmd_type, &i2c->iicstat); i = 0; while ((i < data_len) && (result = I2C_OK)) { - result = WaitForXfer(); + result = WaitForXfer(i2c); writel(data[i], &i2c->iicds); - ReadWriteByte(); + ReadWriteByte(i2c); i++; } } if (result == I2C_OK) - result = WaitForXfer(); + result = WaitForXfer(i2c); /* send STOP */ writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); - ReadWriteByte(); + ReadWriteByte(i2c); break; case I2C_READ: @@ -279,13 +366,13 @@ int i2c_transfer(unsigned char cmd_type, /* send START */ writel(readl(&i2c->iicstat) | I2C_START_STOP, &i2c->iicstat); - result = WaitForXfer(); - if (IsACK()) { + result = WaitForXfer(i2c); + if (IsACK(i2c)) { i = 0; while ((i < addr_len) && (result == I2C_OK)) { writel(addr[i], &i2c->iicds); - ReadWriteByte(); - result = WaitForXfer(); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); i++; } @@ -293,16 +380,17 @@ int i2c_transfer(unsigned char cmd_type, /* resend START */ writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, &i2c->iicstat); - ReadWriteByte(); - result = WaitForXfer(); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); i = 0; while ((i < data_len) && (result == I2C_OK)) { /* disable ACK for final READ */ if (i == data_len - 1) writel(readl(&i2c->iiccon) - & ~0x80, &i2c->iiccon); - ReadWriteByte(); - result = WaitForXfer(); + & ~I2CCON_ACKGEN, + &i2c->iiccon); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); data[i] = readl(&i2c->iicds); i++; } @@ -316,17 +404,18 @@ int i2c_transfer(unsigned char cmd_type, /* send START */ writel(readl(&i2c->iicstat) | I2C_START_STOP, &i2c->iicstat); - result = WaitForXfer(); + result = WaitForXfer(i2c); - if (IsACK()) { + if (IsACK(i2c)) { i = 0; while ((i < data_len) && (result == I2C_OK)) { /* disable ACK for final READ */ if (i == data_len - 1) writel(readl(&i2c->iiccon) & - ~0x80, &i2c->iiccon); - ReadWriteByte(); - result = WaitForXfer(); + ~I2CCON_ACKGEN, + &i2c->iiccon); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); data[i] = readl(&i2c->iicds); i++; } @@ -337,22 +426,24 @@ int i2c_transfer(unsigned char cmd_type, /* send STOP */ writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); - ReadWriteByte(); + ReadWriteByte(i2c); break; default: - printf("i2c_transfer: bad call\n"); + debug("i2c_transfer: bad call\n"); result = I2C_NOK; break; } - return (result); + return result; } int i2c_probe(uchar chip) { + struct s3c24x0_i2c *i2c; uchar buf[1]; + i2c = get_base_i2c(g_current_bus); buf[0] = 0; /* @@ -360,16 +451,17 @@ int i2c_probe(uchar chip) * address was ed (i.e. there was a chip at that address which * drove the data line low). */ - return i2c_transfer(I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK; + return i2c_transfer(i2c, I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK; } int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) { + struct s3c24x0_i2c *i2c; uchar xaddr[4]; int ret; if (alen > 4) { - printf("I2C read: addr len %d not supported\n", alen); + debug("I2C read: addr len %d not supported\n", alen); return 1; } @@ -396,10 +488,11 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif - if ((ret = - i2c_transfer(I2C_READ, chip << 1, &xaddr[4 - alen], alen, - buffer, len)) != 0) { - printf("I2c read: failed %d\n", ret); + i2c = get_base_i2c(g_current_bus); + ret = i2c_transfer(i2c, I2C_READ, chip << 1, &xaddr[4 - alen], alen, + buffer, len); + if (ret != 0) { + debug("I2c read: failed %d\n", ret); return 1; } return 0; @@ -407,10 +500,11 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) { + struct s3c24x0_i2c *i2c; uchar xaddr[4]; if (alen > 4) { - printf("I2C write: addr len %d not supported\n", alen); + debug("I2C write: addr len %d not supported\n", alen); return 1; } @@ -436,8 +530,9 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif + i2c = get_base_i2c(g_current_bus); return (i2c_transfer - (I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer, + (i2c, I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer, len) != 0); } #endif /* CONFIG_HARD_I2C */ diff --git a/drivers/i2c/s3c24x0_i2c.h b/drivers/i2c/s3c24x0_i2c.h index d357a0a..57aafb1 100644 --- a/drivers/i2c/s3c24x0_i2c.h +++ b/drivers/i2c/s3c24x0_i2c.h @@ -23,6 +23,9 @@ #ifndef _S3C24X0_I2C_H #define _S3C24X0_I2C_H +/* I2C channels exynos5 has 8 i2c channel */ +#define I2C0 0 + struct s3c24x0_i2c { u32 iiccon; u32 iicstat;