source: rtems/c/src/lib/libbsp/m68k/mcf5206elite/i2c/i2cdrv.c @ b7044dc

4.104.114.84.95
Last change on this file since b7044dc was b7044dc, checked in by Ralf Corsepius <ralf.corsepius@…>, on Mar 31, 2004 at 4:40:43 AM

2004-03-31 Ralf Corsepius <ralf_corsepius@…>

  • console/console.c, i2c/i2c.c, i2c/i2cdrv.c, include/bsp.h, include/i2c.h, nvram/nvram.c, startup/bspstart.c, startup/init5206e.c, tod/ds1307.c: Convert to using c99 fixed size types.
  • Property mode set to 100644
File size: 7.2 KB
Line 
1/* I2C driver for MCF5206eLITE board. I2C bus accessed through on-chip
2 * MCF5206e MBUS controller.
3 *
4 * The purpose of this module is to perform I2C driver initialization
5 * and serialize I2C transfers.
6 *
7 * Copyright (C) 2000 OKTET Ltd., St.-Petersburg, Russia
8 * Author: Victor V. Vengerov <vvv@oktet.ru>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 *
13 * http://www.rtems.com/license/LICENSE.
14 *
15 * @(#) $Id$
16 */
17
18#include <rtems.h>
19#include <bsp.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include "i2c.h"
24#include "i2cdrv.h"
25#include "mcf5206/mcfmbus.h"
26
27#ifndef I2C_NUMBER_OF_BUSES
28#define I2C_NUMBER_OF_BUSES (1)
29#endif
30
31#ifndef I2C_SELECT_BUS
32#define I2C_SELECT_BUS(bus)
33#endif
34
35/*
36 * Few I2C transfers may be posted simultaneously, but MBUS driver is able
37 * to process it one-by-one. To serialize transfers, function i2c_transfer
38 * put transfer information to the queue and initiate new transfers if MBUS
39 * driver is not busy. When driver is busy, next transfer is dequeued
40 * when current active transfer is finished.
41 */
42
43/*
44 * i2c_qel - I2C transfers queue element; contain information about
45 * delayed transfer
46 */
47typedef struct i2c_qel {
48    i2c_bus_number    bus;      /* I2C bus number */
49    i2c_message      *msg;      /* pointer to the transfer' messages array */
50    int               nmsg;     /* number of messages in transfer */
51    i2c_transfer_done done;     /* transfer done callback function */
52    uint32_t          done_arg; /* arbitrary argument to done callback */
53} i2c_qel;
54
55/* Memory for I2C transfer queue. This queue represented like a ring buffer */
56static i2c_qel *tqueue;
57
58/* Maximum number of elements in transfer queue */
59static int tqueue_size;
60
61/* Position of next free element in a ring buffer */
62static volatile int tqueue_head;
63
64/* Position of the first element in transfer queue */
65static volatile int tqueue_tail;
66
67/* MBus I2C bus controller busy flag */
68static volatile rtems_boolean mbus_busy;
69
70/* MBus I2C bus controller descriptor */
71static mcfmbus mbus;
72
73/* Clock rate selected for each of bus */
74static int i2cdrv_bus_clock_div[I2C_NUMBER_OF_BUSES];
75
76/* Currently selected I2C bus clock rate */
77static int i2cdrv_bus_clock_div_current;
78
79/* Forward function declaration */
80static void i2cdrv_unload(void);
81
82
83/* i2cdrv_done --
84 *     Callback function which is called from MBus low-level driver when
85 *     transfer is finished.
86 */
87static void
88i2cdrv_done(uint32_t         arg)
89{
90    rtems_interrupt_level level;
91    i2c_qel *qel = tqueue + tqueue_tail;
92    qel->done(qel->done_arg);
93    rtems_interrupt_disable(level);
94    tqueue_tail = (tqueue_tail + 1) % tqueue_size;
95    mbus_busy = 0;
96    rtems_interrupt_enable(level);
97    i2cdrv_unload();
98}
99
100/* i2cdrv_unload --
101 *     If MBUS controller is not busy and transfer waiting in a queue,
102 *     initiate processing of the next transfer in queue.
103 */
104static void
105i2cdrv_unload(void)
106{
107    rtems_interrupt_level level;
108    i2c_qel *qel;
109    rtems_status_code sc;
110    rtems_interrupt_disable(level);
111    if (!mbus_busy && (tqueue_head != tqueue_tail))
112    {
113        mbus_busy = 1;
114        rtems_interrupt_enable(level);
115        qel = tqueue + tqueue_tail;
116       
117        I2C_SELECT_BUS(qel->bus);
118        if (i2cdrv_bus_clock_div[qel->bus] != i2cdrv_bus_clock_div_current)
119        {
120            i2cdrv_bus_clock_div_current = i2cdrv_bus_clock_div[qel->bus];
121            mcfmbus_select_clock_divider(&mbus, i2cdrv_bus_clock_div_current);
122        }
123        sc = mcfmbus_i2c_transfer(&mbus, qel->nmsg, qel->msg, i2cdrv_done,
124                                  (uint32_t)qel);
125        if (sc != RTEMS_SUCCESSFUL)
126        {
127            int i;
128            for (i = 0; i < qel->nmsg; i++)
129            {
130                qel->msg[i].status = I2C_RESOURCE_NOT_AVAILABLE;
131            }
132            i2cdrv_done((uint32_t)qel);
133        }
134    }
135    else
136    {
137        rtems_interrupt_enable(level);
138    }
139}
140
141/* i2c_transfer --
142 *     Initiate multiple-messages transfer over specified I2C bus or
143 *     put request into queue if bus or some other resource is busy. (This
144 *     is non-blocking function).
145 *
146 * PARAMETERS:
147 *     bus - I2C bus number
148 *     nmsg - number of messages
149 *     msg - pointer to messages array
150 *     done - function which is called when transfer is finished
151 *     done_arg - arbitrary argument passed to done funciton
152 *
153 * RETURNS:
154 *     RTEMS_SUCCESSFUL if transfer initiated successfully, or error
155 *     code if something failed.
156 */
157rtems_status_code
158i2c_transfer(i2c_bus_number bus, int nmsg, i2c_message *msg,
159             i2c_transfer_done done, uint32_t         done_arg)
160{
161    i2c_qel qel;
162    rtems_interrupt_level level;
163   
164    if (bus >= I2C_NUMBER_OF_BUSES)
165    {
166        return RTEMS_INVALID_NUMBER;
167    }
168   
169    if (msg == NULL)
170    {
171        return RTEMS_INVALID_ADDRESS;
172    }
173   
174    qel.bus = bus;
175    qel.msg = msg;
176    qel.nmsg = nmsg;
177    qel.done = done;
178    qel.done_arg = done_arg;
179    rtems_interrupt_disable(level);
180    if ((tqueue_head + 1) % tqueue_size == tqueue_tail)
181    {
182        rtems_interrupt_enable(level);
183        return RTEMS_TOO_MANY;
184    }
185    memcpy(tqueue + tqueue_head, &qel, sizeof(qel));
186    tqueue_head = (tqueue_head + 1) % tqueue_size;
187    rtems_interrupt_enable(level);
188    i2cdrv_unload();
189    return RTEMS_SUCCESSFUL;
190}
191
192
193/* i2cdrv_initialize --
194 *     I2C driver initialization (rtems I/O driver primitive)
195 */
196rtems_device_driver
197i2cdrv_initialize(rtems_device_major_number major,
198                  rtems_device_minor_number minor,
199                  void *arg)
200{
201    int i;
202    rtems_status_code sc;
203    mbus_busy = 0;
204    tqueue_tail = tqueue_head = 0;
205    tqueue_size = 32;
206    tqueue = calloc(tqueue_size, sizeof(i2c_qel));
207   
208    sc = mcfmbus_initialize(&mbus, MBAR);
209    if (sc != RTEMS_SUCCESSFUL)
210        return sc;
211   
212    for (i = 0; i < I2C_NUMBER_OF_BUSES; i++)
213    {
214        sc = i2c_select_clock_rate(i, 4096);
215        if (sc != RTEMS_SUCCESSFUL)
216            return sc;
217    }
218    i2cdrv_bus_clock_div_current = -1;
219    return RTEMS_SUCCESSFUL;
220}
221
222/* i2c_select_clock_rate --
223 *     select I2C bus clock rate for specified bus. Some bus controller do not
224 *     allow to select arbitrary clock rate; in this case nearest possible
225 *     slower clock rate is selected.
226 *
227 * PARAMETERS:
228 *     bus - I2C bus number
229 *     bps - data transfer rate for this bytes in bits per second
230 *
231 * RETURNS:
232 *     RTEMS_SUCCESSFUL, if operation performed successfully,
233 *     RTEMS_INVALID_NUMBER, if wrong bus number is specified,
234 *     RTEMS_UNSATISFIED, if bus do not support data transfer rate selection
235 *     or specified data transfer rate could not be used.
236 */
237rtems_status_code
238i2c_select_clock_rate(i2c_bus_number bus, int bps)
239{
240    int div;
241    if (bus >= I2C_NUMBER_OF_BUSES)
242        return RTEMS_INVALID_NUMBER;
243   
244    if (bps == 0)
245        return RTEMS_UNSATISFIED;
246   
247    div = BSP_SYSTEM_FREQUENCY / bps;
248    i2cdrv_bus_clock_div[bus] = div;
249    return RTEMS_SUCCESSFUL;
250}
251
252
253/* i2c_poll --
254 *     Poll I2C bus controller for events and hanle it. This function is
255 *     used when I2C driver operates in poll-driven mode.
256 *
257 * PARAMETERS:
258 *     bus - bus number to be polled
259 *
260 * RETURNS:
261 *     none
262 */
263void
264i2c_poll(i2c_bus_number bus)
265{
266    mcfmbus_poll(&mbus);
267}
Note: See TracBrowser for help on using the repository browser.