1 | /* I2C bus driver for mpc8540-based boards */ |
---|
2 | |
---|
3 | /* |
---|
4 | * Authorship |
---|
5 | * ---------- |
---|
6 | * This software ('mvme3100' RTEMS BSP) was created by |
---|
7 | * |
---|
8 | * Till Straumann <strauman@slac.stanford.edu>, 2005-2007, |
---|
9 | * Stanford Linear Accelerator Center, Stanford University. |
---|
10 | * |
---|
11 | * Acknowledgement of sponsorship |
---|
12 | * ------------------------------ |
---|
13 | * The 'mvme3100' BSP was produced by |
---|
14 | * the Stanford Linear Accelerator Center, Stanford University, |
---|
15 | * under Contract DE-AC03-76SFO0515 with the Department of Energy. |
---|
16 | * |
---|
17 | * Government disclaimer of liability |
---|
18 | * ---------------------------------- |
---|
19 | * Neither the United States nor the United States Department of Energy, |
---|
20 | * nor any of their employees, makes any warranty, express or implied, or |
---|
21 | * assumes any legal liability or responsibility for the accuracy, |
---|
22 | * completeness, or usefulness of any data, apparatus, product, or process |
---|
23 | * disclosed, or represents that its use would not infringe privately owned |
---|
24 | * rights. |
---|
25 | * |
---|
26 | * Stanford disclaimer of liability |
---|
27 | * -------------------------------- |
---|
28 | * Stanford University makes no representations or warranties, express or |
---|
29 | * implied, nor assumes any liability for the use of this software. |
---|
30 | * |
---|
31 | * Stanford disclaimer of copyright |
---|
32 | * -------------------------------- |
---|
33 | * Stanford University, owner of the copyright, hereby disclaims its |
---|
34 | * copyright and all other rights in this software. Hence, anyone may |
---|
35 | * freely use it for any purpose without restriction. |
---|
36 | * |
---|
37 | * Maintenance of notices |
---|
38 | * ---------------------- |
---|
39 | * In the interest of clarity regarding the origin and status of this |
---|
40 | * SLAC software, this and all the preceding Stanford University notices |
---|
41 | * are to remain affixed to any copy or derivative of this software made |
---|
42 | * or distributed by the recipient and are to be affixed to any copy of |
---|
43 | * software made or distributed by the recipient that contains a copy or |
---|
44 | * derivative of this software. |
---|
45 | * |
---|
46 | * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 |
---|
47 | */ |
---|
48 | |
---|
49 | /* Note: We maintain base address, IRQ etc. statically and |
---|
50 | * globally. We don't bother creating driver-specific |
---|
51 | * data or using the bus handle but simply assume |
---|
52 | * this is the only 8540/i2c bus in the system. |
---|
53 | * Proper support for multiple instances would not |
---|
54 | * be very hard to add but I don't see the point... |
---|
55 | */ |
---|
56 | |
---|
57 | #include <rtems.h> |
---|
58 | #include <bsp.h> |
---|
59 | #include <rtems/libi2c.h> |
---|
60 | #include <bsp/irq.h> |
---|
61 | #include <libcpu/spr.h> |
---|
62 | #include <libcpu/io.h> |
---|
63 | #include <rtems/bspIo.h> |
---|
64 | #include <rtems/score/sysstate.h> |
---|
65 | |
---|
66 | #include <bsp/mpc8540_i2c_busdrv.h> |
---|
67 | |
---|
68 | #define STATIC static |
---|
69 | |
---|
70 | /* I2C controller register definitions */ |
---|
71 | #define I2CADR 0x3000 |
---|
72 | #define I2CFDR 0x3004 |
---|
73 | #define I2CCR 0x3008 |
---|
74 | #define I2CCR_MEN (1<<(7-0)) |
---|
75 | #define I2CCR_MIEN (1<<(7-1)) |
---|
76 | #define I2CCR_MSTA (1<<(7-2)) |
---|
77 | #define I2CCR_MTX (1<<(7-3)) |
---|
78 | #define I2CCR_TXAK (1<<(7-4)) |
---|
79 | #define I2CCR_RSTA (1<<(7-5)) |
---|
80 | #define I2CCR_BCST (1<<(7-7)) |
---|
81 | #define I2CSR 0x300c |
---|
82 | #define I2CSR_MCF (1<<(7-0)) |
---|
83 | #define I2CSR_MAAS (1<<(7-1)) |
---|
84 | #define I2CSR_MBB (1<<(7-2)) |
---|
85 | #define I2CSR_MAL (1<<(7-3)) |
---|
86 | #define I2CSR_BCSTM (1<<(7-4)) |
---|
87 | #define I2CSR_SRW (1<<(7-5)) |
---|
88 | #define I2CSR_MIF (1<<(7-6)) |
---|
89 | #define I2CSR_RXAK (1<<(7-7)) |
---|
90 | #define I2CDR 0x3010 |
---|
91 | #define I2CDFSRR 0x3014 |
---|
92 | |
---|
93 | SPR_RO(TBRL) |
---|
94 | |
---|
95 | /********* Global Variables **********/ |
---|
96 | |
---|
97 | /* |
---|
98 | * Semaphore for synchronizing accessing task |
---|
99 | * with the (slow) hardware operation. |
---|
100 | * Task takes semaphore and blocks, ISR releases. |
---|
101 | */ |
---|
102 | static rtems_id syncsem = 0; |
---|
103 | |
---|
104 | static inline int ok_to_block(void) |
---|
105 | { |
---|
106 | return syncsem && _System_state_Is_up( _System_state_Get() ); |
---|
107 | } |
---|
108 | |
---|
109 | /* |
---|
110 | * Wild guess for 0.2 s; this timeout is effective |
---|
111 | * in polling mode; during early init we don't know |
---|
112 | * the system clock rate yet - it's one of the things |
---|
113 | * we have to read from VPD -- via i2c. |
---|
114 | */ |
---|
115 | |
---|
116 | static uint32_t poll_timeout = 333333333/8/5; |
---|
117 | |
---|
118 | /********* Primitives ****************/ |
---|
119 | |
---|
120 | static inline uint8_t |
---|
121 | i2c_rd(unsigned reg) |
---|
122 | { |
---|
123 | return in_8( (volatile uint8_t *)(BSP_8540_CCSR_BASE + reg) ); |
---|
124 | } |
---|
125 | |
---|
126 | static inline void |
---|
127 | i2c_wr(unsigned reg, uint8_t val) |
---|
128 | { |
---|
129 | out_8( (volatile uint8_t *)(BSP_8540_CCSR_BASE + reg), val ); |
---|
130 | } |
---|
131 | |
---|
132 | static inline void |
---|
133 | i2c_set(unsigned reg, uint8_t val) |
---|
134 | { |
---|
135 | i2c_wr( reg, i2c_rd( reg ) | val ); |
---|
136 | } |
---|
137 | |
---|
138 | static inline void |
---|
139 | i2c_clr(unsigned reg, uint8_t val) |
---|
140 | { |
---|
141 | i2c_wr( reg, i2c_rd( reg ) & ~val ); |
---|
142 | } |
---|
143 | |
---|
144 | /********* Helper Routines ***********/ |
---|
145 | |
---|
146 | /* Synchronize (wait) for a condition on the |
---|
147 | * i2c bus. Wait for START or STOP to be complete |
---|
148 | * or wait for a byte-transfer. |
---|
149 | * The latter is much slower (9 bit times vs. 1/2 |
---|
150 | * in the former cases). |
---|
151 | * |
---|
152 | * If the system is up (and we may block) then |
---|
153 | * this routine attempts to block the current |
---|
154 | * task rather than busy-waiting. |
---|
155 | * |
---|
156 | * NOTE: waiting for START/STOP always requires |
---|
157 | * polling. |
---|
158 | */ |
---|
159 | |
---|
160 | /* wait until i2c status reg AND mask == cond */ |
---|
161 | static rtems_status_code |
---|
162 | i2c_wait( uint8_t msk, uint8_t cond ) |
---|
163 | { |
---|
164 | uint32_t then; |
---|
165 | rtems_status_code sc; |
---|
166 | static int warn = 0; |
---|
167 | |
---|
168 | if ( I2CSR_MIF == msk && ok_to_block() ) { |
---|
169 | /* block on semaphore only if system is up and sema initialized */ |
---|
170 | sc = rtems_semaphore_obtain( syncsem, RTEMS_WAIT, 100 ); |
---|
171 | if ( RTEMS_SUCCESSFUL != sc ) |
---|
172 | return sc; |
---|
173 | } else { |
---|
174 | /* system not up (no SEMA yet ) or waiting on something other |
---|
175 | * than MIF |
---|
176 | */ |
---|
177 | if ( I2CSR_MIF == msk && _System_state_Is_up( _System_state_Get() ) ) { |
---|
178 | if ( warn < 8 || ! (warn & 0x1f) ) |
---|
179 | printk("WARNING: i2c bus driver running in polled mode -- should initialize properly!\n"); |
---|
180 | warn++; |
---|
181 | } |
---|
182 | |
---|
183 | then = _read_TBRL(); |
---|
184 | do { |
---|
185 | /* poll for .2 seconds */ |
---|
186 | if ( (_read_TBRL() - then) > poll_timeout ) |
---|
187 | return RTEMS_TIMEOUT; |
---|
188 | } while ( (msk & i2c_rd( I2CSR )) != cond ); |
---|
189 | } |
---|
190 | |
---|
191 | return RTEMS_SUCCESSFUL; |
---|
192 | } |
---|
193 | |
---|
194 | /* |
---|
195 | * multi-byte transfer |
---|
196 | * - set transfer direction (master read or master write) |
---|
197 | * - transfer byte |
---|
198 | * - wait/synchronize |
---|
199 | * - check for ACK |
---|
200 | * |
---|
201 | * RETURNS: number of bytes transferred or negative error code. |
---|
202 | */ |
---|
203 | |
---|
204 | STATIC int |
---|
205 | i2c_xfer(int rw, uint8_t *buf, int len) |
---|
206 | { |
---|
207 | int i; |
---|
208 | rtems_status_code sc; |
---|
209 | |
---|
210 | if ( rw ) { |
---|
211 | i2c_clr( I2CCR, I2CCR_MTX ); |
---|
212 | } else { |
---|
213 | i2c_set( I2CCR, I2CCR_MTX ); |
---|
214 | } |
---|
215 | |
---|
216 | for ( i = 0; i< len; i++ ) { |
---|
217 | i2c_clr( I2CSR, I2CSR_MIF ); |
---|
218 | /* Enable interrupts if necessary */ |
---|
219 | if ( ok_to_block() ) |
---|
220 | i2c_set( I2CCR, I2CCR_MIEN ); |
---|
221 | if ( rw ) { |
---|
222 | buf[i] = i2c_rd( I2CDR ); |
---|
223 | } else { |
---|
224 | i2c_wr( I2CDR, buf[i] ); |
---|
225 | } |
---|
226 | if ( RTEMS_SUCCESSFUL != (sc = i2c_wait( I2CSR_MIF, I2CSR_MIF )) ) |
---|
227 | return -sc; |
---|
228 | if ( (I2CSR_RXAK & i2c_rd( I2CSR )) ) { |
---|
229 | /* NO ACK */ |
---|
230 | return -RTEMS_IO_ERROR; |
---|
231 | } |
---|
232 | } |
---|
233 | |
---|
234 | return i; |
---|
235 | } |
---|
236 | |
---|
237 | /* |
---|
238 | * This bus controller gives us lagging data, i.e., |
---|
239 | * when we read a byte from the data reg then that |
---|
240 | * issues a read cycle on the bus and gives us the |
---|
241 | * byte from the *previous* read cycle :-( |
---|
242 | * |
---|
243 | * This makes it impossible to properly terminate |
---|
244 | * a read transaction w/o knowing ahead of time |
---|
245 | * how many bytes are going to be read (API decouples |
---|
246 | * 'START'/'STOP' from 'READ') since we would have to |
---|
247 | * set TXAK when reading the next-to-last byte |
---|
248 | * (i.e., when the last byte is read on the i2c bus). |
---|
249 | * |
---|
250 | * Hence, (if we are reading) we must do a dummy |
---|
251 | * read-cycle here -- hopefully |
---|
252 | * that has no side-effects! (i.e., EEPROM drivers should |
---|
253 | * reposition file pointers after issuing STOP) |
---|
254 | * |
---|
255 | */ |
---|
256 | |
---|
257 | static void |
---|
258 | rd1byte_noack(void) |
---|
259 | { |
---|
260 | uint8_t dum; |
---|
261 | uint8_t ccr; |
---|
262 | |
---|
263 | /* If we are in reading state then read one more |
---|
264 | * byte w/o acknowledge |
---|
265 | */ |
---|
266 | |
---|
267 | ccr = i2c_rd (I2CCR ); |
---|
268 | |
---|
269 | if ( ! ( I2CCR_MTX & ccr ) ) { |
---|
270 | i2c_wr( I2CCR, ccr | I2CCR_TXAK ); |
---|
271 | i2c_xfer(1, &dum, 1); |
---|
272 | /* restore original TXAK bit setting */ |
---|
273 | i2c_clr( I2CCR, (I2CCR_TXAK & ccr) ); |
---|
274 | } |
---|
275 | } |
---|
276 | |
---|
277 | |
---|
278 | /********* ISR ***********************/ |
---|
279 | |
---|
280 | static void i2c_isr(rtems_irq_hdl_param arg) |
---|
281 | { |
---|
282 | /* disable irq */ |
---|
283 | i2c_clr( I2CCR, I2CCR_MIEN ); |
---|
284 | /* release task */ |
---|
285 | rtems_semaphore_release( syncsem ); |
---|
286 | } |
---|
287 | |
---|
288 | /********* IIC Bus Driver Ops ********/ |
---|
289 | |
---|
290 | STATIC rtems_status_code |
---|
291 | i2c_init(rtems_libi2c_bus_t *bh) |
---|
292 | { |
---|
293 | rtems_status_code sc; |
---|
294 | |
---|
295 | /* compute more accurate timeout */ |
---|
296 | if ( BSP_bus_frequency && BSP_time_base_divisor ) |
---|
297 | poll_timeout = BSP_bus_frequency/BSP_time_base_divisor*1000/5; |
---|
298 | |
---|
299 | i2c_clr( I2CCR, I2CCR_MEN ); |
---|
300 | i2c_set( I2CCR, I2CCR_MEN ); |
---|
301 | |
---|
302 | i2c_wr( I2CADR, 0 ); |
---|
303 | |
---|
304 | /* leave motload settings for divisor and filter registers */ |
---|
305 | |
---|
306 | if ( SYSTEM_STATE_BEFORE_MULTITASKING <= _System_state_Get() && !syncsem ) { |
---|
307 | sc = rtems_semaphore_create( |
---|
308 | rtems_build_name('i','2','c','b'), |
---|
309 | 0, |
---|
310 | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_LOCAL, |
---|
311 | 0, |
---|
312 | &syncsem); |
---|
313 | if ( RTEMS_SUCCESSFUL == sc ) { |
---|
314 | rtems_irq_connect_data xxx; |
---|
315 | xxx.name = BSP_I2C_IRQ; |
---|
316 | xxx.on = 0; |
---|
317 | xxx.off = 0; |
---|
318 | xxx.isOn = 0; |
---|
319 | xxx.hdl = i2c_isr; |
---|
320 | xxx.handle = 0; |
---|
321 | if ( ! BSP_install_rtems_irq_handler( &xxx ) ) { |
---|
322 | printk("Unable to install i2c ISR -- falling back to polling mode\n"); |
---|
323 | rtems_semaphore_delete( syncsem ); |
---|
324 | /* fall back to polling mode */ |
---|
325 | syncsem = 0; |
---|
326 | } |
---|
327 | } else { |
---|
328 | syncsem = 0; |
---|
329 | } |
---|
330 | } |
---|
331 | |
---|
332 | return RTEMS_SUCCESSFUL; |
---|
333 | } |
---|
334 | |
---|
335 | STATIC rtems_status_code |
---|
336 | i2c_start(rtems_libi2c_bus_t *bh) |
---|
337 | { |
---|
338 | uint8_t v; |
---|
339 | rtems_status_code sc = RTEMS_SUCCESSFUL; |
---|
340 | |
---|
341 | v = i2c_rd( I2CCR ); |
---|
342 | if ( I2CCR_MSTA & v ) { |
---|
343 | /* RESTART */ |
---|
344 | rd1byte_noack(); |
---|
345 | v |= I2CCR_RSTA; |
---|
346 | } else { |
---|
347 | v |= I2CCR_MSTA; |
---|
348 | } |
---|
349 | i2c_wr( I2CCR, v ); |
---|
350 | |
---|
351 | /* On MBB we can only poll-wait (no IRQ is generated) |
---|
352 | * and this is also much faster than reading a byte |
---|
353 | * (1/2-bit time) so the overhead of an IRQ may not |
---|
354 | * be justified. |
---|
355 | * OTOH, we can put this off into the 'send_addr' routine |
---|
356 | * |
---|
357 | |
---|
358 | sc = i2c_wait( I2CSR_MBB, I2CSR_MBB ); |
---|
359 | */ |
---|
360 | |
---|
361 | return sc; |
---|
362 | } |
---|
363 | |
---|
364 | STATIC rtems_status_code |
---|
365 | i2c_stop(rtems_libi2c_bus_t *bh) |
---|
366 | { |
---|
367 | rd1byte_noack(); |
---|
368 | |
---|
369 | /* STOP */ |
---|
370 | i2c_clr( I2CCR, I2CCR_TXAK | I2CCR_MSTA ); |
---|
371 | |
---|
372 | /* FIXME: should we really spend 1/2 bit-time polling |
---|
373 | * or should we just go ahead and hope noone |
---|
374 | * else will get a chance to do something to |
---|
375 | * the bus until the STOP completes? |
---|
376 | */ |
---|
377 | return i2c_wait( I2CSR_MBB, 0 ); |
---|
378 | } |
---|
379 | |
---|
380 | STATIC rtems_status_code |
---|
381 | i2c_send_addr(rtems_libi2c_bus_t *bh, uint32_t addr, int rw) |
---|
382 | { |
---|
383 | uint8_t buf[2]; |
---|
384 | int l = 0; |
---|
385 | uint8_t read_mask = rw ? 1 : 0; |
---|
386 | rtems_status_code sc; |
---|
387 | |
---|
388 | /* Make sure we are started; (i2c_start() didn't bother to wait |
---|
389 | * so we do it here - some time already has expired. |
---|
390 | */ |
---|
391 | sc = i2c_wait( I2CSR_MBB, I2CSR_MBB ); |
---|
392 | |
---|
393 | if ( RTEMS_SUCCESSFUL != sc ) |
---|
394 | return sc; |
---|
395 | |
---|
396 | if ( addr > 0x7f ) { |
---|
397 | /* 10-bit request; 1st address byte is 0b11110<b9><b8><r/w> */ |
---|
398 | buf[l] = 0xf0 | ((addr >> 7) & 0x06) | read_mask; |
---|
399 | read_mask = 0; |
---|
400 | l++; |
---|
401 | buf[l] = addr & 0xff; |
---|
402 | } else { |
---|
403 | buf[l] = (addr << 1) | read_mask; |
---|
404 | l++; |
---|
405 | } |
---|
406 | |
---|
407 | /* |
---|
408 | * After sending a an address for reading we must |
---|
409 | * read a dummy byte (this actually clocks the first real |
---|
410 | * byte on the i2c bus and makes it available in the |
---|
411 | * data register so that the first 'read_bytes' operation |
---|
412 | * obtains the byte we clock in here [and starts clocking |
---|
413 | * the second byte]) to overcome the pipeline |
---|
414 | * delay in the hardware (I don't like this design) :-(. |
---|
415 | */ |
---|
416 | sc = i2c_xfer( 0, buf, l ); |
---|
417 | if ( rw && l == sc ) { |
---|
418 | sc = i2c_xfer( 1, buf, 1 ); |
---|
419 | } |
---|
420 | return sc >=0 ? RTEMS_SUCCESSFUL : -sc; |
---|
421 | } |
---|
422 | |
---|
423 | STATIC int |
---|
424 | i2c_read_bytes(rtems_libi2c_bus_t *bh, unsigned char *buf, int len) |
---|
425 | { |
---|
426 | return i2c_xfer( 1, buf, len ); |
---|
427 | } |
---|
428 | |
---|
429 | STATIC int |
---|
430 | i2c_write_bytes(rtems_libi2c_bus_t *bh, unsigned char *buf, int len) |
---|
431 | { |
---|
432 | return i2c_xfer( 0, buf, len ); |
---|
433 | } |
---|
434 | |
---|
435 | /********* Driver Glue Vars **********/ |
---|
436 | |
---|
437 | static rtems_libi2c_bus_ops_t myops = { |
---|
438 | init: i2c_init, |
---|
439 | send_start: i2c_start, |
---|
440 | send_stop: i2c_stop, |
---|
441 | send_addr: i2c_send_addr, |
---|
442 | read_bytes: i2c_read_bytes, |
---|
443 | write_bytes: i2c_write_bytes, |
---|
444 | }; |
---|
445 | |
---|
446 | static rtems_libi2c_bus_t my_bus_tbl = { |
---|
447 | ops: &myops, |
---|
448 | size: sizeof(my_bus_tbl), |
---|
449 | }; |
---|
450 | |
---|
451 | /********* Global Driver Handle ******/ |
---|
452 | |
---|
453 | rtems_libi2c_bus_t *mpc8540_i2c_bus_descriptor = &my_bus_tbl; |
---|