source: rtems/c/src/lib/libcpu/m68k/mcf5206/mbus/mcfmbus.c @ 1857b00

4.104.115
Last change on this file since 1857b00 was 1857b00, checked in by Ralf Corsepius <ralf.corsepius@…>, on 11/30/09 at 17:14:20

Whitespace removal.

  • Property mode set to 100644
File size: 18.5 KB
Line 
1/* MCF5206e MBUS module (I2C bus) driver
2 *
3 * Copyright (C) 2000 OKTET Ltd., St.-Petersburg, Russia
4 * Author: Victor V. Vengerov <vvv@oktet.ru>
5 *
6 * The license and distribution terms for this file may be
7 * found in the file LICENSE in this distribution or at
8 *
9 * http://www.rtems.com/license/LICENSE.
10 *
11 * @(#) $Id$
12 */
13
14#include "mcf5206/mcfmbus.h"
15#include "mcf5206/mcf5206e.h"
16#include "i2c.h"
17
18/* Events of I2C machine */
19typedef enum i2c_event {
20    EVENT_NONE,      /* Spurious event */
21    EVENT_TRANSFER,  /* Start new transfer */
22    EVENT_NEXTMSG,   /* Start processing of next message in transfer */
23    EVENT_ACK,       /* Sending finished with ACK */
24    EVENT_NACK,      /* Sending finished with NACK */
25    EVENT_TIMEOUT,   /* Timeout occured */
26    EVENT_DATA_RECV, /* Data received */
27    EVENT_ARB_LOST,  /* Arbitration lost */
28    EVENT_SLAVE      /* Addressed as a slave */
29} i2c_event;
30
31static mcfmbus *mbus;
32
33/*** Auxillary primitives ***/
34
35/* Change state of finite state machine */
36#define next_state(bus,new_state) \
37    do {                             \
38        (bus)->state = (new_state);  \
39    } while (0)
40
41/* Initiate start condition on the I2C bus */
42#define mcfmbus_start(bus) \
43    do {                                                    \
44        *MCF5206E_MBCR((bus)->base) |= MCF5206E_MBCR_MSTA;  \
45    } while (0)
46
47/* Initiate stop condition on the I2C bus */
48#define mcfmbus_stop(bus) \
49    do {                                                    \
50        *MCF5206E_MBCR((bus)->base) &= ~MCF5206E_MBCR_MSTA; \
51    } while (0)
52
53/* Initiate repeat start condition on the I2C bus */
54#define mcfmbus_rstart(bus) \
55    do {                                                    \
56        *MCF5206E_MBCR((bus)->base) |= MCF5206E_MBCR_RSTA;  \
57    } while (0)
58
59/* Send byte to the bus */
60#define mcfmbus_send(bus,byte) \
61    do {                                      \
62        *MCF5206E_MBDR((bus)->base) = (byte); \
63    } while (0)
64
65/* Set transmit mode */
66#define mcfmbus_tx_mode(bus) \
67    do {                                                     \
68        *MCF5206E_MBCR((bus)->base) |= MCF5206E_MBCR_MTX;    \
69    } while (0)
70
71/* Set receive mode */
72#define mcfmbus_rx_mode(bus) \
73    do {                                                     \
74        *MCF5206E_MBCR((bus)->base) &= ~MCF5206E_MBCR_MTX;   \
75        (void)*MCF5206E_MBDR((bus)->base);                   \
76    } while (0)
77
78
79/* Transmit acknowledge when byte received */
80#define mcfmbus_send_ack(bus) \
81    do {                                                     \
82        *MCF5206E_MBCR((bus)->base) &= ~MCF5206E_MBCR_TXAK;  \
83    } while (0)
84
85/* DO NOT transmit acknowledge when byte received */
86#define mcfmbus_send_nack(bus) \
87    do {                                                     \
88        *MCF5206E_MBCR((bus)->base) |= MCF5206E_MBCR_TXAK;   \
89    } while (0)
90
91#define mcfmbus_error(bus,err_status) \
92    do {                                                       \
93        do {                                                   \
94           (bus)->cmsg->status = (err_status);                 \
95           (bus)->cmsg++;                                      \
96        } while (((bus)->cmsg - (bus)->msg < (bus)->nmsg) &&   \
97                 ((bus)->cmsg->flags & I2C_MSG_ERRSKIP));      \
98        bus->cmsg--;                                           \
99    } while (0)
100
101/* mcfmbus_get_event --
102 *     Read MBUS module status register, determine interrupt reason and
103 *     return appropriate event.
104 *
105 * PARAMETERS:
106 *     bus - pointer to MBUS module descriptor structure
107 *
108 * RETURNS:
109 *     event code
110 */
111static i2c_event
112mcfmbus_get_event(mcfmbus *bus)
113{
114    i2c_event event;
115    uint8_t   status, control;
116    rtems_interrupt_level level;
117    rtems_interrupt_disable(level);
118    status = *MCF5206E_MBSR(bus->base);
119    control = *MCF5206E_MBCR(bus->base);
120    if (status & MCF5206E_MBSR_MIF) /* Interrupt occured */
121    {
122        if (status & MCF5206E_MBSR_MAAS)
123        {
124            event = EVENT_SLAVE;
125            *MCF5206E_MBCR(bus->base) = control; /* To clear Addressed As Slave
126                                                    condition */
127        }
128        else if (status & MCF5206E_MBSR_MAL) /* Arbitration lost */
129        {
130            *MCF5206E_MBSR(bus->base) = status & ~MCF5206E_MBSR_MAL;
131            event = EVENT_ARB_LOST;
132        }
133        else if (control & MCF5206E_MBCR_MTX) /* Trasmit mode */
134        {
135            if (status & MCF5206E_MBSR_RXAK)
136                event = EVENT_NACK;
137            else
138                event = EVENT_ACK;
139        }
140        else /* Received */
141        {
142            event = EVENT_DATA_RECV;
143        }
144
145        /* Clear interrupt condition */
146        *MCF5206E_MBSR(bus->base) &= ~MCF5206E_MBSR_MIF;
147    }
148    else
149    {
150        event = EVENT_NONE;
151    }
152    rtems_interrupt_enable(level);
153    return event;
154}
155
156static void
157mcfmbus_machine_error(mcfmbus *bus, i2c_event event)
158{
159    return;
160}
161
162/* mcfmbus_machine --
163 *     finite state machine for I2C bus protocol
164 *
165 * PARAMETERS:
166 *     bus - pointer to ColdFire MBUS descriptor structure
167 *     event - I2C event
168 *
169 * RETURNS:
170 *     none
171 */
172static void
173mcfmbus_machine(mcfmbus *bus, i2c_event event)
174{
175    uint8_t   b;
176    switch (bus->state)
177    {
178        case STATE_IDLE:
179            switch (event)
180            {
181                case EVENT_NEXTMSG:  /* Start new message processing */
182                    bus->cmsg++;
183                    /* FALLTHRU */
184
185                case EVENT_TRANSFER: /* Initiate new transfer */
186                    if (bus->cmsg - bus->msg >= bus->nmsg)
187                    {
188                        mcfmbus_stop(bus);
189                        next_state(bus, STATE_IDLE);
190                        bus->msg = bus->cmsg = NULL;
191                        bus->nmsg = bus->byte = 0;
192                        bus->done(bus->done_arg_ptr);
193                        break;
194                    }
195
196                    /* Initiate START or REPEATED START condition on the bus */
197                    if (event == EVENT_TRANSFER)
198                    {
199                        mcfmbus_start(bus);
200                    }
201                    else /* (event == EVENT_NEXTMSG) */
202                    {
203                        mcfmbus_rstart(bus);
204                    }
205
206                    bus->byte = 0;
207                    mcfmbus_tx_mode(bus);
208
209                    /* Initiate slave address sending */
210                    if (bus->cmsg->flags & I2C_MSG_ADDR_10)
211                    {
212                        i2c_address a = bus->cmsg->addr;
213                        b = 0xf0 | (((a >> 8) & 0x03) << 1);
214                        if (bus->cmsg->flags & I2C_MSG_WR)
215                        {
216                            mcfmbus_send(bus, b);
217                            next_state(bus, STATE_ADDR_1_W);
218                        }
219                        else
220                        {
221                            mcfmbus_send(bus, b | 1);
222                            next_state(bus, STATE_ADDR_1_R);
223                        }
224                    }
225                    else
226                    {
227                        b = (bus->cmsg->addr & ~0x01);
228
229                        if (bus->cmsg->flags & I2C_MSG_WR)
230                        {
231                            next_state(bus, STATE_SENDING);
232                        }
233                        else
234                        {
235                            next_state(bus, STATE_ADDR_7);
236                            b |= 1;
237                        }
238
239                        mcfmbus_send(bus, b);
240                    }
241                    break;
242
243                default:
244                    mcfmbus_machine_error(bus, event);
245                    break;
246            }
247            break;
248
249        case STATE_ADDR_7:
250            switch (event)
251            {
252                case EVENT_ACK:
253                    mcfmbus_rx_mode(bus);
254                    if (bus->cmsg->len <= 1)
255                        mcfmbus_send_nack(bus);
256                    else
257                        mcfmbus_send_ack(bus);
258                    next_state(bus, STATE_RECEIVING);
259                    break;
260
261                case EVENT_NACK:
262                    mcfmbus_error(bus, I2C_NO_DEVICE);
263                    next_state(bus, STATE_IDLE);
264                    mcfmbus_machine(bus, EVENT_NEXTMSG);
265                    break;
266
267                case EVENT_ARB_LOST:
268                    mcfmbus_error(bus, I2C_ARBITRATION_LOST);
269                    next_state(bus, STATE_IDLE);
270                    mcfmbus_machine(bus, EVENT_NEXTMSG);
271                    break;
272
273                default:
274                    mcfmbus_machine_error(bus, event);
275                    break;
276            }
277            break;
278
279        case STATE_ADDR_1_R:
280        case STATE_ADDR_1_W:
281            switch (event)
282            {
283                case EVENT_ACK:
284                {
285                    uint8_t   b = (bus->cmsg->addr & 0xff);
286                    mcfmbus_send(bus, b);
287                    if (bus->state == STATE_ADDR_1_W)
288                    {
289                        next_state(bus, STATE_SENDING);
290                    }
291                    else
292                    {
293                        i2c_address a;
294                        mcfmbus_rstart(bus);
295                        mcfmbus_tx_mode(bus);
296                        a = bus->cmsg->addr;
297                        b = 0xf0 | (((a >> 8) & 0x03) << 1) | 1;
298                        mcfmbus_send(bus, b);
299                        next_state(bus, STATE_ADDR_7);
300                    }
301                    break;
302                }
303
304                case EVENT_NACK:
305                    mcfmbus_error(bus, I2C_NO_DEVICE);
306                    next_state(bus, STATE_IDLE);
307                    mcfmbus_machine(bus, EVENT_NEXTMSG);
308                    break;
309
310                case EVENT_ARB_LOST:
311                    mcfmbus_error(bus, I2C_ARBITRATION_LOST);
312                    next_state(bus, STATE_IDLE);
313                    mcfmbus_machine(bus, EVENT_NEXTMSG);
314                    break;
315
316                default:
317                    mcfmbus_machine_error(bus, event);
318                    break;
319            }
320            break;
321
322        case STATE_SENDING:
323            switch (event)
324            {
325                case EVENT_ACK:
326                    if (bus->byte == bus->cmsg->len)
327                    {
328                        next_state(bus, STATE_IDLE);
329                        mcfmbus_machine(bus, EVENT_NEXTMSG);
330                    }
331                    else
332                    {
333                        mcfmbus_send(bus, bus->cmsg->buf[bus->byte++]);
334                        next_state(bus, STATE_SENDING);
335                    }
336                    break;
337
338                case EVENT_NACK:
339                    if (bus->byte == 0)
340                    {
341                        mcfmbus_error(bus, I2C_NO_DEVICE);
342                    }
343                    else
344                    {
345                        mcfmbus_error(bus, I2C_NO_ACKNOWLEDGE);
346                    }
347                    next_state(bus, STATE_IDLE);
348                    mcfmbus_machine(bus, EVENT_NEXTMSG);
349                    break;
350
351                case EVENT_ARB_LOST:
352                    mcfmbus_error(bus, I2C_ARBITRATION_LOST);
353                    next_state(bus, STATE_IDLE);
354                    mcfmbus_machine(bus, EVENT_NEXTMSG);
355                    break;
356
357                default:
358                    mcfmbus_machine_error(bus, event);
359                    break;
360
361            }
362            break;
363
364        case STATE_RECEIVING:
365            switch (event)
366            {
367                case EVENT_DATA_RECV:
368                    if (bus->cmsg->len - bus->byte <= 2)
369                    {
370                        mcfmbus_send_nack(bus);
371                        if (bus->cmsg->len - bus->byte <= 1)
372                        {
373                            if (bus->cmsg - bus->msg + 1 == bus->nmsg)
374                                mcfmbus_stop(bus);
375                            else
376                                mcfmbus_rstart(bus);
377                        }
378                    }
379                    else
380                    {
381                        mcfmbus_send_ack(bus);
382                    }
383                    bus->cmsg->buf[bus->byte++] = *MCF5206E_MBDR(bus->base);
384                    if (bus->cmsg->len == bus->byte)
385                    {
386                        next_state(bus,STATE_IDLE);
387                        mcfmbus_machine(bus, EVENT_NEXTMSG);
388                    }
389                    else
390                    {
391                        next_state(bus,STATE_RECEIVING);
392                    }
393                    break;
394
395                case EVENT_ARB_LOST:
396                    mcfmbus_error(bus, I2C_ARBITRATION_LOST);
397                    next_state(bus, STATE_IDLE);
398                    mcfmbus_machine(bus, EVENT_NEXTMSG);
399                    break;
400
401                default:
402                    mcfmbus_machine_error(bus, event);
403                    break;
404            }
405            break;
406    }
407}
408
409/* mcfmbus_interrupt_handler --
410 *     MBUS module interrupt handler routine
411 *
412 * PARAMETERS:
413 *     vector - interrupt vector number (not used)
414 *
415 * RETURNS:
416 *     none
417 */
418rtems_isr
419mcfmbus_interrupt_handler(rtems_vector_number vector)
420{
421    i2c_event event;
422    event = mcfmbus_get_event(mbus);
423    mcfmbus_machine(mbus, event);
424}
425
426/* mcfmbus_poll --
427 *     MBUS module poll routine; used to poll events when I2C driver
428 *     operates in poll-driven mode.
429 *
430 * PARAMETERS:
431 *     none
432 *
433 * RETURNS:
434 *     none
435 */
436void
437mcfmbus_poll(mcfmbus *bus)
438{
439    i2c_event event;
440    event = mcfmbus_get_event(bus);
441    if (event != EVENT_NONE)
442        mcfmbus_machine(bus, event);
443}
444
445/* mcfmbus_select_clock_divider --
446 *     Select divider for system clock which is used for I2C bus clock
447 *     generation. Not each divider can be selected for I2C bus; this
448 *     function select nearest larger or equal divider.
449 *
450 * PARAMETERS:
451 *     i2c_bus - pointer to the bus descriptor structure
452 *     divider - system frequency divider for I2C serial clock.
453 * RETURNS:
454 *     RTEMS_SUCCESSFUL, if operation performed successfully, or
455 *     RTEMS error code when failed.
456 */
457rtems_status_code
458mcfmbus_select_clock_divider(mcfmbus *i2c_bus, int divider)
459{
460    int i;
461    int mbc;
462    struct {
463        int divider;
464        int mbc;
465    } dividers[] ={
466        { 20,   0x20 }, { 22,   0x21 }, { 24,   0x22 }, { 26,   0x23 },
467        { 28,   0x00 }, { 30,   0x01 }, { 32,   0x25 }, { 34,   0x02 },
468        { 36,   0x26 }, { 40,   0x03 }, { 44,   0x04 }, { 48,   0x05 },
469        { 56,   0x06 }, { 64,   0x2a }, { 68,   0x07 }, { 72,   0x2B },
470        { 80,   0x08 }, { 88,   0x09 }, { 96,   0x2D }, { 104,  0x0A },
471        { 112,  0x2E }, { 128,  0x0B }, { 144,  0x0C }, { 160,  0x0D },
472        { 192,  0x0E }, { 224,  0x32 }, { 240,  0x0F }, { 256,  0x33 },
473        { 288,  0x10 }, { 320,  0x11 }, { 384,  0x12 }, { 448,  0x36 },
474        { 480,  0x13 }, { 512,  0x37 }, { 576,  0x14 }, { 640,  0x15 },
475        { 768,  0x16 }, { 896,  0x3A }, { 960,  0x17 }, { 1024, 0x3B },
476        { 1152, 0x18 }, { 1280, 0x19 }, { 1536, 0x1A }, { 1792, 0x3E },
477        { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D },
478        { 3072, 0x1E }, { 3840, 0x1F }
479    };
480
481    if (i2c_bus == NULL)
482        return RTEMS_INVALID_ADDRESS;
483
484    for (i = 0, mbc = -1; i < sizeof(dividers)/sizeof(dividers[0]); i++)
485    {
486        mbc = dividers[i].mbc;
487        if (dividers[i].divider >= divider)
488        {
489            break;
490        }
491    }
492    *MCF5206E_MFDR(i2c_bus->base) = mbc;
493    return RTEMS_SUCCESSFUL;
494}
495
496/* mcfmbus_initialize --
497 *     Initialize ColdFire MBUS I2C bus controller.
498 *
499 * PARAMETERS:
500 *     i2c_bus - pointer to the bus descriptor structure
501 *     base    - ColdFire internal peripherial base address
502 *
503 * RETURNS:
504 *     RTEMS_SUCCESSFUL, or RTEMS error code when initialization failed.
505 */
506rtems_status_code
507mcfmbus_initialize(mcfmbus *i2c_bus, uint32_t   base)
508{
509    rtems_interrupt_level level;
510    rtems_status_code sc;
511
512    if (mbus != NULL) /* Check if already initialized */
513        return RTEMS_RESOURCE_IN_USE;
514
515    if (i2c_bus == NULL)
516        return RTEMS_INVALID_ADDRESS;
517
518
519    i2c_bus->base = base;
520    i2c_bus->state = STATE_IDLE;
521    i2c_bus->msg = NULL;
522    i2c_bus->cmsg = NULL;
523    i2c_bus->nmsg = 0;
524    i2c_bus->byte = 0;
525
526    sc = rtems_interrupt_catch(
527        mcfmbus_interrupt_handler,
528        24 + ((*MCF5206E_ICR(base, MCF5206E_INTR_MBUS) & MCF5206E_ICR_IL) >>
529                                   MCF5206E_ICR_IL_S),
530        &i2c_bus->oldisr
531    );
532    if (sc != RTEMS_SUCCESSFUL)
533        return sc;
534
535    mbus = i2c_bus;
536    rtems_interrupt_disable(level);
537    *MCF5206E_IMR(base) &= ~MCF5206E_INTR_BIT(MCF5206E_INTR_MBUS);
538    *MCF5206E_MBCR(base) = 0;
539    *MCF5206E_MBSR(base) = 0;
540    *MCF5206E_MBDR(base) = 0x1F; /* Maximum possible divider is 3840 */
541    *MCF5206E_MBCR(base) = MCF5206E_MBCR_MEN | MCF5206E_MBCR_MIEN;
542    rtems_interrupt_enable(level);
543
544    return RTEMS_SUCCESSFUL;
545}
546
547/* mcfmbus_i2c_transfer --
548 *     Initiate multiple-messages transfer over I2C bus via ColdFire MBUS
549 *     controller.
550 *
551 * PARAMETERS:
552 *     bus - pointer to MBUS controller descriptor
553 *     nmsg - number of messages
554 *     msg - pointer to messages array
555 *     done - function which is called when transfer is finished
556 *     done_arg_ptr - arbitrary argument ptr passed to done funciton
557 *
558 * RETURNS:
559 *     RTEMS_SUCCESSFUL if transfer initiated successfully, or error
560 *     code when failed.
561 */
562rtems_status_code
563mcfmbus_i2c_transfer(mcfmbus *bus, int nmsg, i2c_message *msg,
564                     i2c_transfer_done done, void *done_arg_ptr)
565{
566    if (bus != mbus)
567        return RTEMS_NOT_CONFIGURED;
568
569    bus->done = done;
570    bus->done_arg_ptr = done_arg_ptr;
571    bus->cmsg = bus->msg = msg;
572    bus->nmsg = nmsg;
573    bus->byte = 0;
574    bus->state = STATE_IDLE;
575    mcfmbus_machine(bus, EVENT_TRANSFER);
576    return RTEMS_SUCCESSFUL;
577}
578
579
580/* mcfmbus_i2c_done --
581 *     Close ColdFire MBUS I2C bus controller and release all resources.
582 *
583 * PARAMETERS:
584 *     bus - pointer to MBUS controller descriptor
585 *
586 * RETURNS:
587 *     RTEMS_SUCCESSFUL, if transfer initiated successfully, or error
588 *     code when failed.
589 */
590rtems_status_code
591mcfmbus_i2c_done(mcfmbus *i2c_bus)
592{
593    rtems_status_code sc;
594    uint32_t   base;
595    if (mbus == NULL)
596        return RTEMS_NOT_CONFIGURED;
597
598    if (mbus != i2c_bus)
599        return RTEMS_INVALID_ADDRESS;
600
601    base = i2c_bus->base;
602
603    *MCF5206E_IMR(base) |= MCF5206E_INTR_BIT(MCF5206E_INTR_MBUS);
604    *MCF5206E_MBCR(base) = 0;
605
606    sc = rtems_interrupt_catch(
607        i2c_bus->oldisr,
608        24 + ((*MCF5206E_ICR(base, MCF5206E_INTR_MBUS) & MCF5206E_ICR_IL) >>
609                                   MCF5206E_ICR_IL_S),
610        NULL
611    );
612    return sc;
613}
Note: See TracBrowser for help on using the repository browser.