Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:46

0001 /* I2C driver for MCF5206eLITE board. I2C bus accessed through on-chip
0002  * MCF5206e MBUS controller.
0003  *
0004  * The purpose of this module is to perform I2C driver initialization
0005  * and serialize I2C transfers.
0006  *
0007  * Copyright (C) 2000 OKTET Ltd., St.-Petersburg, Russia
0008  * Author: Victor V. Vengerov <vvv@oktet.ru>
0009  *
0010  * The license and distribution terms for this file may be
0011  * found in the file LICENSE in this distribution or at
0012  *
0013  * http://www.rtems.org/license/LICENSE.
0014  */
0015 
0016 #include <rtems.h>
0017 #include <bsp.h>
0018 #include <stdlib.h>
0019 #include <string.h>
0020 
0021 #include "i2c.h"
0022 #include "i2cdrv.h"
0023 #include "mcf5206/mcfmbus.h"
0024 
0025 #ifndef I2C_NUMBER_OF_BUSES
0026 #define I2C_NUMBER_OF_BUSES (1)
0027 #endif
0028 
0029 #ifndef I2C_SELECT_BUS
0030 #define I2C_SELECT_BUS(bus)
0031 #endif
0032 
0033 /*
0034  * Few I2C transfers may be posted simultaneously, but MBUS driver is able
0035  * to process it one-by-one. To serialize transfers, function i2c_transfer
0036  * put transfer information to the queue and initiate new transfers if MBUS
0037  * driver is not busy. When driver is busy, next transfer is dequeued
0038  * when current active transfer is finished.
0039  */
0040 
0041 /*
0042  * i2c_qel - I2C transfers queue element; contain information about
0043  * delayed transfer
0044  */
0045 typedef struct i2c_qel {
0046     i2c_bus_number    bus;      /* I2C bus number */
0047     i2c_message      *msg;      /* pointer to the transfer' messages array */
0048     int               nmsg;     /* number of messages in transfer */
0049     i2c_transfer_done done;     /* transfer done callback function */
0050     void *    done_arg_ptr;     /* arbitrary arg pointer to done callback */
0051 } i2c_qel;
0052 
0053 /* Memory for I2C transfer queue. This queue represented like a ring buffer */
0054 static i2c_qel *tqueue;
0055 
0056 /* Maximum number of elements in transfer queue */
0057 static int tqueue_size;
0058 
0059 /* Position of next free element in a ring buffer */
0060 static volatile int tqueue_head;
0061 
0062 /* Position of the first element in transfer queue */
0063 static volatile int tqueue_tail;
0064 
0065 /* MBus I2C bus controller busy flag */
0066 static volatile bool mbus_busy;
0067 
0068 /* MBus I2C bus controller descriptor */
0069 static mcfmbus mbus;
0070 
0071 /* Clock rate selected for each of bus */
0072 static int i2cdrv_bus_clock_div[I2C_NUMBER_OF_BUSES];
0073 
0074 /* Currently selected I2C bus clock rate */
0075 static int i2cdrv_bus_clock_div_current;
0076 
0077 /* Forward function declaration */
0078 static void i2cdrv_unload(void);
0079 
0080 /* i2cdrv_done --
0081  *     Callback function which is called from MBus low-level driver when
0082  *     transfer is finished.
0083  */
0084 static void
0085 i2cdrv_done(void * arg_ptr)
0086 {
0087     rtems_interrupt_level level;
0088     i2c_qel *qel = tqueue + tqueue_tail;
0089     qel->done(qel->done_arg_ptr);
0090     rtems_interrupt_disable(level);
0091     tqueue_tail = (tqueue_tail + 1) % tqueue_size;
0092     mbus_busy = false;
0093     rtems_interrupt_enable(level);
0094     i2cdrv_unload();
0095 }
0096 
0097 /* i2cdrv_unload --
0098  *     If MBUS controller is not busy and transfer waiting in a queue,
0099  *     initiate processing of the next transfer in queue.
0100  */
0101 static void
0102 i2cdrv_unload(void)
0103 {
0104     rtems_interrupt_level level;
0105     i2c_qel *qel;
0106     rtems_status_code sc;
0107     rtems_interrupt_disable(level);
0108     if (!mbus_busy && (tqueue_head != tqueue_tail))
0109     {
0110         mbus_busy = true;
0111         rtems_interrupt_enable(level);
0112         qel = tqueue + tqueue_tail;
0113 
0114         I2C_SELECT_BUS(qel->bus);
0115         if (i2cdrv_bus_clock_div[qel->bus] != i2cdrv_bus_clock_div_current)
0116         {
0117             i2cdrv_bus_clock_div_current = i2cdrv_bus_clock_div[qel->bus];
0118             mcfmbus_select_clock_divider(&mbus, i2cdrv_bus_clock_div_current);
0119         }
0120         sc = mcfmbus_i2c_transfer(&mbus, qel->nmsg, qel->msg,
0121                   i2cdrv_done,qel);
0122         if (sc != RTEMS_SUCCESSFUL)
0123         {
0124             int i;
0125             for (i = 0; i < qel->nmsg; i++)
0126             {
0127                 qel->msg[i].status = I2C_RESOURCE_NOT_AVAILABLE;
0128             }
0129             i2cdrv_done(qel);
0130         }
0131     }
0132     else
0133     {
0134         rtems_interrupt_enable(level);
0135     }
0136 }
0137 
0138 /* i2c_transfer --
0139  *     Initiate multiple-messages transfer over specified I2C bus or
0140  *     put request into queue if bus or some other resource is busy. (This
0141  *     is non-blocking function).
0142  *
0143  * PARAMETERS:
0144  *     bus - I2C bus number
0145  *     nmsg - number of messages
0146  *     msg - pointer to messages array
0147  *     done - function which is called when transfer is finished
0148  *     done_arg_ptr - arbitrary argument pointer passed to done funciton
0149  *
0150  * RETURNS:
0151  *     RTEMS_SUCCESSFUL if transfer initiated successfully, or error
0152  *     code if something failed.
0153  */
0154 rtems_status_code
0155 i2c_transfer(i2c_bus_number bus, int nmsg, i2c_message *msg,
0156              i2c_transfer_done done, void *     done_arg_ptr)
0157 {
0158     i2c_qel qel;
0159     rtems_interrupt_level level;
0160 
0161     if (bus >= I2C_NUMBER_OF_BUSES)
0162     {
0163         return RTEMS_INVALID_NUMBER;
0164     }
0165 
0166     if (msg == NULL)
0167     {
0168         return RTEMS_INVALID_ADDRESS;
0169     }
0170 
0171     qel.bus = bus;
0172     qel.msg = msg;
0173     qel.nmsg = nmsg;
0174     qel.done = done;
0175     qel.done_arg_ptr = done_arg_ptr;
0176     rtems_interrupt_disable(level);
0177     if ((tqueue_head + 1) % tqueue_size == tqueue_tail)
0178     {
0179         rtems_interrupt_enable(level);
0180         return RTEMS_TOO_MANY;
0181     }
0182     memcpy(tqueue + tqueue_head, &qel, sizeof(qel));
0183     tqueue_head = (tqueue_head + 1) % tqueue_size;
0184     rtems_interrupt_enable(level);
0185     i2cdrv_unload();
0186     return RTEMS_SUCCESSFUL;
0187 }
0188 
0189 /* i2cdrv_initialize --
0190  *     I2C driver initialization (rtems I/O driver primitive)
0191  */
0192 rtems_device_driver
0193 i2cdrv_initialize(rtems_device_major_number major,
0194                   rtems_device_minor_number minor,
0195                   void *arg)
0196 {
0197     int i;
0198     rtems_status_code sc;
0199     mbus_busy = false;
0200     tqueue_tail = tqueue_head = 0;
0201     tqueue_size = 32;
0202     tqueue = calloc(tqueue_size, sizeof(i2c_qel));
0203 
0204     sc = mcfmbus_initialize(&mbus, MBAR);
0205     if (sc != RTEMS_SUCCESSFUL)
0206         return sc;
0207 
0208     for (i = 0; i < I2C_NUMBER_OF_BUSES; i++)
0209     {
0210         sc = i2c_select_clock_rate(i, 4096);
0211         if (sc != RTEMS_SUCCESSFUL)
0212             return sc;
0213     }
0214     i2cdrv_bus_clock_div_current = -1;
0215     return RTEMS_SUCCESSFUL;
0216 }
0217 
0218 /* i2c_select_clock_rate --
0219  *     select I2C bus clock rate for specified bus. Some bus controller do not
0220  *     allow to select arbitrary clock rate; in this case nearest possible
0221  *     slower clock rate is selected.
0222  *
0223  * PARAMETERS:
0224  *     bus - I2C bus number
0225  *     bps - data transfer rate for this bytes in bits per second
0226  *
0227  * RETURNS:
0228  *     RTEMS_SUCCESSFUL, if operation performed successfully,
0229  *     RTEMS_INVALID_NUMBER, if wrong bus number is specified,
0230  *     RTEMS_UNSATISFIED, if bus do not support data transfer rate selection
0231  *     or specified data transfer rate could not be used.
0232  */
0233 rtems_status_code
0234 i2c_select_clock_rate(i2c_bus_number bus, int bps)
0235 {
0236     int div;
0237     if (bus >= I2C_NUMBER_OF_BUSES)
0238         return RTEMS_INVALID_NUMBER;
0239 
0240     if (bps == 0)
0241         return RTEMS_UNSATISFIED;
0242 
0243     div = BSP_SYSTEM_FREQUENCY / bps;
0244     i2cdrv_bus_clock_div[bus] = div;
0245     return RTEMS_SUCCESSFUL;
0246 }
0247 
0248 /* i2c_poll --
0249  *     Poll I2C bus controller for events and hanle it. This function is
0250  *     used when I2C driver operates in poll-driven mode.
0251  *
0252  * PARAMETERS:
0253  *     bus - bus number to be polled
0254  *
0255  * RETURNS:
0256  *     none
0257  */
0258 void
0259 i2c_poll(i2c_bus_number bus)
0260 {
0261     mcfmbus_poll(&mbus);
0262 }