xref: /rk3399_rockchip-uboot/drivers/i2c/s3c24x0_i2c.c (revision d9abba8254c3e6b9a1d5c2e52c2d8088bbeb520f)
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>
30ac67804fSkevin.morfitt@fearnside-systems.co.uk #include <asm/arch/s3c24x0_cpu.h>
31eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 
32eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk #include <asm/io.h>
33d3b63577SJean-Christophe PLAGNIOL-VILLARD #include <i2c.h>
34d3b63577SJean-Christophe PLAGNIOL-VILLARD 
35d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_HARD_I2C
36d3b63577SJean-Christophe PLAGNIOL-VILLARD 
37d3b63577SJean-Christophe PLAGNIOL-VILLARD #define	I2C_WRITE	0
38d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_READ	1
39d3b63577SJean-Christophe PLAGNIOL-VILLARD 
40d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_OK		0
41d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NOK		1
42d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NACK	2
43d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NOK_LA	3	/* Lost arbitration */
44d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NOK_TOUT	4	/* time out */
45d3b63577SJean-Christophe PLAGNIOL-VILLARD 
46d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2CSTAT_BSY	0x20	/* Busy bit */
47d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2CSTAT_NACK	0x01	/* Nack bit */
48d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2CCON_IRPND	0x10	/* Interrupt pending bit */
49d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_MODE_MT	0xC0	/* Master Transmit Mode */
50d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_MODE_MR	0x80	/* Master Receive Mode */
51d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_START_STOP	0x20	/* START / STOP */
52d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_TXRX_ENA	0x10	/* I2C Tx/Rx enable */
53d3b63577SJean-Christophe PLAGNIOL-VILLARD 
54d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_TIMEOUT 1		/* 1 second */
55d3b63577SJean-Christophe PLAGNIOL-VILLARD 
56d3b63577SJean-Christophe PLAGNIOL-VILLARD static int GetI2CSDA(void)
57d3b63577SJean-Christophe PLAGNIOL-VILLARD {
58eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
59d3b63577SJean-Christophe PLAGNIOL-VILLARD 
60d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
61*d9abba82SC Nauman 	return (readl(&gpio->gpedat) & 0x8000) >> 15;
62d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
63d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
64*d9abba82SC Nauman 	return (readl(&gpio->pgdat) & 0x0020) >> 5;
65d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
66d3b63577SJean-Christophe PLAGNIOL-VILLARD }
67d3b63577SJean-Christophe PLAGNIOL-VILLARD 
68d3b63577SJean-Christophe PLAGNIOL-VILLARD #if 0
69d3b63577SJean-Christophe PLAGNIOL-VILLARD static void SetI2CSDA(int x)
70d3b63577SJean-Christophe PLAGNIOL-VILLARD {
71d3b63577SJean-Christophe PLAGNIOL-VILLARD 	rGPEDAT = (rGPEDAT & ~0x8000) | (x & 1) << 15;
72d3b63577SJean-Christophe PLAGNIOL-VILLARD }
73d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
74d3b63577SJean-Christophe PLAGNIOL-VILLARD 
75d3b63577SJean-Christophe PLAGNIOL-VILLARD static void SetI2CSCL(int x)
76d3b63577SJean-Christophe PLAGNIOL-VILLARD {
77eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
78d3b63577SJean-Christophe PLAGNIOL-VILLARD 
79d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
80*d9abba82SC Nauman 	writel((readl(&gpio->gpedat) & ~0x4000) | (x & 1) << 14, &gpio->gpedat);
81d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
82d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
83*d9abba82SC Nauman 	writel((readl(&gpio->pgdat) & ~0x0040) | (x & 1) << 6, &gpio->pgdat);
84d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
85d3b63577SJean-Christophe PLAGNIOL-VILLARD }
86d3b63577SJean-Christophe PLAGNIOL-VILLARD 
87d3b63577SJean-Christophe PLAGNIOL-VILLARD static int WaitForXfer(void)
88d3b63577SJean-Christophe PLAGNIOL-VILLARD {
89eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
90eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	int i;
91d3b63577SJean-Christophe PLAGNIOL-VILLARD 
92d3b63577SJean-Christophe PLAGNIOL-VILLARD 	i = I2C_TIMEOUT * 10000;
93*d9abba82SC Nauman 	while (!(readl(&i2c->iiccon) & I2CCON_IRPND) && (i > 0)) {
94d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(100);
95d3b63577SJean-Christophe PLAGNIOL-VILLARD 		i--;
96d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
97d3b63577SJean-Christophe PLAGNIOL-VILLARD 
98*d9abba82SC Nauman 	return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT;
99d3b63577SJean-Christophe PLAGNIOL-VILLARD }
100d3b63577SJean-Christophe PLAGNIOL-VILLARD 
101d3b63577SJean-Christophe PLAGNIOL-VILLARD static int IsACK(void)
102d3b63577SJean-Christophe PLAGNIOL-VILLARD {
103eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
104d3b63577SJean-Christophe PLAGNIOL-VILLARD 
105*d9abba82SC Nauman 	return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
106d3b63577SJean-Christophe PLAGNIOL-VILLARD }
107d3b63577SJean-Christophe PLAGNIOL-VILLARD 
108d3b63577SJean-Christophe PLAGNIOL-VILLARD static void ReadWriteByte(void)
109d3b63577SJean-Christophe PLAGNIOL-VILLARD {
110eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
111d3b63577SJean-Christophe PLAGNIOL-VILLARD 
112*d9abba82SC Nauman 	writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon);
113d3b63577SJean-Christophe PLAGNIOL-VILLARD }
114d3b63577SJean-Christophe PLAGNIOL-VILLARD 
115d3b63577SJean-Christophe PLAGNIOL-VILLARD void i2c_init(int speed, int slaveadd)
116d3b63577SJean-Christophe PLAGNIOL-VILLARD {
117eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
118eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
119d3b63577SJean-Christophe PLAGNIOL-VILLARD 	ulong freq, pres = 16, div;
120eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	int i;
121d3b63577SJean-Christophe PLAGNIOL-VILLARD 
122d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/* wait for some time to give previous transfer a chance to finish */
123d3b63577SJean-Christophe PLAGNIOL-VILLARD 
124d3b63577SJean-Christophe PLAGNIOL-VILLARD 	i = I2C_TIMEOUT * 1000;
125*d9abba82SC Nauman 	while ((readl(&i2c->iicstat) && I2CSTAT_BSY) && (i > 0)) {
126d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(1000);
127d3b63577SJean-Christophe PLAGNIOL-VILLARD 		i--;
128d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
129d3b63577SJean-Christophe PLAGNIOL-VILLARD 
130*d9abba82SC Nauman 	if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) {
131d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
132*d9abba82SC Nauman 		ulong old_gpecon = readl(&gpio->gpecon);
133d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
134d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
135*d9abba82SC Nauman 		ulong old_gpecon = readl(&gpio->pgcon);
136d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
137eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 		/* bus still busy probably by (most) previously interrupted
138eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 		   transfer */
139d3b63577SJean-Christophe PLAGNIOL-VILLARD 
140d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
141d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* set I2CSDA and I2CSCL (GPE15, GPE14) to GPIO */
142*d9abba82SC Nauman 		writel((readl(&gpio->gpecon) & ~0xF0000000) | 0x10000000,
143*d9abba82SC Nauman 		       &gpio->gpecon);
144d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
145d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
146d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* set I2CSDA and I2CSCL (PG5, PG6) to GPIO */
147*d9abba82SC Nauman 		writel((readl(&gpio->pgcon) & ~0x00003c00) | 0x00001000,
148*d9abba82SC Nauman 		       &gpio->pgcon);
149d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
150d3b63577SJean-Christophe PLAGNIOL-VILLARD 
151d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* toggle I2CSCL until bus idle */
152d3b63577SJean-Christophe PLAGNIOL-VILLARD 		SetI2CSCL(0);
153d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(1000);
154d3b63577SJean-Christophe PLAGNIOL-VILLARD 		i = 10;
155d3b63577SJean-Christophe PLAGNIOL-VILLARD 		while ((i > 0) && (GetI2CSDA() != 1)) {
156d3b63577SJean-Christophe PLAGNIOL-VILLARD 			SetI2CSCL(1);
157d3b63577SJean-Christophe PLAGNIOL-VILLARD 			udelay(1000);
158d3b63577SJean-Christophe PLAGNIOL-VILLARD 			SetI2CSCL(0);
159d3b63577SJean-Christophe PLAGNIOL-VILLARD 			udelay(1000);
160d3b63577SJean-Christophe PLAGNIOL-VILLARD 			i--;
161d3b63577SJean-Christophe PLAGNIOL-VILLARD 		}
162d3b63577SJean-Christophe PLAGNIOL-VILLARD 		SetI2CSCL(1);
163d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(1000);
164d3b63577SJean-Christophe PLAGNIOL-VILLARD 
165d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* restore pin functions */
166d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410
167*d9abba82SC Nauman 		writel(old_gpecon, &gpio->gpecon);
168d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
169d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400
170*d9abba82SC Nauman 		writel(old_gpecon, &gpio->pgcon);
171d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
172d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
173d3b63577SJean-Christophe PLAGNIOL-VILLARD 
174d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/* calculate prescaler and divisor values */
175d3b63577SJean-Christophe PLAGNIOL-VILLARD 	freq = get_PCLK();
176d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if ((freq / pres / (16 + 1)) > speed)
177d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* set prescaler to 512 */
178d3b63577SJean-Christophe PLAGNIOL-VILLARD 		pres = 512;
179d3b63577SJean-Christophe PLAGNIOL-VILLARD 
180d3b63577SJean-Christophe PLAGNIOL-VILLARD 	div = 0;
181d3b63577SJean-Christophe PLAGNIOL-VILLARD 	while ((freq / pres / (div + 1)) > speed)
182d3b63577SJean-Christophe PLAGNIOL-VILLARD 		div++;
183d3b63577SJean-Christophe PLAGNIOL-VILLARD 
184d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/* set prescaler, divisor according to freq, also set
185d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * ACKGEN, IRQ */
186*d9abba82SC Nauman 	writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
187d3b63577SJean-Christophe PLAGNIOL-VILLARD 
188d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/* init to SLAVE REVEIVE and set slaveaddr */
189*d9abba82SC Nauman 	writel(0, &i2c->iicstat);
190*d9abba82SC Nauman 	writel(slaveadd, &i2c->iicadd);
191d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/* program Master Transmit (and implicit STOP) */
192*d9abba82SC Nauman 	writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
193d3b63577SJean-Christophe PLAGNIOL-VILLARD 
194d3b63577SJean-Christophe PLAGNIOL-VILLARD }
195d3b63577SJean-Christophe PLAGNIOL-VILLARD 
196d3b63577SJean-Christophe PLAGNIOL-VILLARD /*
197d3b63577SJean-Christophe PLAGNIOL-VILLARD  * cmd_type is 0 for write, 1 for read.
198d3b63577SJean-Christophe PLAGNIOL-VILLARD  *
199d3b63577SJean-Christophe PLAGNIOL-VILLARD  * addr_len can take any value from 0-255, it is only limited
200d3b63577SJean-Christophe PLAGNIOL-VILLARD  * by the char, we could make it larger if needed. If it is
201d3b63577SJean-Christophe PLAGNIOL-VILLARD  * 0 we skip the address write cycle.
202d3b63577SJean-Christophe PLAGNIOL-VILLARD  */
203d3b63577SJean-Christophe PLAGNIOL-VILLARD static
204d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_transfer(unsigned char cmd_type,
205d3b63577SJean-Christophe PLAGNIOL-VILLARD 		 unsigned char chip,
206d3b63577SJean-Christophe PLAGNIOL-VILLARD 		 unsigned char addr[],
207d3b63577SJean-Christophe PLAGNIOL-VILLARD 		 unsigned char addr_len,
208d3b63577SJean-Christophe PLAGNIOL-VILLARD 		 unsigned char data[], unsigned short data_len)
209d3b63577SJean-Christophe PLAGNIOL-VILLARD {
210eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
211eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	int i, result;
212d3b63577SJean-Christophe PLAGNIOL-VILLARD 
213d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (data == 0 || data_len == 0) {
214d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/*Don't support data transfer of no length or to address 0 */
215d3b63577SJean-Christophe PLAGNIOL-VILLARD 		printf("i2c_transfer: bad call\n");
216d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return I2C_NOK;
217d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
218d3b63577SJean-Christophe PLAGNIOL-VILLARD 
219d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/* Check I2C bus idle */
220d3b63577SJean-Christophe PLAGNIOL-VILLARD 	i = I2C_TIMEOUT * 1000;
221*d9abba82SC Nauman 	while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) {
222d3b63577SJean-Christophe PLAGNIOL-VILLARD 		udelay(1000);
223d3b63577SJean-Christophe PLAGNIOL-VILLARD 		i--;
224d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
225d3b63577SJean-Christophe PLAGNIOL-VILLARD 
226*d9abba82SC Nauman 	if (readl(&i2c->iicstat) & I2CSTAT_BSY)
227d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return I2C_NOK_TOUT;
228d3b63577SJean-Christophe PLAGNIOL-VILLARD 
229*d9abba82SC Nauman 	writel(readl(&i2c->iiccon) | 0x80, &i2c->iiccon);
230d3b63577SJean-Christophe PLAGNIOL-VILLARD 	result = I2C_OK;
231d3b63577SJean-Christophe PLAGNIOL-VILLARD 
232d3b63577SJean-Christophe PLAGNIOL-VILLARD 	switch (cmd_type) {
233d3b63577SJean-Christophe PLAGNIOL-VILLARD 	case I2C_WRITE:
234d3b63577SJean-Christophe PLAGNIOL-VILLARD 		if (addr && addr_len) {
235*d9abba82SC Nauman 			writel(chip, &i2c->iicds);
236d3b63577SJean-Christophe PLAGNIOL-VILLARD 			/* send START */
237eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 			writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
238*d9abba82SC Nauman 			       &i2c->iicstat);
239d3b63577SJean-Christophe PLAGNIOL-VILLARD 			i = 0;
240d3b63577SJean-Christophe PLAGNIOL-VILLARD 			while ((i < addr_len) && (result == I2C_OK)) {
241d3b63577SJean-Christophe PLAGNIOL-VILLARD 				result = WaitForXfer();
242*d9abba82SC Nauman 				writel(addr[i], &i2c->iicds);
243d3b63577SJean-Christophe PLAGNIOL-VILLARD 				ReadWriteByte();
244d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i++;
245d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
246d3b63577SJean-Christophe PLAGNIOL-VILLARD 			i = 0;
247d3b63577SJean-Christophe PLAGNIOL-VILLARD 			while ((i < data_len) && (result == I2C_OK)) {
248d3b63577SJean-Christophe PLAGNIOL-VILLARD 				result = WaitForXfer();
249*d9abba82SC Nauman 				writel(data[i], &i2c->iicds);
250d3b63577SJean-Christophe PLAGNIOL-VILLARD 				ReadWriteByte();
251d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i++;
252d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
253d3b63577SJean-Christophe PLAGNIOL-VILLARD 		} else {
254*d9abba82SC Nauman 			writel(chip, &i2c->iicds);
255d3b63577SJean-Christophe PLAGNIOL-VILLARD 			/* send START */
256eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 			writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
257*d9abba82SC Nauman 			       &i2c->iicstat);
258d3b63577SJean-Christophe PLAGNIOL-VILLARD 			i = 0;
259d3b63577SJean-Christophe PLAGNIOL-VILLARD 			while ((i < data_len) && (result = I2C_OK)) {
260d3b63577SJean-Christophe PLAGNIOL-VILLARD 				result = WaitForXfer();
261*d9abba82SC Nauman 				writel(data[i], &i2c->iicds);
262d3b63577SJean-Christophe PLAGNIOL-VILLARD 				ReadWriteByte();
263d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i++;
264d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
265d3b63577SJean-Christophe PLAGNIOL-VILLARD 		}
266d3b63577SJean-Christophe PLAGNIOL-VILLARD 
267d3b63577SJean-Christophe PLAGNIOL-VILLARD 		if (result == I2C_OK)
268d3b63577SJean-Christophe PLAGNIOL-VILLARD 			result = WaitForXfer();
269d3b63577SJean-Christophe PLAGNIOL-VILLARD 
270d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* send STOP */
271*d9abba82SC Nauman 		writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
272d3b63577SJean-Christophe PLAGNIOL-VILLARD 		ReadWriteByte();
273d3b63577SJean-Christophe PLAGNIOL-VILLARD 		break;
274d3b63577SJean-Christophe PLAGNIOL-VILLARD 
275d3b63577SJean-Christophe PLAGNIOL-VILLARD 	case I2C_READ:
276d3b63577SJean-Christophe PLAGNIOL-VILLARD 		if (addr && addr_len) {
277*d9abba82SC Nauman 			writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
278*d9abba82SC Nauman 			writel(chip, &i2c->iicds);
279d3b63577SJean-Christophe PLAGNIOL-VILLARD 			/* send START */
280*d9abba82SC Nauman 			writel(readl(&i2c->iicstat) | I2C_START_STOP,
281*d9abba82SC Nauman 			       &i2c->iicstat);
282d3b63577SJean-Christophe PLAGNIOL-VILLARD 			result = WaitForXfer();
283d3b63577SJean-Christophe PLAGNIOL-VILLARD 			if (IsACK()) {
284d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i = 0;
285d3b63577SJean-Christophe PLAGNIOL-VILLARD 				while ((i < addr_len) && (result == I2C_OK)) {
286*d9abba82SC Nauman 					writel(addr[i], &i2c->iicds);
287d3b63577SJean-Christophe PLAGNIOL-VILLARD 					ReadWriteByte();
288d3b63577SJean-Christophe PLAGNIOL-VILLARD 					result = WaitForXfer();
289d3b63577SJean-Christophe PLAGNIOL-VILLARD 					i++;
290d3b63577SJean-Christophe PLAGNIOL-VILLARD 				}
291d3b63577SJean-Christophe PLAGNIOL-VILLARD 
292*d9abba82SC Nauman 				writel(chip, &i2c->iicds);
293d3b63577SJean-Christophe PLAGNIOL-VILLARD 				/* resend START */
294eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 				writel(I2C_MODE_MR | I2C_TXRX_ENA |
295*d9abba82SC Nauman 				       I2C_START_STOP, &i2c->iicstat);
296d3b63577SJean-Christophe PLAGNIOL-VILLARD 				ReadWriteByte();
297d3b63577SJean-Christophe PLAGNIOL-VILLARD 				result = WaitForXfer();
298d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i = 0;
299d3b63577SJean-Christophe PLAGNIOL-VILLARD 				while ((i < data_len) && (result == I2C_OK)) {
300d3b63577SJean-Christophe PLAGNIOL-VILLARD 					/* disable ACK for final READ */
301d3b63577SJean-Christophe PLAGNIOL-VILLARD 					if (i == data_len - 1)
302*d9abba82SC Nauman 						writel(readl(&i2c->iiccon)
303*d9abba82SC Nauman 						       & ~0x80, &i2c->iiccon);
304d3b63577SJean-Christophe PLAGNIOL-VILLARD 					ReadWriteByte();
305d3b63577SJean-Christophe PLAGNIOL-VILLARD 					result = WaitForXfer();
306*d9abba82SC Nauman 					data[i] = readl(&i2c->iicds);
307d3b63577SJean-Christophe PLAGNIOL-VILLARD 					i++;
308d3b63577SJean-Christophe PLAGNIOL-VILLARD 				}
309d3b63577SJean-Christophe PLAGNIOL-VILLARD 			} else {
310d3b63577SJean-Christophe PLAGNIOL-VILLARD 				result = I2C_NACK;
311d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
312d3b63577SJean-Christophe PLAGNIOL-VILLARD 
313d3b63577SJean-Christophe PLAGNIOL-VILLARD 		} else {
314*d9abba82SC Nauman 			writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
315*d9abba82SC Nauman 			writel(chip, &i2c->iicds);
316d3b63577SJean-Christophe PLAGNIOL-VILLARD 			/* send START */
317*d9abba82SC Nauman 			writel(readl(&i2c->iicstat) | I2C_START_STOP,
318*d9abba82SC Nauman 			       &i2c->iicstat);
319d3b63577SJean-Christophe PLAGNIOL-VILLARD 			result = WaitForXfer();
320d3b63577SJean-Christophe PLAGNIOL-VILLARD 
321d3b63577SJean-Christophe PLAGNIOL-VILLARD 			if (IsACK()) {
322d3b63577SJean-Christophe PLAGNIOL-VILLARD 				i = 0;
323d3b63577SJean-Christophe PLAGNIOL-VILLARD 				while ((i < data_len) && (result == I2C_OK)) {
324d3b63577SJean-Christophe PLAGNIOL-VILLARD 					/* disable ACK for final READ */
325d3b63577SJean-Christophe PLAGNIOL-VILLARD 					if (i == data_len - 1)
326*d9abba82SC Nauman 						writel(readl(&i2c->iiccon) &
327*d9abba82SC Nauman 						       ~0x80, &i2c->iiccon);
328d3b63577SJean-Christophe PLAGNIOL-VILLARD 					ReadWriteByte();
329d3b63577SJean-Christophe PLAGNIOL-VILLARD 					result = WaitForXfer();
330*d9abba82SC Nauman 					data[i] = readl(&i2c->iicds);
331d3b63577SJean-Christophe PLAGNIOL-VILLARD 					i++;
332d3b63577SJean-Christophe PLAGNIOL-VILLARD 				}
333d3b63577SJean-Christophe PLAGNIOL-VILLARD 			} else {
334d3b63577SJean-Christophe PLAGNIOL-VILLARD 				result = I2C_NACK;
335d3b63577SJean-Christophe PLAGNIOL-VILLARD 			}
336d3b63577SJean-Christophe PLAGNIOL-VILLARD 		}
337d3b63577SJean-Christophe PLAGNIOL-VILLARD 
338d3b63577SJean-Christophe PLAGNIOL-VILLARD 		/* send STOP */
339*d9abba82SC Nauman 		writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
340d3b63577SJean-Christophe PLAGNIOL-VILLARD 		ReadWriteByte();
341d3b63577SJean-Christophe PLAGNIOL-VILLARD 		break;
342d3b63577SJean-Christophe PLAGNIOL-VILLARD 
343d3b63577SJean-Christophe PLAGNIOL-VILLARD 	default:
344d3b63577SJean-Christophe PLAGNIOL-VILLARD 		printf("i2c_transfer: bad call\n");
345d3b63577SJean-Christophe PLAGNIOL-VILLARD 		result = I2C_NOK;
346d3b63577SJean-Christophe PLAGNIOL-VILLARD 		break;
347d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
348d3b63577SJean-Christophe PLAGNIOL-VILLARD 
349d3b63577SJean-Christophe PLAGNIOL-VILLARD 	return (result);
350d3b63577SJean-Christophe PLAGNIOL-VILLARD }
351d3b63577SJean-Christophe PLAGNIOL-VILLARD 
352d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_probe(uchar chip)
353d3b63577SJean-Christophe PLAGNIOL-VILLARD {
354d3b63577SJean-Christophe PLAGNIOL-VILLARD 	uchar buf[1];
355d3b63577SJean-Christophe PLAGNIOL-VILLARD 
356d3b63577SJean-Christophe PLAGNIOL-VILLARD 	buf[0] = 0;
357d3b63577SJean-Christophe PLAGNIOL-VILLARD 
358d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/*
359d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * What is needed is to send the chip address and verify that the
360d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * address was <ACK>ed (i.e. there was a chip at that address which
361d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * drove the data line low).
362d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 */
363eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 	return i2c_transfer(I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK;
364d3b63577SJean-Christophe PLAGNIOL-VILLARD }
365d3b63577SJean-Christophe PLAGNIOL-VILLARD 
366d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
367d3b63577SJean-Christophe PLAGNIOL-VILLARD {
368d3b63577SJean-Christophe PLAGNIOL-VILLARD 	uchar xaddr[4];
369d3b63577SJean-Christophe PLAGNIOL-VILLARD 	int ret;
370d3b63577SJean-Christophe PLAGNIOL-VILLARD 
371d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 4) {
372d3b63577SJean-Christophe PLAGNIOL-VILLARD 		printf("I2C read: addr len %d not supported\n", alen);
373d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return 1;
374d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
375d3b63577SJean-Christophe PLAGNIOL-VILLARD 
376d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 0) {
377d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[0] = (addr >> 24) & 0xFF;
378d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[1] = (addr >> 16) & 0xFF;
379d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[2] = (addr >> 8) & 0xFF;
380d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[3] = addr & 0xFF;
381d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
382d3b63577SJean-Christophe PLAGNIOL-VILLARD 
383d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
384d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/*
385d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * EEPROM chips that implement "address overflow" are ones
386d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
387d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * address and the extra bits end up in the "chip address"
388d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * bit slots. This makes a 24WC08 (1Kbyte) chip look like
389d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * four 256 byte chips.
390d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 *
391d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * Note that we consider the length of the address field to
392d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * still be one byte because the extra address bits are
393d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * hidden in the chip address.
394d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 */
395d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 0)
396eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 		chip |= ((addr >> (alen * 8)) &
397eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 			 CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
398d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
399d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if ((ret =
400d3b63577SJean-Christophe PLAGNIOL-VILLARD 	     i2c_transfer(I2C_READ, chip << 1, &xaddr[4 - alen], alen,
401d3b63577SJean-Christophe PLAGNIOL-VILLARD 			  buffer, len)) != 0) {
402d3b63577SJean-Christophe PLAGNIOL-VILLARD 		printf("I2c read: failed %d\n", ret);
403d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return 1;
404d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
405d3b63577SJean-Christophe PLAGNIOL-VILLARD 	return 0;
406d3b63577SJean-Christophe PLAGNIOL-VILLARD }
407d3b63577SJean-Christophe PLAGNIOL-VILLARD 
408d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
409d3b63577SJean-Christophe PLAGNIOL-VILLARD {
410d3b63577SJean-Christophe PLAGNIOL-VILLARD 	uchar xaddr[4];
411d3b63577SJean-Christophe PLAGNIOL-VILLARD 
412d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 4) {
413d3b63577SJean-Christophe PLAGNIOL-VILLARD 		printf("I2C write: addr len %d not supported\n", alen);
414d3b63577SJean-Christophe PLAGNIOL-VILLARD 		return 1;
415d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
416d3b63577SJean-Christophe PLAGNIOL-VILLARD 
417d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 0) {
418d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[0] = (addr >> 24) & 0xFF;
419d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[1] = (addr >> 16) & 0xFF;
420d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[2] = (addr >> 8) & 0xFF;
421d3b63577SJean-Christophe PLAGNIOL-VILLARD 		xaddr[3] = addr & 0xFF;
422d3b63577SJean-Christophe PLAGNIOL-VILLARD 	}
423d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
424d3b63577SJean-Christophe PLAGNIOL-VILLARD 	/*
425d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * EEPROM chips that implement "address overflow" are ones
426d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
427d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * address and the extra bits end up in the "chip address"
428d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * bit slots. This makes a 24WC08 (1Kbyte) chip look like
429d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * four 256 byte chips.
430d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 *
431d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * Note that we consider the length of the address field to
432d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * still be one byte because the extra address bits are
433d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 * hidden in the chip address.
434d3b63577SJean-Christophe PLAGNIOL-VILLARD 	 */
435d3b63577SJean-Christophe PLAGNIOL-VILLARD 	if (alen > 0)
436eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 		chip |= ((addr >> (alen * 8)) &
437eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 			 CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
438d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif
439d3b63577SJean-Christophe PLAGNIOL-VILLARD 	return (i2c_transfer
440d3b63577SJean-Christophe PLAGNIOL-VILLARD 		(I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer,
441d3b63577SJean-Christophe PLAGNIOL-VILLARD 		 len) != 0);
442d3b63577SJean-Christophe PLAGNIOL-VILLARD }
443d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_HARD_I2C */
444