summaryrefslogtreecommitdiffstats
path: root/u-boot/board/goldelico/gta04/i2c1-fix.c
blob: 2e21acc0bc9ef49bc7a177aed3cb7394a20e74c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/* u-boot driver for the layout issue of the I2C1 on the GTA04A2 board
 *
 * Copyright (C) 2010 by Golden Delicious Computers GmbH&Co. KG
 * Author: H. Nikolaus Schaller <hns@goldelico.com>
 * All rights reserved.
 *
 * 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., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 *
 * fragments taken from http://www.koders.com/c/fidDC48AC380BE8326E4C3F14F8195BBDB8ED713932.aspx?s=%22Cam%22#L4
 * see: // see http://www.cc5x.de/I2C.html
 *
 */


#include <common.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/arch/i2c.h>
#include <asm/io.h>

#ifdef CONFIG_OMAP3_GTA04A2

/* #error this is for GTA04A2 only */

#include "../../../drivers/i2c/omap24xx_i2c.h"

/* this is a bit-bang driver for the I2C1
 since the GTA04A2 board has swapped SCL and SDA.
 We can access these lines in System Test Mode
 through the I2C_SYSTEST register in ST_EN mode
 */

#define I2C_SYSTEST_ST_EN	(1 << 15) /* System test enable */
#define I2C_SYSTEST_FREE	(1 << 14) /* Free running mode, on brkpoint) */
#define I2C_SYSTEST_TMODE_MASK	(3 << 12) /* Test mode select */
#define I2C_SYSTEST_TMODE_SHIFT	(12)	  /* Test mode select */
#define I2C_SYSTEST_SCL_I	(1 << 3)  /* SCL line sense input value */
#define I2C_SYSTEST_SCL_O	(1 << 2)  /* SCL line drive output value */
#define I2C_SYSTEST_SDA_I	(1 << 1)  /* SDA line sense input value */
#define I2C_SYSTEST_SDA_O	(1 << 0)  /* SDA line drive output value */

#define KBIT	100

static inline void delay_full_clock(void)
{
	udelay(1000/KBIT);
}
static inline void delay_half_clock(void)
{
	udelay(500/KBIT);
}
static inline void delay_quarter_clock(void)
{
	udelay(250/KBIT);
}

static inline void setscl(struct i2c *base, int state)
{
	if(base == (struct i2c *)I2C_BASE1)
		{ // swap SCL&SDA on I2C1 due to hardware bug
			if (state) {
				base->systest |= I2C_SYSTEST_SDA_O;
			} else {
				base->systest &= ~I2C_SYSTEST_SDA_O;
			}			
		}
	else
		{		
			if (state) {
				base->systest |= I2C_SYSTEST_SCL_O;
			} else {
				base->systest &= ~I2C_SYSTEST_SCL_O;
			}
		}
}

static inline void setsda(struct i2c *base, int state)
{
	if(base == (struct i2c *)I2C_BASE1)
		{ // swap SCL&SDA on I2C1 due to hardware bug
			if (state) {
				base->systest |= I2C_SYSTEST_SCL_O;
			} else {
				base->systest &= ~I2C_SYSTEST_SCL_O;
			}
		}
	else
		{
			if (state) {
				base->systest |= I2C_SYSTEST_SDA_O;
			} else {
				base->systest &= ~I2C_SYSTEST_SDA_O;
			}
		}
} 

static inline int getscl(struct i2c *base)
{
	if(base == (struct i2c *)I2C_BASE1)
		{ // swap SCL&SDA on I2C1 due to hardware bug
			return (base->systest & I2C_SYSTEST_SDA_I) ? 1 : 0;
		}
	else
		return (base->systest & I2C_SYSTEST_SCL_I) ? 1 : 0;
}

static inline int getsda(struct i2c *base)
{
	if(base == (struct i2c *)I2C_BASE1)
		{ // swap SCL&SDA on I2C1 due to hardware bug
			return (base->systest & I2C_SYSTEST_SCL_I) ? 1 : 0;			
		}
	else
		return (base->systest & I2C_SYSTEST_SDA_I) ? 1 : 0;
}

static inline void busidle(struct i2c *base)
{
	/*
	 * float the SCL and SDA lines. The lines have pull-ups
	 */
	setscl(base, 1);
	setsda(base, 1);
}

static inline void start(struct i2c *base)
{
	busidle(base);
	delay_full_clock();
	setsda(base, 0);	// switch data during SCL=H -> this is START
	delay_quarter_clock();
	setscl(base, 0);	// switch clock -> prepare for bits
	//	printf("start\n");
}

