xref: /rk3399_rockchip-uboot/drivers/i2c/s3c24x0_i2c.c (revision c86d9ed3820bbd89e264261fb022bdf9448bfde7)
1d3b63577SJean-Christophe PLAGNIOL-VILLARD /*
2d3b63577SJean-Christophe PLAGNIOL-VILLARD  * (C) Copyright 2002
3d3b63577SJean-Christophe PLAGNIOL-VILLARD  * David Mueller, ELSOFT AG, d.mueller@elsoft.ch
4d3b63577SJean-Christophe PLAGNIOL-VILLARD  *
5d3b63577SJean-Christophe PLAGNIOL-VILLARD  * See file CREDITS for list of people who contributed to this
6d3b63577SJean-Christophe PLAGNIOL-VILLARD  * project.
7d3b63577SJean-Christophe PLAGNIOL-VILLARD  *
8d3b63577SJean-Christophe PLAGNIOL-VILLARD  * This program is free software; you can redistribute it and/or
9d3b63577SJean-Christophe PLAGNIOL-VILLARD  * modify it under the terms of the GNU General Public License as
10d3b63577SJean-Christophe PLAGNIOL-VILLARD  * published by the Free Software Foundation; either version 2 of
11d3b63577SJean-Christophe PLAGNIOL-VILLARD  * the License, or (at your option) any later version.
12d3b63577SJean-Christophe PLAGNIOL-VILLARD  *
13d3b63577SJean-Christophe PLAGNIOL-VILLARD  * This program is distributed in the hope that it will be useful,
14d3b63577SJean-Christophe PLAGNIOL-VILLARD  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15d3b63577SJean-Christophe PLAGNIOL-VILLARD  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16d3b63577SJean-Christophe PLAGNIOL-VILLARD  * GNU General Public License for more details.
17d3b63577SJean-Christophe PLAGNIOL-VILLARD  *
18d3b63577SJean-Christophe PLAGNIOL-VILLARD  * You should have received a copy of the GNU General Public License
19d3b63577SJean-Christophe PLAGNIOL-VILLARD  * along with this program; if not, write to the Free Software
20d3b63577SJean-Christophe PLAGNIOL-VILLARD  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21d3b63577SJean-Christophe PLAGNIOL-VILLARD  * MA 02111-1307 USA
22d3b63577SJean-Christophe PLAGNIOL-VILLARD  */
23d3b63577SJean-Christophe PLAGNIOL-VILLARD 
24d3b63577SJean-Christophe PLAGNIOL-VILLARD /* This code should work for both the S3C2400 and the S3C2410
25d3b63577SJean-Christophe PLAGNIOL-VILLARD  * as they seem to have the same I2C controller inside.
26d3b63577SJean-Christophe PLAGNIOL-VILLARD  * The different address mapping is handled by the s3c24xx.h files below.
27d3b63577SJean-Christophe PLAGNIOL-VILLARD  */
28d3b63577SJean-Christophe PLAGNIOL-VILLARD 
29d3b63577SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
30*c86d9ed3SPiotr Wilczek #if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
31ab7e52bbSRajeshwari Shinde #include <asm/arch/clk.h>
32ab7e52bbSRajeshwari Shinde #include <asm/arch/cpu.h>
33ab7e52bbSRajeshwari Shinde #else
34ac67804fSkevin.morfitt@fearnside-systems.co.uk #include <asm/arch/s3c24x0_cpu.h>
35ab7e52bbSRajeshwari Shinde #endif
36eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk #include <asm/io.h>
37d3b63577SJean-Christophe PLAGNIOL-VILLARD #include <i2c.h>
38ab7e52bbSRajeshwari Shinde #include "s3c24x0_i2c.h"
39d3b63577SJean-Christophe PLAGNIOL-VILLARD 
40d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_HARD_I2C
41d3b63577SJean-Christophe PLAGNIOL-VILLARD 
42d3b63577SJean-Christophe PLAGNIOL-VILLARD #define	I2C_WRITE	0
43d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_READ	1
44d3b63577SJean-Christophe PLAGNIOL-VILLARD 
45d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_OK		0
46d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NOK		1
47d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NACK	2
48d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NOK_LA	3	/* Lost arbitration */
49d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NOK_TOUT	4	/* time out */
50d3b63577SJean-Christophe PLAGNIOL-VILLARD 
51d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2CSTAT_BSY	0x20	/* Busy bit */
52d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2CSTAT_NACK	0x01	/* Nack bit */
53ab7e52bbSRajeshwari Shinde #define I2CCON_ACKGEN	0x80	/* Acknowledge generation */
54d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2CCON_IRPND	0x10	/* Interrupt pending bit */
55d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_MODE_MT	0xC0	/* Master Transmit Mode */
56d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_MODE_MR	0x80	/* Master Receive Mode */
57d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_START_STOP	0x20	/* START / STOP */
58d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_TXRX_ENA	0x10	/* I2C Tx/Rx enable */
59d3b63577SJean-Christophe PLAGNIOL-VILLARD 
60d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_TIMEOUT 1		/* 1 second */
61d3b63577SJean-Christophe PLAGNIOL-VILLARD 
62ab7e52bbSRajeshwari Shinde 
63ab7e52bbSRajeshwari Shinde static unsigned int g_current_bus;	/* Stores Current I2C Bus */
64ab7e52bbSRajeshwari Shinde 
65*c86d9ed3SPiotr Wilczek #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
66d3b63577SJean-Christophe PLAGNIOL-VILLARD static int GetI2CSDA(void)
67d3b63577SJean-Christophe PLAGNIOL-VILLARD {
68eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
69d3b63577SJean-Christophe PLAGNIOL-VILLARD 
70d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
71d9abba82SC Nauman 	return (readl(&gpio->gpedat) & 0x8000) >> 15;
72d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
73d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
74d9abba82SC Nauman 	return (readl(&gpio->pgdat) & 0x0020) >> 5;
75d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
76d3b63577SJean-Christophe PLAGNIOL-VILLARD }
77d3b63577SJean-Christophe PLAGNIOL-VILLARD 
78d3b63577SJean-Christophe PLAGNIOL-VILLARD #if 0
79d3b63577SJean-Christophe PLAGNIOL-VILLARD static void SetI2CSDA(int x)
80d3b63577SJean-Christophe PLAGNIOL-VILLARD {
81d3b63577SJean-Christophe PLAGNIOL-VILLARD 	rGPEDAT = (rGPEDAT & ~0x8000) | (x & 1) << 15;
82d3b63577SJean-Christophe PLAGNIOL-VILLARD }
83d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
84d3b63577SJean-Christophe PLAGNIOL-VILLARD 
85d3b63577SJean-Christophe PLAGNIOL-VILLARD static void SetI2CSCL(int x)
86d3b63577SJean-Christophe PLAGNIOL-VILLARD {
87eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
88d3b63577SJean-Christophe PLAGNIOL-VILLARD 
89d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
90ab7e52bbSRajeshwari Shinde 	writel((readl(&gpio->gpedat) & ~0x4000) |
91ab7e52bbSRajeshwari Shinde 					(x & 1) << 14, &gpio->gpedat);
92d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
93d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
94d9abba82SC Nauman 	writel((readl(&gpio->pgdat) & ~0x0040) | (x & 1) << 6, &gpio->pgdat);
95d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
96d3b63577SJean-Christophe PLAGNIOL-VILLARD }
97ab7e52bbSRajeshwari Shinde #endif
98d3b63577SJean-Christophe PLAGNIOL-VILLARD 
99ab7e52bbSRajeshwari Shinde static int WaitForXfer(struct s3c24x0_i2c *i2c)
100d3b63577SJean-Christophe PLAGNIOL-VILLARD {
101eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	int i;
102d3b63577SJean-Christophe PLAGNIOL-VILLARD 
103d3b63577SJean-Christophe PLAGNIOL-VILLARD 	i = I2C_TIMEOUT * 10000;
104d9abba82SC Nauman 	while (!(readl(&i2c->iiccon) & I2CCON_IRPND) && (i > 0)) {
105d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(100);
106d3b63577SJean-Christophe PLAGNIOL-VILLARD 		i--;
107d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
108d3b63577SJean-Christophe PLAGNIOL-VILLARD 
109d9abba82SC Nauman 	return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT;
110d3b63577SJean-Christophe PLAGNIOL-VILLARD }
111d3b63577SJean-Christophe PLAGNIOL-VILLARD 
112ab7e52bbSRajeshwari Shinde static int IsACK(struct s3c24x0_i2c *i2c)
113d3b63577SJean-Christophe PLAGNIOL-VILLARD {
114d9abba82SC Nauman 	return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
115d3b63577SJean-Christophe PLAGNIOL-VILLARD }
116d3b63577SJean-Christophe PLAGNIOL-VILLARD 
117ab7e52bbSRajeshwari Shinde static void ReadWriteByte(struct s3c24x0_i2c *i2c)
118d3b63577SJean-Christophe PLAGNIOL-VILLARD {
119d9abba82SC Nauman 	writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon);
120d3b63577SJean-Christophe PLAGNIOL-VILLARD }
121d3b63577SJean-Christophe PLAGNIOL-VILLARD 
122ab7e52bbSRajeshwari Shinde static struct s3c24x0_i2c *get_base_i2c(void)
123ab7e52bbSRajeshwari Shinde {
124*c86d9ed3SPiotr Wilczek #ifdef CONFIG_EXYNOS4
125*c86d9ed3SPiotr Wilczek 	struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c()
126*c86d9ed3SPiotr Wilczek 							+ (EXYNOS4_I2C_SPACING
127*c86d9ed3SPiotr Wilczek 							* g_current_bus));
128*c86d9ed3SPiotr Wilczek 	return i2c;
129*c86d9ed3SPiotr Wilczek #elif defined CONFIG_EXYNOS5
130ab7e52bbSRajeshwari Shinde 	struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c()
131ab7e52bbSRajeshwari Shinde 							+ (EXYNOS5_I2C_SPACING
132ab7e52bbSRajeshwari Shinde 							* g_current_bus));
133ab7e52bbSRajeshwari Shinde 	return i2c;
134ab7e52bbSRajeshwari Shinde #else
135ab7e52bbSRajeshwari Shinde 	return s3c24x0_get_base_i2c();
136ab7e52bbSRajeshwari Shinde #endif
137ab7e52bbSRajeshwari Shinde }
138ab7e52bbSRajeshwari Shinde 
139ab7e52bbSRajeshwari Shinde static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd)
140ab7e52bbSRajeshwari Shinde {
141ab7e52bbSRajeshwari Shinde 	ulong freq, pres = 16, div;
142*c86d9ed3SPiotr Wilczek #if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
143ab7e52bbSRajeshwari Shinde 	freq = get_i2c_clk();
144ab7e52bbSRajeshwari Shinde #else
145ab7e52bbSRajeshwari Shinde 	freq = get_PCLK();
146ab7e52bbSRajeshwari Shinde #endif
147ab7e52bbSRajeshwari Shinde 	/* calculate prescaler and divisor values */
148ab7e52bbSRajeshwari Shinde 	if ((freq / pres / (16 + 1)) > speed)
149ab7e52bbSRajeshwari Shinde 		/* set prescaler to 512 */
150ab7e52bbSRajeshwari Shinde 		pres = 512;
151ab7e52bbSRajeshwari Shinde 
152ab7e52bbSRajeshwari Shinde 	div = 0;
153ab7e52bbSRajeshwari Shinde 	while ((freq / pres / (div + 1)) > speed)
154ab7e52bbSRajeshwari Shinde 		div++;
155ab7e52bbSRajeshwari Shinde 
156ab7e52bbSRajeshwari Shinde 	/* set prescaler, divisor according to freq, also set ACKGEN, IRQ */
157ab7e52bbSRajeshwari Shinde 	writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
158ab7e52bbSRajeshwari Shinde 
159ab7e52bbSRajeshwari Shinde 	/* init to SLAVE REVEIVE and set slaveaddr */
160ab7e52bbSRajeshwari Shinde 	writel(0, &i2c->iicstat);
161ab7e52bbSRajeshwari Shinde 	writel(slaveadd, &i2c->iicadd);
162ab7e52bbSRajeshwari Shinde 	/* program Master Transmit (and implicit STOP) */
163ab7e52bbSRajeshwari Shinde 	writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
164ab7e52bbSRajeshwari Shinde }
165ab7e52bbSRajeshwari Shinde 
166178239deSRajeshwari Shinde /*
167178239deSRajeshwari Shinde  * MULTI BUS I2C support
168178239deSRajeshwari Shinde  */
169178239deSRajeshwari Shinde 
170178239deSRajeshwari Shinde #ifdef CONFIG_I2C_MULTI_BUS
171178239deSRajeshwari Shinde int i2c_set_bus_num(unsigned int bus)
172178239deSRajeshwari Shinde {
173178239deSRajeshwari Shinde 	struct s3c24x0_i2c *i2c;
174178239deSRajeshwari Shinde 
175178239deSRajeshwari Shinde 	if ((bus < 0) || (bus >= CONFIG_MAX_I2C_NUM)) {
176178239deSRajeshwari Shinde 		debug("Bad bus: %d\n", bus);
177178239deSRajeshwari Shinde 		return -1;
178178239deSRajeshwari Shinde 	}
179178239deSRajeshwari Shinde 
180178239deSRajeshwari Shinde 	g_current_bus = bus;
181178239deSRajeshwari Shinde 	i2c = get_base_i2c();
182178239deSRajeshwari Shinde 	i2c_ch_init(i2c, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
183178239deSRajeshwari Shinde 
184178239deSRajeshwari Shinde 	return 0;
185178239deSRajeshwari Shinde }
186178239deSRajeshwari Shinde 
187178239deSRajeshwari Shinde unsigned int i2c_get_bus_num(void)
188178239deSRajeshwari Shinde {
189178239deSRajeshwari Shinde 	return g_current_bus;
190178239deSRajeshwari Shinde }
191178239deSRajeshwari Shinde #endif
192178239deSRajeshwari Shinde 
193d3b63577SJean-Christophe PLAGNIOL-VILLARD void i2c_init(int speed, int slaveadd)
194d3b63577SJean-Christophe PLAGNIOL-VILLARD {
195ab7e52bbSRajeshwari Shinde 	struct s3c24x0_i2c *i2c;
196*c86d9ed3SPiotr Wilczek #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
197eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
198ab7e52bbSRajeshwari Shinde #endif
199eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	int i;
200d3b63577SJean-Christophe PLAGNIOL-VILLARD 
201ab7e52bbSRajeshwari Shinde 	/* By default i2c channel 0 is the current bus */
202ab7e52bbSRajeshwari Shinde 	g_current_bus = 0;
203ab7e52bbSRajeshwari Shinde 	i2c = get_base_i2c();
204d3b63577SJean-Christophe PLAGNIOL-VILLARD 
205ab7e52bbSRajeshwari Shinde 	/* wait for some time to give previous transfer a chance to finish */
206d3b63577SJean-Christophe PLAGNIOL-VILLARD 	i = I2C_TIMEOUT * 1000;
207ab7e52bbSRajeshwari Shinde 	while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) {
208d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(1000);
209d3b63577SJean-Christophe PLAGNIOL-VILLARD 		i--;
210d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
211d3b63577SJean-Christophe PLAGNIOL-VILLARD 
212*c86d9ed3SPiotr Wilczek #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
213d9abba82SC Nauman 	if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) {
214d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
215d9abba82SC Nauman 		ulong old_gpecon = readl(&gpio->gpecon);
216d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
217d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
218d9abba82SC Nauman 		ulong old_gpecon = readl(&gpio->pgcon);
219d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
220eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 		/* bus still busy probably by (most) previously interrupted
221eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 		   transfer */
222d3b63577SJean-Christophe PLAGNIOL-VILLARD 
223d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
224d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* set I2CSDA and I2CSCL (GPE15, GPE14) to GPIO */
225d9abba82SC Nauman 		writel((readl(&gpio->gpecon) & ~0xF0000000) | 0x10000000,
226d9abba82SC Nauman 		       &gpio->gpecon);
227d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
228d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
229d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* set I2CSDA and I2CSCL (PG5, PG6) to GPIO */
230d9abba82SC Nauman 		writel((readl(&gpio->pgcon) & ~0x00003c00) | 0x00001000,
231d9abba82SC Nauman 		       &gpio->pgcon);
232d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
233d3b63577SJean-Christophe PLAGNIOL-VILLARD 
234d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* toggle I2CSCL until bus idle */
235d3b63577SJean-Christophe PLAGNIOL-VILLARD 		SetI2CSCL(0);
236d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(1000);
237d3b63577SJean-Christophe PLAGNIOL-VILLARD 		i = 10;
238d3b63577SJean-Christophe PLAGNIOL-VILLARD 		while ((i > 0) && (GetI2CSDA() != 1)) {
239d3b63577SJean-Christophe PLAGNIOL-VILLARD 			SetI2CSCL(1);
240d3b63577SJean-Christophe PLAGNIOL-VILLARD 			udelay(1000);
241d3b63577SJean-Christophe PLAGNIOL-VILLARD 			SetI2CSCL(0);
242d3b63577SJean-Christophe PLAGNIOL-VILLARD 			udelay(1000);
243d3b63577SJean-Christophe PLAGNIOL-VILLARD 			i--;
244d3b63577SJean-Christophe PLAGNIOL-VILLARD 		}
245d3b63577SJean-Christophe PLAGNIOL-VILLARD 		SetI2CSCL(1);
246d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(1000);
247d3b63577SJean-Christophe PLAGNIOL-VILLARD 
248d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* restore pin functions */
249d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
250d9abba82SC Nauman 		writel(old_gpecon, &gpio->gpecon);
251d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
252d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
253d9abba82SC Nauman 		writel(old_gpecon, &gpio->pgcon);
254d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
255d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
256*c86d9ed3SPiotr Wilczek #endif /* #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) */
257ab7e52bbSRajeshwari Shinde 	i2c_ch_init(i2c, speed, slaveadd);
258d3b63577SJean-Christophe PLAGNIOL-VILLARD }
259d3b63577SJean-Christophe PLAGNIOL-VILLARD 
260d3b63577SJean-Christophe PLAGNIOL-VILLARD /*
261d3b63577SJean-Christophe PLAGNIOL-VILLARD  * cmd_type is 0 for write, 1 for read.
262d3b63577SJean-Christophe PLAGNIOL-VILLARD  *
263d3b63577SJean-Christophe PLAGNIOL-VILLARD  * addr_len can take any value from 0-255, it is only limited
264d3b63577SJean-Christophe PLAGNIOL-VILLARD  * by the char, we could make it larger if needed. If it is
265d3b63577SJean-Christophe PLAGNIOL-VILLARD  * 0 we skip the address write cycle.
266d3b63577SJean-Christophe PLAGNIOL-VILLARD  */
267ab7e52bbSRajeshwari Shinde static int i2c_transfer(struct s3c24x0_i2c *i2c,
268ab7e52bbSRajeshwari Shinde 			unsigned char cmd_type,
269d3b63577SJean-Christophe PLAGNIOL-VILLARD 			unsigned char chip,
270d3b63577SJean-Christophe PLAGNIOL-VILLARD 			unsigned char addr[],
271d3b63577SJean-Christophe PLAGNIOL-VILLARD 			unsigned char addr_len,
272ab7e52bbSRajeshwari Shinde 			unsigned char data[],
273ab7e52bbSRajeshwari Shinde 			unsigned short data_len)
274d3b63577SJean-Christophe PLAGNIOL-VILLARD {
275eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	int i, result;
276d3b63577SJean-Christophe PLAGNIOL-VILLARD 
277d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (data == 0 || data_len == 0) {
278d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/*Don't support data transfer of no length or to address 0 */
279ab7e52bbSRajeshwari Shinde 		debug("i2c_transfer: bad call\n");
280d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return I2C_NOK;
281d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
282d3b63577SJean-Christophe PLAGNIOL-VILLARD 
283d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/* Check I2C bus idle */
284d3b63577SJean-Christophe PLAGNIOL-VILLARD 	i = I2C_TIMEOUT * 1000;
285d9abba82SC Nauman 	while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) {
286d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(1000);
287d3b63577SJean-Christophe PLAGNIOL-VILLARD 		i--;
288d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
289d3b63577SJean-Christophe PLAGNIOL-VILLARD 
290d9abba82SC Nauman 	if (readl(&i2c->iicstat) & I2CSTAT_BSY)
291d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return I2C_NOK_TOUT;
292d3b63577SJean-Christophe PLAGNIOL-VILLARD 
293ab7e52bbSRajeshwari Shinde 	writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon);
294d3b63577SJean-Christophe PLAGNIOL-VILLARD 	result = I2C_OK;
295d3b63577SJean-Christophe PLAGNIOL-VILLARD 
296d3b63577SJean-Christophe PLAGNIOL-VILLARD 	switch (cmd_type) {
297d3b63577SJean-Christophe PLAGNIOL-VILLARD 	case I2C_WRITE:
298d3b63577SJean-Christophe PLAGNIOL-VILLARD 		if (addr && addr_len) {
299d9abba82SC Nauman 			writel(chip, &i2c->iicds);
300d3b63577SJean-Christophe PLAGNIOL-VILLARD 			/* send START */
301eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 			writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
302d9abba82SC Nauman 			       &i2c->iicstat);
303d3b63577SJean-Christophe PLAGNIOL-VILLARD 			i = 0;
304d3b63577SJean-Christophe PLAGNIOL-VILLARD 			while ((i < addr_len) && (result == I2C_OK)) {
305ab7e52bbSRajeshwari Shinde 				result = WaitForXfer(i2c);
306d9abba82SC Nauman 				writel(addr[i], &i2c->iicds);
307ab7e52bbSRajeshwari Shinde 				ReadWriteByte(i2c);
308d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i++;
309d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
310d3b63577SJean-Christophe PLAGNIOL-VILLARD 			i = 0;
311d3b63577SJean-Christophe PLAGNIOL-VILLARD 			while ((i < data_len) && (result == I2C_OK)) {
312ab7e52bbSRajeshwari Shinde 				result = WaitForXfer(i2c);
313d9abba82SC Nauman 				writel(data[i], &i2c->iicds);
314ab7e52bbSRajeshwari Shinde 				ReadWriteByte(i2c);
315d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i++;
316d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
317d3b63577SJean-Christophe PLAGNIOL-VILLARD 		} else {
318d9abba82SC Nauman 			writel(chip, &i2c->iicds);
319d3b63577SJean-Christophe PLAGNIOL-VILLARD 			/* send START */
320eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 			writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
321d9abba82SC Nauman 			       &i2c->iicstat);
322d3b63577SJean-Christophe PLAGNIOL-VILLARD 			i = 0;
323d3b63577SJean-Christophe PLAGNIOL-VILLARD 			while ((i < data_len) && (result = I2C_OK)) {
324ab7e52bbSRajeshwari Shinde 				result = WaitForXfer(i2c);
325d9abba82SC Nauman 				writel(data[i], &i2c->iicds);
326ab7e52bbSRajeshwari Shinde 				ReadWriteByte(i2c);
327d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i++;
328d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
329d3b63577SJean-Christophe PLAGNIOL-VILLARD 		}
330d3b63577SJean-Christophe PLAGNIOL-VILLARD 
331d3b63577SJean-Christophe PLAGNIOL-VILLARD 		if (result == I2C_OK)
332ab7e52bbSRajeshwari Shinde 			result = WaitForXfer(i2c);
333d3b63577SJean-Christophe PLAGNIOL-VILLARD 
334d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* send STOP */
335d9abba82SC Nauman 		writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
336ab7e52bbSRajeshwari Shinde 		ReadWriteByte(i2c);
337d3b63577SJean-Christophe PLAGNIOL-VILLARD 		break;
338d3b63577SJean-Christophe PLAGNIOL-VILLARD 
339d3b63577SJean-Christophe PLAGNIOL-VILLARD 	case I2C_READ:
340d3b63577SJean-Christophe PLAGNIOL-VILLARD 		if (addr && addr_len) {
341d9abba82SC Nauman 			writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
342d9abba82SC Nauman 			writel(chip, &i2c->iicds);
343d3b63577SJean-Christophe PLAGNIOL-VILLARD 			/* send START */
344d9abba82SC Nauman 			writel(readl(&i2c->iicstat) | I2C_START_STOP,
345d9abba82SC Nauman 			       &i2c->iicstat);
346ab7e52bbSRajeshwari Shinde 			result = WaitForXfer(i2c);
347ab7e52bbSRajeshwari Shinde 			if (IsACK(i2c)) {
348d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i = 0;
349d3b63577SJean-Christophe PLAGNIOL-VILLARD 				while ((i < addr_len) && (result == I2C_OK)) {
350d9abba82SC Nauman 					writel(addr[i], &i2c->iicds);
351ab7e52bbSRajeshwari Shinde 					ReadWriteByte(i2c);
352ab7e52bbSRajeshwari Shinde 					result = WaitForXfer(i2c);
353d3b63577SJean-Christophe PLAGNIOL-VILLARD 					i++;
354d3b63577SJean-Christophe PLAGNIOL-VILLARD 				}
355d3b63577SJean-Christophe PLAGNIOL-VILLARD 
356d9abba82SC Nauman 				writel(chip, &i2c->iicds);
357d3b63577SJean-Christophe PLAGNIOL-VILLARD 				/* resend START */
358eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 				writel(I2C_MODE_MR | I2C_TXRX_ENA |
359d9abba82SC Nauman 				       I2C_START_STOP, &i2c->iicstat);
360ab7e52bbSRajeshwari Shinde 			ReadWriteByte(i2c);
361ab7e52bbSRajeshwari Shinde 			result = WaitForXfer(i2c);
362d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i = 0;
363d3b63577SJean-Christophe PLAGNIOL-VILLARD 				while ((i < data_len) && (result == I2C_OK)) {
364d3b63577SJean-Christophe PLAGNIOL-VILLARD 					/* disable ACK for final READ */
365d3b63577SJean-Christophe PLAGNIOL-VILLARD 					if (i == data_len - 1)
366d9abba82SC Nauman 						writel(readl(&i2c->iiccon)
367ab7e52bbSRajeshwari Shinde 							& ~I2CCON_ACKGEN,
368ab7e52bbSRajeshwari Shinde 							&i2c->iiccon);
369ab7e52bbSRajeshwari Shinde 				ReadWriteByte(i2c);
370ab7e52bbSRajeshwari Shinde 				result = WaitForXfer(i2c);
371d9abba82SC Nauman 					data[i] = readl(&i2c->iicds);
372d3b63577SJean-Christophe PLAGNIOL-VILLARD 					i++;
373d3b63577SJean-Christophe PLAGNIOL-VILLARD 				}
374d3b63577SJean-Christophe PLAGNIOL-VILLARD 			} else {
375d3b63577SJean-Christophe PLAGNIOL-VILLARD 				result = I2C_NACK;
376d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
377d3b63577SJean-Christophe PLAGNIOL-VILLARD 
378d3b63577SJean-Christophe PLAGNIOL-VILLARD 		} else {
379d9abba82SC Nauman 			writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
380d9abba82SC Nauman 			writel(chip, &i2c->iicds);
381d3b63577SJean-Christophe PLAGNIOL-VILLARD 			/* send START */
382d9abba82SC Nauman 			writel(readl(&i2c->iicstat) | I2C_START_STOP,
383d9abba82SC Nauman 			       &i2c->iicstat);
384ab7e52bbSRajeshwari Shinde 			result = WaitForXfer(i2c);
385d3b63577SJean-Christophe PLAGNIOL-VILLARD 
386ab7e52bbSRajeshwari Shinde 			if (IsACK(i2c)) {
387d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i = 0;
388d3b63577SJean-Christophe PLAGNIOL-VILLARD 				while ((i < data_len) && (result == I2C_OK)) {
389d3b63577SJean-Christophe PLAGNIOL-VILLARD 					/* disable ACK for final READ */
390d3b63577SJean-Christophe PLAGNIOL-VILLARD 					if (i == data_len - 1)
391d9abba82SC Nauman 						writel(readl(&i2c->iiccon) &
392ab7e52bbSRajeshwari Shinde 							~I2CCON_ACKGEN,
393ab7e52bbSRajeshwari Shinde 							&i2c->iiccon);
394ab7e52bbSRajeshwari Shinde 					ReadWriteByte(i2c);
395ab7e52bbSRajeshwari Shinde 					result = WaitForXfer(i2c);
396d9abba82SC Nauman 					data[i] = readl(&i2c->iicds);
397d3b63577SJean-Christophe PLAGNIOL-VILLARD 					i++;
398d3b63577SJean-Christophe PLAGNIOL-VILLARD 				}
399d3b63577SJean-Christophe PLAGNIOL-VILLARD 			} else {
400d3b63577SJean-Christophe PLAGNIOL-VILLARD 				result = I2C_NACK;
401d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
402d3b63577SJean-Christophe PLAGNIOL-VILLARD 		}
403d3b63577SJean-Christophe PLAGNIOL-VILLARD 
404d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* send STOP */
405d9abba82SC Nauman 		writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
406ab7e52bbSRajeshwari Shinde 		ReadWriteByte(i2c);
407d3b63577SJean-Christophe PLAGNIOL-VILLARD 		break;
408d3b63577SJean-Christophe PLAGNIOL-VILLARD 
409d3b63577SJean-Christophe PLAGNIOL-VILLARD 	default:
410ab7e52bbSRajeshwari Shinde 		debug("i2c_transfer: bad call\n");
411d3b63577SJean-Christophe PLAGNIOL-VILLARD 		result = I2C_NOK;
412d3b63577SJean-Christophe PLAGNIOL-VILLARD 		break;
413d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
414d3b63577SJean-Christophe PLAGNIOL-VILLARD 
415ab7e52bbSRajeshwari Shinde 	return result;
416d3b63577SJean-Christophe PLAGNIOL-VILLARD }
417d3b63577SJean-Christophe PLAGNIOL-VILLARD 
418d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_probe(uchar chip)
419d3b63577SJean-Christophe PLAGNIOL-VILLARD {
420ab7e52bbSRajeshwari Shinde 	struct s3c24x0_i2c *i2c;
421d3b63577SJean-Christophe PLAGNIOL-VILLARD 	uchar buf[1];
422d3b63577SJean-Christophe PLAGNIOL-VILLARD 
423ab7e52bbSRajeshwari Shinde 	i2c = get_base_i2c();
424d3b63577SJean-Christophe PLAGNIOL-VILLARD 	buf[0] = 0;
425d3b63577SJean-Christophe PLAGNIOL-VILLARD 
426d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/*
427d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * What is needed is to send the chip address and verify that the
428d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * address was <ACK>ed (i.e. there was a chip at that address which
429d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * drove the data line low).
430d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 */
431ab7e52bbSRajeshwari Shinde 	return i2c_transfer(i2c, I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK;
432d3b63577SJean-Christophe PLAGNIOL-VILLARD }
433d3b63577SJean-Christophe PLAGNIOL-VILLARD 
434d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
435d3b63577SJean-Christophe PLAGNIOL-VILLARD {
436ab7e52bbSRajeshwari Shinde 	struct s3c24x0_i2c *i2c;
437d3b63577SJean-Christophe PLAGNIOL-VILLARD 	uchar xaddr[4];
438d3b63577SJean-Christophe PLAGNIOL-VILLARD 	int ret;
439d3b63577SJean-Christophe PLAGNIOL-VILLARD 
440d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 4) {
441ab7e52bbSRajeshwari Shinde 		debug("I2C read: addr len %d not supported\n", alen);
442d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return 1;
443d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
444d3b63577SJean-Christophe PLAGNIOL-VILLARD 
445d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 0) {
446d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[0] = (addr >> 24) & 0xFF;
447d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[1] = (addr >> 16) & 0xFF;
448d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[2] = (addr >> 8) & 0xFF;
449d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[3] = addr & 0xFF;
450d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
451d3b63577SJean-Christophe PLAGNIOL-VILLARD 
452d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
453d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/*
454d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * EEPROM chips that implement "address overflow" are ones
455d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
456d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * address and the extra bits end up in the "chip address"
457d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * bit slots. This makes a 24WC08 (1Kbyte) chip look like
458d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * four 256 byte chips.
459d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 *
460d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * Note that we consider the length of the address field to
461d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * still be one byte because the extra address bits are
462d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * hidden in the chip address.
463d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 */
464d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 0)
465eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 		chip |= ((addr >> (alen * 8)) &
466eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 			 CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
467d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
468ab7e52bbSRajeshwari Shinde 	i2c = get_base_i2c();
469ab7e52bbSRajeshwari Shinde 	ret = i2c_transfer(i2c, I2C_READ, chip << 1, &xaddr[4 - alen], alen,
470ab7e52bbSRajeshwari Shinde 			buffer, len);
471ab7e52bbSRajeshwari Shinde 	if (ret != 0) {
472ab7e52bbSRajeshwari Shinde 		debug("I2c read: failed %d\n", ret);
473d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return 1;
474d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
475d3b63577SJean-Christophe PLAGNIOL-VILLARD 	return 0;
476d3b63577SJean-Christophe PLAGNIOL-VILLARD }
477d3b63577SJean-Christophe PLAGNIOL-VILLARD 
478d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
479d3b63577SJean-Christophe PLAGNIOL-VILLARD {
480ab7e52bbSRajeshwari Shinde 	struct s3c24x0_i2c *i2c;
481d3b63577SJean-Christophe PLAGNIOL-VILLARD 	uchar xaddr[4];
482d3b63577SJean-Christophe PLAGNIOL-VILLARD 
483d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 4) {
484ab7e52bbSRajeshwari Shinde 		debug("I2C write: addr len %d not supported\n", alen);
485d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return 1;
486d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
487d3b63577SJean-Christophe PLAGNIOL-VILLARD 
488d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 0) {
489d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[0] = (addr >> 24) & 0xFF;
490d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[1] = (addr >> 16) & 0xFF;
491d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[2] = (addr >> 8) & 0xFF;
492d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[3] = addr & 0xFF;
493d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
494d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
495d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/*
496d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * EEPROM chips that implement "address overflow" are ones
497d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
498d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * address and the extra bits end up in the "chip address"
499d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * bit slots. This makes a 24WC08 (1Kbyte) chip look like
500d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * four 256 byte chips.
501d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 *
502d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * Note that we consider the length of the address field to
503d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * still be one byte because the extra address bits are
504d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * hidden in the chip address.
505d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 */
506d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 0)
507eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 		chip |= ((addr >> (alen * 8)) &
508eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 			 CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
509d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
510ab7e52bbSRajeshwari Shinde 	i2c = get_base_i2c();
511d3b63577SJean-Christophe PLAGNIOL-VILLARD 	return (i2c_transfer
512ab7e52bbSRajeshwari Shinde 		(i2c, I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer,
513d3b63577SJean-Christophe PLAGNIOL-VILLARD 		 len) != 0);
514d3b63577SJean-Christophe PLAGNIOL-VILLARD }
515d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_HARD_I2C */
516