xref: /inferno-os/os/sa1110/i2cgpio.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 /*
2  *	I2C master emulation using GPIO pins.
3  *	7 bit addressing only.
4  */
5 #include	"u.h"
6 #include	"../port/lib.h"
7 #include	"mem.h"
8 #include	"dat.h"
9 #include	"fns.h"
10 #include	"../port/error.h"
11 #include	"io.h"
12 #include	"i2c.h"
13 
14 /* GPIO bitmasks */
15 static  struct {
16 	Lock;
17 	ulong	sda;
18 	ulong	scl;
19 } i2c;
20 
21 
22 /* set pin level high by disabling output drive and allowing pull-up to work */
23 static void
i2c_set(int pin)24 i2c_set(int pin)
25 {
26 	GPIOREG->gpdr &= ~pin;	/* configure pin as input */
27 }
28 
29 /* set pin level low with output drive */
30 static void
i2c_clear(int pin)31 i2c_clear(int pin)
32 {
33 	GPIOREG->gpcr = pin;	/* set pin output low */
34 	GPIOREG->gpdr |= pin;	/* configure pin as output */
35 }
36 
37 static int
i2c_getack(void)38 i2c_getack(void)
39 {
40 	/* scl is low, sda is not defined */
41 
42 	i2c_set(i2c.sda);		/* set data high */
43 	timer_delay(US2TMR(3));
44 
45 	i2c_set(i2c.scl);		/* raise clock */
46 	timer_delay(US2TMR(5));
47 
48 	/* check for ack from slave! */
49 	if (GPIOREG->gplr & i2c.sda)
50 		print("I2C: Warning did not get ack!\n");
51 
52 	i2c_clear(i2c.sda);		/* lower data */
53 	i2c_clear(i2c.scl);		/* lower clock */
54 	timer_delay(US2TMR(3));
55 
56 	/* scl is low, sda is low */
57 	return 1;
58 }
59 
60 
61 static void
i2c_putack(void)62 i2c_putack(void)
63 {
64 	/* scl is low, sda is not defined */
65 
66 	timer_delay(US2TMR(3));		/* lower data */
67 	i2c_clear(i2c.sda);
68 
69 	i2c_set(i2c.scl);			/* pulse clock */
70 	timer_delay(US2TMR(5));
71 
72 	i2c_clear(i2c.scl);			/* lower clock */
73 	timer_delay(US2TMR(3));
74 
75 	/* scl is low, sda is low */
76 }
77 
78 
79 static void
i2c_putbyte(uchar b)80 i2c_putbyte(uchar b)
81 {
82 	uchar m;
83 
84 	/* start condition has been sent */
85 	/* scl is low, sda is low */
86 
87 	for(m=0x80; m; m >>= 1) {
88 
89 		/* set data bit */
90 		if(b&m)
91 			i2c_set(i2c.sda);
92 		else
93 			i2c_clear(i2c.sda);
94 
95 		/* pulse clock */
96 		timer_delay(US2TMR(3));
97 		i2c_set(i2c.scl);
98 		timer_delay(US2TMR(5));
99 		i2c_clear(i2c.scl);
100 		timer_delay(US2TMR(3));
101 	}
102 
103 	i2c_clear(i2c.sda);
104 	/* scl is low, sda is low */
105 }
106 
107 
108 static uchar
i2c_getbyte(void)109 i2c_getbyte(void)
110 {
111 	/* start condition, address and ack been done */
112 	/* scl is low, sda is high */
113 	uchar data = 0x00;
114 	int i;
115 
116 	i2c_set(i2c.sda);
117 	for (i=7; i >= 0; i--) {
118 
119 		timer_delay(US2TMR(3));
120 
121 		/* raise clock */
122 		i2c_set(i2c.scl);
123 		timer_delay(US2TMR(5));
124 
125 		/* sample data */
126 		if(GPIOREG->gplr & i2c.sda)
127 			data |= 1<<i;
128 
129 		/* lower clock */
130 		i2c_clear(i2c.scl);
131 		timer_delay(US2TMR(3));
132 	}
133 
134 	i2c_clear(i2c.sda);
135 	return data;
136 }
137 
138 /* generate I2C start condition */
139 static int
i2c_start(void)140 i2c_start(void)
141 {
142 	/* check that both scl and sda are high */
143 	if ((GPIOREG->gplr & (i2c.sda | i2c.scl)) != (i2c.sda | i2c.scl))
144 		print("I2C: Bus not clear when attempting start condition\n");
145 
146 	i2c_clear(i2c.sda);			/* lower sda */
147 	timer_delay(US2TMR(5));
148 
149 	i2c_clear(i2c.scl);			/* lower scl */
150 	timer_delay(US2TMR(3));
151 
152 	return 1;
153 }
154 
155 /* generate I2C stop condition */
156 static int
i2c_stop(void)157 i2c_stop(void)
158 {
159 	/* clock is low, data is low */
160 	timer_delay(US2TMR(3));
161 
162 	i2c_set(i2c.scl);
163 	timer_delay(US2TMR(5));
164 
165 	i2c_set(i2c.sda);
166 
167 	timer_delay(MS2TMR(1));		/* ensure separation between commands */
168 
169 	return 1;
170 }
171 
172 /*
173  * external I2C interface
174  */
175 
176 /* write a byte over the i2c bus */
177 int
i2c_write_byte(uchar addr,uchar data)178 i2c_write_byte(uchar addr, uchar data)
179 {
180 	int rc = 0;
181 
182 	ilock(&i2c);
183 	if(i2c_start() < 0)			/* start condition */
184 		rc = -1;
185 
186 	i2c_putbyte(addr & 0xfe);		/* address byte (LSB = 0 -> write) */
187 
188 	if (i2c_getack() < 0)			/* get ack */
189 		rc = -2;
190 
191 	i2c_putbyte(data);			/* data byte */
192 
193 	if (i2c_getack() < 0)			/* get ack */
194 		rc = -3;
195 
196 	if (i2c_stop() < 0)
197 		rc = -4;			/* stop condition */
198 	iunlock(&i2c);
199 
200 	return rc;
201 }
202 
203 /* read a byte over the i2c bus */
204 int
i2c_read_byte(uchar addr,uchar * data)205 i2c_read_byte(uchar addr, uchar *data)
206 {
207 	int rc = 0;
208 
209 	ilock(&i2c);
210 	if(i2c_start() < 0)			/* start condition */
211 		rc = -1;
212 
213 	i2c_putbyte(addr | 0x01);		/* address byte (LSB = 1 -> read) */
214 
215 	if(i2c_getack() < 0)			/* get ack */
216 		rc = -2;
217 
218 	*data = i2c_getbyte();			/* data byte */
219 
220 	i2c_putack();				/* put ack */
221 
222 	if (i2c_stop() < 0) 			/* stop condition */
223 		rc = -4;
224 	iunlock(&i2c);
225 
226 	return rc;
227 }
228 
229 void
i2c_reset(void)230 i2c_reset(void)
231 {
232 	/* initialise bitmasks */
233 	i2c.sda = (1 << gpio_i2c_sda);
234 	i2c.scl = (1 << gpio_i2c_scl);
235 
236 	/* ensure that both clock and data are high */
237 	i2c_set(i2c.sda);
238 	i2c_set(i2c.scl);
239 	timer_delay(MS2TMR(5));
240 }
241 
242 
243 /*
244  * external pin set/clear interface
245  */
246 uchar i2c_iactl[2] = { 0xff, 0xff };		/* defaults overridden in arch?????.c */
247 
248 int
i2c_setpin(int b)249 i2c_setpin(int b)
250 {
251 	int i = b>>3;
252 
253 	ilock(&i2c);
254 	i2c_iactl[i] |= (1 << (b&7));
255 	iunlock(&i2c);
256 	return i2c_write_byte(0x40 | (i << 1), i2c_iactl[i]);
257 }
258 
259 int
i2c_clrpin(int b)260 i2c_clrpin(int b)
261 {
262 	int i = b>>3;
263 
264 	ilock(&i2c);
265 	i2c_iactl[i] &= ~(1 << (b&7));
266 	iunlock(&i2c);
267 	return i2c_write_byte(0x40 | (i << 1), i2c_iactl[i]);
268 }
269 
270 int
i2c_getpin(int b)271 i2c_getpin(int b)
272 {
273 	return (i2c_iactl[(b>>3)&1] & (1<<(b&7))) != 0;
274 }
275