static inline void stop(struct i2c *base)
{
	setsda(base, 0);	// set next data bit
	delay_quarter_clock();
	setscl(base, 1);	// this is like sending a 0 bit
	delay_half_clock();
	setsda(base, 1);	// but switch data during clock -> STOP
	delay_quarter_clock();
}

static inline void write_bit(struct i2c *base, int bit)
{
//	printf("write bit %d\n", bit);
	setsda(base, bit);
	delay_quarter_clock();
	setscl(base, 1);
	delay_half_clock();
	setscl(base, 0);
	delay_quarter_clock();
}

static inline int read_bit(struct i2c *base)
{
	int bit;
	setsda(base, 1);	// so that we can read from the OC line...
	delay_quarter_clock();
	setscl(base, 1);
	delay_half_clock();
	setscl(base, 0);
	bit = getsda(base);
	delay_quarter_clock();
//	printf("read bit %d\n", bit);
	return bit;
}

static int write_byte(struct i2c *base, int byte)
{
	int i;
	
	for (i=7; i>=0; i--)
		write_bit(base, (byte & (1<<i)) != 0);
	return read_bit(base) == 0; // ok (1) if NACK is asserted
}

static int read_byte(struct i2c *base)
{
	int i;
	u8 byte=0;
		
	for (i=7; i>=0; i--)
		byte = (byte << 1) | read_bit(base);
	return byte;
}

int i2c_bitbang_probe(struct i2c *base, unchar devaddr)
{
//	printf("i2c probe %02x SDA %d SCL %d\n", devaddr, getsda(base), getscl(base));
	start(base);
	if (write_byte(base, (devaddr<<1))) { // write probe
		stop(base);	// send stop condition
		return 0;	// ok
	}
	stop(base);	// send stop condition
	return 1;	// fail
}

int i2c_bitbang_write_byte (struct i2c *base, u8 devaddr, u8 regoffset, u8 value)
{
//	printf("i2c-bb write dev=%02x regoffset=%02x value=%02x\n", devaddr, regoffset, value);
	start(base);
	if (write_byte(base, (devaddr<<1))) { // send chip address for write command
		if (write_byte(base, regoffset)) { // send register offset
			if (write_byte(base, value)) { // send value
				stop(base);	// send stop condition
				return 0;	// ok
			}
		}
	}
	stop(base);	// send stop condition
	return 1;	// fail
}

int i2c_bitbang_read_byte (struct i2c *base, u8 devaddr, u8 regoffset, u8 * value)
{
//	printf("i2c-bb read dev=%02x regoffset=%02x ", devaddr, regoffset);
	start(base);
	if (write_byte(base, (devaddr<<1))) { // send chip address for write command
		if (write_byte(base, regoffset)) { // send register offset - this defines where we want to read
			start(base);	// repeated start
			if (write_byte(base, (devaddr<<1) | 1)) { // send chip address for read command
				*value = read_byte(base);
//				printf("%02x\n", *value);
				write_bit(base, 1);	// NAK
				stop(base);	// send stop condition
				return 0;	// ok
			}
			else {
//				printf(" 2nd chip address error ");
			}
		}
		else {
//			printf(" offset error ");
		}
	}
	else {
//		printf(" chip address error ");
	}

	stop(base);	// send stop condition
	return 1;	// fail
}

void i2c_bitbang_init(struct i2c *base)
{
	writew(I2C_SYSTEST_ST_EN | I2C_SYSTEST_FREE | (3 << I2C_SYSTEST_TMODE_SHIFT), &base->systest);
	busidle(base);
//	printf("i2c-bb init base=%08x\n", base);
#if OLD
	printf("I2C_SYSTEST1 *0x4807003C = %04x\n", readw(0x4807003C));
	printf("I2C_SYSTEST2 *0x4807203C = %04x\n", readw(0x4807203C));
	printf("I2C_SYSTEST3 *0x4806003C = %04x\n", readw(0x4806003C));
	setscl(base, 0);
	delay_full_clock();
	printf("I2C_SYSTEST2 *0x4807203C = %04x\n", readw(0x4807203C));
	setscl(base, 1);
	delay_full_clock();
	printf("I2C_SYSTEST2 *0x4807203C = %04x\n", readw(0x4807203C));
#endif
}


void i2c_bitbang_close(struct i2c *base)
{
	busidle(base);	
	base->systest = 0; // get out of test mode
}

#else

/* not required */

#endif