]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/i2c/m68k/mcf52xx/v2_0/src/i2c_mcf52xx.c
Initial revision
[karo-tx-redboot.git] / packages / devs / i2c / m68k / mcf52xx / v2_0 / src / i2c_mcf52xx.c
1 //==========================================================================
2 //
3 //      devs/i2c/m68k/mcf52xx/current/src/i2c_mcf52xx.c
4 //
5 //      I2C driver for Motorola coldfire processors
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 2005, 2006 eCosCentric Ltd.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 // -------------------------------------------
36 //####ECOSGPLCOPYRIGHTEND####
37 //==========================================================================
38 //#####DESCRIPTIONBEGIN####
39 //
40 // Author(s):     Uwe Kindler, Bart Veer
41 // Contributors:  
42 // Date:          2005-10-23
43 // Description:   I2C driver for motorola coldfire processor
44 //####DESCRIPTIONEND####
45 //==========================================================================
46
47 #include <pkgconf/system.h>
48 #include <pkgconf/devs_i2c_mcf52xx.h>
49
50 #include <cyg/infra/cyg_type.h>
51 #include <cyg/infra/cyg_ass.h>
52 #include <cyg/infra/diag.h>
53 #include <cyg/io/i2c.h>
54 #include <cyg/io/i2c_mcf52xx.h>
55 #include <cyg/hal/hal_arch.h>
56 #include <cyg/hal/hal_io.h>
57 #include <cyg/hal/hal_intr.h>
58 #include <cyg/hal/drv_api.h>
59
60 // Optimize for the case of a single bus device, while still allowing
61 // multiple devices.
62 #ifndef CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES
63 # define    I2C_BASE(_extra_)       (cyg_uint8*)HAL_MCF52xx_I2C_SINGLETON_BASE
64 # define    I2C_ISRVEC(_extra_)     HAL_MCF52xx_I2C_SINGLETON_ISRVEC
65 # define    I2C_ISRPRI(_extra_)     HAL_MCF52xx_I2C_SINGLETON_ISRPRI
66 # define    I2C_FDR(_extra_)        HAL_MCF52xx_I2C_SINGLETON_FDR
67 #else
68 # define    I2C_BASE(_extra_)       ((_extra_)->i2c_base)
69 # define    I2C_ISRVEC(_extra_)     ((_extra_)->i2c_isrvec)
70 # define    I2C_ISRPRI(_extra_)     ((_extra_)->i2c_isrpri)
71 # define    I2C_FDR(_extra_)        ((_extra_)->i2c_fdr)
72 #endif
73
74 // If building for a singleton but the macros are no defined, assume
75 // the I2C support is conditional on a disabled platform HAL
76 // configuration option. This handles the common case of an I2C bus
77 // accessed only via an expansion connector.
78 #if defined(CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES) || defined(HAL_MCF52xx_I2C_SINGLETON_BASE)
79
80 // ----------------------------------------------------------------------------
81 // Interrupt handling and polling
82 //
83 // The MCF52xx I2C bus device does not have a fifo or any kind of DMA
84 // capability, so can generate interrupts at a very high rate: ~10K
85 // interrupts per second if the bus is running at the standard 100KHz,
86 // or 50K for a high-speed 400KHz bus. To keep the cpu load down to
87 // something vaguely reasonable as much work as possible has to be
88 // done in the ISR, with the DSR used only for completion.
89 static cyg_uint32
90 mcf52xx_i2c_isr(cyg_vector_t vec, cyg_addrword_t data)
91 {
92     cyg_mcf52xx_i2c_extra*  extra   = (cyg_mcf52xx_i2c_extra*)data;
93     cyg_uint8               sr, dr;
94     cyg_uint8*              base    = I2C_BASE(extra);
95     cyg_uint32              result  = CYG_ISR_HANDLED;
96
97     // Read the current status, then clear the interrupt and
98     // arbitration-lost flags. No later code will look at the
99     // SR register again.
100     HAL_READ_UINT8( base + HAL_MCF52xx_I2C_SR_OFF, sr);
101     HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, 0x00);
102
103     // What to do next depends on the current transfer mode.
104     if (CYG_MCF52xx_I2C_XFER_MODE_TX == extra->i2c_mode) {
105         // We are in a transmit, or sending the address byte just
106         // before a transmit.
107         if (sr & HAL_MCF52xx_I2C_SR_IAL) {
108             // Lost the bus, abort the transfer. count has already been
109             // decremented. Assume the byte did not actually arrive.
110             extra->i2c_count    += 1;
111             result              = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
112         } else if (sr & HAL_MCF52xx_I2C_SR_RXAK) {
113             // This byte has been sent but the device cannot accept
114             // any more. The nack must be remembered. Otherwise if
115             // we got a nack for the last byte in a tx then the
116             // calling code will think the entire tx succeeded,
117             // and there will be problems if the next call is
118             // another tx without a repeated start.
119             extra->i2c_got_nack = 1;
120             result              = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
121         } else if (0 == extra->i2c_count) {
122             // No more bytes to send.
123             result          = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
124         } else {
125             HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, *(extra->i2c_data.i2c_tx_data));
126             extra->i2c_data.i2c_tx_data += 1;
127             extra->i2c_count            -= 1;
128         }
129     } else if (CYG_MCF52xx_I2C_XFER_MODE_RX == extra->i2c_mode) {
130         if (sr & HAL_MCF52xx_I2C_SR_IAL) {
131             // Lost the bus? Maybe a spurious stop
132             result = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
133         } else {
134             if (extra->i2c_send_nack && (2 == extra->i2c_count)) {
135                 // Received one, one more to go, and that one should be nacked.
136                 HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF,
137                                 HAL_MCF52xx_I2C_CR_IEN  |
138                                 HAL_MCF52xx_I2C_CR_IIEN |
139                                 HAL_MCF52xx_I2C_CR_MSTA |
140                                 HAL_MCF52xx_I2C_CR_TXAK);
141             } else if (1 == extra->i2c_count) {
142                 // Received the last byte. The docs say to send a stop,
143                 // but there may be another transaction_rx() call. We
144                 // cannot just read DR again, that would trigger another
145                 // read. So instead switch to transmit mode for now,
146                 // which should cause the h/w to wait until a byte is
147                 // written to DR.
148                 HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF,
149                                 HAL_MCF52xx_I2C_CR_IEN  |
150                                 HAL_MCF52xx_I2C_CR_IIEN |
151                                 HAL_MCF52xx_I2C_CR_MSTA |
152                                 HAL_MCF52xx_I2C_CR_MTX);
153                 result = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
154             }
155
156             HAL_READ_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, dr);
157             *(extra->i2c_data.i2c_rx_data)  = dr;
158             extra->i2c_data.i2c_rx_data     += 1;
159             extra->i2c_count                -= 1;
160         }
161     } else if (CYG_MCF52xx_I2C_XFER_MODE_STARTRX == extra->i2c_mode) {
162         // Start followed by RX. The address byte has been sent, we
163         // need to switch to receiving.
164         if (sr & HAL_MCF52xx_I2C_SR_IAL) {
165             // Looks like no device acknowledged the address.
166             result = CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
167         } else {
168             extra->i2c_mode = CYG_MCF52xx_I2C_XFER_MODE_RX;
169             if (extra->i2c_send_nack && (1 == extra->i2c_count)) {
170                 HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF,
171                                 HAL_MCF52xx_I2C_CR_IEN  |
172                                 HAL_MCF52xx_I2C_CR_IIEN |
173                                 HAL_MCF52xx_I2C_CR_MSTA |
174                                 HAL_MCF52xx_I2C_CR_TXAK);
175             } else {
176                 HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF,
177                                 HAL_MCF52xx_I2C_CR_IEN  |
178                                 HAL_MCF52xx_I2C_CR_IIEN |
179                                 HAL_MCF52xx_I2C_CR_MSTA);
180             }
181             // This dummy read causes the next rx to start
182             HAL_READ_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, dr);
183         }
184     } else {
185         // Invalid state? Some kind of spurious interrupt? Just ignore
186         // it.
187         CYG_FAIL("I2C spurious interrupt");
188     }
189
190     // NOTE: this will acknowledge the interrupt even in polled mode.
191     // Probably harmless. Using I2C_ISRVEC rather than the vec arg
192     // means a constant number for the singleton case, which may
193     // allow the HAL to optimize the acknowledge away completely.
194     HAL_INTERRUPT_ACKNOWLEDGE(I2C_ISRVEC(extra));
195     return result;
196 }
197
198 static void
199 mcf52xx_i2c_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
200 {
201     cyg_mcf52xx_i2c_extra*  extra   = (cyg_mcf52xx_i2c_extra*)data;
202     extra->i2c_completed    = 1;
203     cyg_drv_cond_signal(&(extra->i2c_wait));
204 }
205
206 // A transfer has been started. Wait for completion, allowing for both
207 // polled and interrupt-driven mode.
208 static inline void
209 mcf52xx_i2c_doit(cyg_mcf52xx_i2c_extra* extra)
210 {
211     cyg_uint8*  base    = I2C_BASE(extra);
212     int         ints_state;
213     int         sr;
214     
215     HAL_QUERY_INTERRUPTS(ints_state);
216     if (((ints_state >> 8) & 0x07) > CYGNUM_HAL_INTERRUPT_DEFAULT_IPL_LEVEL) {
217         // Interrupts are currently disabled. We'll have to poll.
218         for ( ; ; ) {
219             HAL_READ_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, sr);
220             if (sr & HAL_MCF52xx_I2C_SR_IIF) {
221                 if (CYG_ISR_CALL_DSR & mcf52xx_i2c_isr(I2C_ISRVEC(extra), (cyg_addrword_t)extra)) {
222                     break;
223                 }
224             }
225         }
226     } else {
227         cyg_drv_mutex_lock(&(extra->i2c_lock));
228         cyg_drv_dsr_lock();
229         while (! extra->i2c_completed) {
230             cyg_drv_cond_wait(&(extra->i2c_wait));
231         }
232         cyg_drv_dsr_unlock();
233         cyg_drv_mutex_unlock(&(extra->i2c_lock));
234     }
235 }
236
237 static cyg_bool
238 mcf52xx_i2c_send_start(cyg_mcf52xx_i2c_extra* extra, int address)
239 {
240     cyg_uint8*  base    = I2C_BASE(extra);
241     cyg_uint8   sr;
242     
243     // This may be a repeated start or the beginning of a transaction.
244     // If the former then we still own the bus.
245     if (!extra->i2c_owner) {
246         // The bus is currently in slave mode. See if another master
247         // currently owns the bus and if so fail immediately. It is up
248         // to higher level code to decide when to retry. Alternatively
249         // if the bus has somehow got stuck in busy mode it is again
250         // up to higher level code to sort things out.
251         HAL_READ_UINT8(I2C_BASE(extra) + HAL_MCF52xx_I2C_SR_OFF, sr);
252         if (sr & HAL_MCF52xx_I2C_SR_IBB) {
253             return 0;
254         }
255
256         // Now we can put the bus into master mode
257         HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF,
258                         HAL_MCF52xx_I2C_CR_IEN   |
259                         HAL_MCF52xx_I2C_CR_IIEN  |
260                         HAL_MCF52xx_I2C_CR_MSTA  |  // This implicitly generates the start
261                         HAL_MCF52xx_I2C_CR_MTX);    // The address byte needs to be transmitted.
262         extra->i2c_owner    = 1;
263     } else {
264         HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF,
265                         HAL_MCF52xx_I2C_CR_IEN   |
266                         HAL_MCF52xx_I2C_CR_IIEN  |
267                         HAL_MCF52xx_I2C_CR_MSTA  |  // Already set so no start generated by this
268                         HAL_MCF52xx_I2C_CR_MTX   |
269                         HAL_MCF52xx_I2C_CR_RSTA);    // Repeated start
270     }
271
272     // Any previous nack is no longer relevant. If the device cannot accept
273     // more data it will nack the address.
274     extra->i2c_got_nack = 0;
275     // Now send the address. The rest of the transfer is handled by the
276     // interrupt/polling code.
277     HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, address);
278     return 1;
279 }
280
281 static inline void
282 mcf52xx_i2c_stopit(cyg_mcf52xx_i2c_extra* extra)
283 {
284     // If we still own the bus this releases it (by clearing MSTA) and
285     // generating a stop. If we have lost arbitration then this write
286     // has no effect (other than disabling interrupts). Either way the
287     // bus should end up in a consistent state.
288     HAL_WRITE_UINT8(I2C_BASE(extra) + HAL_MCF52xx_I2C_CR_OFF, HAL_MCF52xx_I2C_CR_IEN);
289     extra->i2c_lost_arb = 0;
290     extra->i2c_owner    = 0;
291     extra->i2c_mode     = CYG_MCF52xx_I2C_XFER_MODE_INVALID;
292 }
293
294 // ----------------------------------------------------------------------------
295 // The functions needed for all I2C devices.
296
297 void
298 cyg_mcf52xx_i2c_init(struct cyg_i2c_bus* bus)
299 {
300     cyg_mcf52xx_i2c_extra*  extra   = (cyg_mcf52xx_i2c_extra*)bus->i2c_extra;
301     cyg_uint8               reg;
302     cyg_uint8*              base    = I2C_BASE(extra);
303
304     cyg_drv_mutex_init(&extra->i2c_lock);
305     cyg_drv_cond_init(&extra->i2c_wait, &extra->i2c_lock);
306     cyg_drv_interrupt_create(I2C_ISRVEC(extra),
307                              I2C_ISRPRI(extra),
308                              (cyg_addrword_t) extra,
309                              &mcf52xx_i2c_isr,
310                              &mcf52xx_i2c_dsr,
311                              &(extra->i2c_interrupt_handle),
312                              &(extra->i2c_interrupt_data));
313     cyg_drv_interrupt_attach(extra->i2c_interrupt_handle);
314
315     // Before unmasking the interrupt sort out the hardware.
316     //
317     // The bus frequency is set by the platform HAL or user, since
318     // it depends on what mixture of devices are present on the bus.
319     HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_FDR_OFF, I2C_FDR(extra));
320     // The device will operate in slave mode when idle. If there is
321     // another bus master then the coldfire might accidentally accept
322     // requests intended for another device. Address 0 is installed
323     // as the slave address. This is the General Call address, used
324     // for broadcasting. It might be better to use another address
325     // like an Hs-mode one, but conflicts are still possible.
326     HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_ADR_OFF, 0x0);
327     // Enable the I2C device but do not start any transfers and
328     // leave interrupts disabled.
329     HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, HAL_MCF52xx_I2C_CR_IEN);
330     
331     // As per the documentation, if IBB is set then issue a stop. It
332     // is not really clear this is the right thing to do in
333     // multimaster setups, if another master happens to start a
334     // transfer at this exact time. Presumably it solves more problems
335     // than it might cause.
336     HAL_READ_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, reg);
337     if (reg & HAL_MCF52xx_I2C_SR_IBB) {
338         HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, 0x0000);
339         HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, 0x00A0);
340         HAL_READ_UINT8( base + HAL_MCF52xx_I2C_DR_OFF, reg);
341         HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, 0x0000);
342         HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, 0x0000);
343
344         // Don't forget to reenable the device.
345         HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF, HAL_MCF52xx_I2C_CR_IEN);
346     }
347     
348     // Clear any pending conditions including interrupts.
349     HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_SR_OFF, 0);
350
351     // Interrupts can now be safely unmasked
352     HAL_INTERRUPT_UNMASK(I2C_ISRVEC(extra));
353 }
354
355 cyg_uint32
356 cyg_mcf52xx_i2c_tx(const cyg_i2c_device* dev, cyg_bool send_start, const cyg_uint8* tx_data, cyg_uint32 count, cyg_bool send_stop)
357 {
358     cyg_mcf52xx_i2c_extra*  extra   = (cyg_mcf52xx_i2c_extra*)dev->i2c_bus->i2c_extra;
359
360     extra->i2c_count        = count;
361     if (! extra->i2c_lost_arb) {
362         extra->i2c_completed    = 0;
363         extra->i2c_mode         = CYG_MCF52xx_I2C_XFER_MODE_TX;
364
365         if (send_start) {
366             extra->i2c_data.i2c_tx_data = tx_data;
367             if (! mcf52xx_i2c_send_start(extra, (dev->i2c_address << 1) | 0x00)) {
368                 diag_printf("send_start failed\n");
369                 return 0;
370             }
371             mcf52xx_i2c_doit(extra);
372         } else if ( !extra->i2c_got_nack) {
373             // We are in the middle of a transaction and not
374             // generating a repeated start, so the device must already
375             // be set up for writes.
376             extra->i2c_data.i2c_tx_data = &(tx_data[1]);
377             extra->i2c_count            = count - 1;
378             HAL_WRITE_UINT8(I2C_BASE(extra) + HAL_MCF52xx_I2C_DR_OFF, *tx_data);
379             mcf52xx_i2c_doit(extra);
380         }
381     }
382     if (send_stop) {
383         mcf52xx_i2c_stopit(extra);
384     }
385
386     // tx() should return the number of bytes actually transmitted.
387     // ISR() increments extra->count after a failure, which leads to
388     // an edge condition when send_start and there is no acknowledgment
389     // of the address byte.
390     if (extra->i2c_count > count) {
391         return 0;
392     }
393     return count - extra->i2c_count;
394 }
395
396 cyg_uint32
397 cyg_mcf52xx_i2c_rx(const cyg_i2c_device* dev, cyg_bool send_start, cyg_uint8* rx_data, cyg_uint32 count, cyg_bool send_nack, cyg_bool send_stop)
398 {
399     cyg_mcf52xx_i2c_extra*  extra   = (cyg_mcf52xx_i2c_extra*)dev->i2c_bus->i2c_extra;
400     cyg_uint8*              base    = I2C_BASE(extra);
401     cyg_uint8               discard;
402     
403     extra->i2c_count        = count;
404     extra->i2c_send_nack    = send_nack;
405     
406     if (! extra->i2c_lost_arb) {
407         extra->i2c_completed        = 0;
408         extra->i2c_data.i2c_rx_data = rx_data;
409         if (send_start) {
410             extra->i2c_mode     = CYG_MCF52xx_I2C_XFER_MODE_STARTRX;
411             if (! mcf52xx_i2c_send_start(extra, (dev->i2c_address << 1) | 0x01) ) {
412                 return 0;
413             }
414         } else {
415             // In the middle of a transaction. The previous transfer
416             // will have left the device in tx mode.
417             extra->i2c_mode     = CYG_MCF52xx_I2C_XFER_MODE_RX;
418             if (send_nack && (1 == count)) {
419                 HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF,
420                                 HAL_MCF52xx_I2C_CR_IEN  |
421                                 HAL_MCF52xx_I2C_CR_IIEN |
422                                 HAL_MCF52xx_I2C_CR_MSTA |
423                                 HAL_MCF52xx_I2C_CR_TXAK);
424             } else {
425                 HAL_WRITE_UINT8(base + HAL_MCF52xx_I2C_CR_OFF,
426                                 HAL_MCF52xx_I2C_CR_IEN  |
427                                 HAL_MCF52xx_I2C_CR_IIEN |
428                                 HAL_MCF52xx_I2C_CR_MSTA);
429             }
430             // So reading the data register here should get the device
431             // reading the next byte.
432             HAL_READ_UINT8(base + HAL_MCF52xx_I2C_DR_OFF, discard);
433         }
434         mcf52xx_i2c_doit(extra);
435     }
436     if (send_stop) {
437         mcf52xx_i2c_stopit(extra);
438     }
439     return count - extra->i2c_count;
440 }
441
442 void
443 cyg_mcf52xx_i2c_stop(const cyg_i2c_device* dev)
444 {
445     cyg_mcf52xx_i2c_extra*  extra   = (cyg_mcf52xx_i2c_extra*)dev->i2c_bus->i2c_extra;
446     mcf52xx_i2c_stopit(extra);
447 }
448
449 #endif  //  defined(CYGHWR_DEVS_I2C_MCF52xx_MULTIPLE_BUSES) || defined(HAL_MCF52xx_I2C_SINGLETON_BASE)
450 //---------------------------------------------------------------------------
451 // EOF i2c_mcf52xx.c