Back to home page

LXR

 
 

    


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

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