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

4.104.114.95
Last change on this file since fcd7483 was fcd7483, checked in by Ralf Corsepius <ralf.corsepius@…>, on 09/05/08 at 11:40:32

Convert to "bool".

  • Property mode set to 100644
File size: 7.1 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 bool 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/* i2cdrv_done --
83 *     Callback function which is called from MBus low-level driver when
84 *     transfer is finished.
85 */
86static void
87i2cdrv_done(uint32_t         arg)
88{
89    rtems_interrupt_level level;
90    i2c_qel *qel = tqueue + tqueue_tail;
91    qel->done(qel->done_arg);
92    rtems_interrupt_disable(level);
93    tqueue_tail = (tqueue_tail + 1) % tqueue_size;
94    mbus_busy = false;
95    rtems_interrupt_enable(level);
96    i2cdrv_unload();
97}
98
99/* i2cdrv_unload --
100 *     If MBUS controller is not busy and transfer waiting in a queue,
101 *     initiate processing of the next transfer in queue.
102 */
103static void
104i2cdrv_unload(void)
105{
106    rtems_interrupt_level level;
107    i2c_qel *qel;
108    rtems_status_code sc;
109    rtems_interrupt_disable(level);
110    if (!mbus_busy && (tqueue_head != tqueue_tail))
111    {
112        mbus_busy = true;
113        rtems_interrupt_enable(level);
114        qel = tqueue + tqueue_tail;
115
116        I2C_SELECT_BUS(qel->bus);
117        if (i2cdrv_bus_clock_div[qel->bus] != i2cdrv_bus_clock_div_current)
118        {
119            i2cdrv_bus_clock_div_current = i2cdrv_bus_clock_div[qel->bus];
120            mcfmbus_select_clock_divider(&mbus, i2cdrv_bus_clock_div_current);
121        }
122        sc = mcfmbus_i2c_transfer(&mbus, qel->nmsg, qel->msg, i2cdrv_done,
123                                  (uint32_t)qel);
124        if (sc != RTEMS_SUCCESSFUL)
125        {
126            int i;
127            for (i = 0; i < qel->nmsg; i++)
128            {
129                qel->msg[i].status = I2C_RESOURCE_NOT_AVAILABLE;
130            }
131            i2cdrv_done((uint32_t)qel);
132        }
133    }
134    else
135    {
136        rtems_interrupt_enable(level);
137    }
138}
139
140/* i2c_transfer --
141 *     Initiate multiple-messages transfer over specified I2C bus or
142 *     put request into queue if bus or some other resource is busy. (This
143 *     is non-blocking function).
144 *
145 * PARAMETERS:
146 *     bus - I2C bus number
147 *     nmsg - number of messages
148 *     msg - pointer to messages array
149 *     done - function which is called when transfer is finished
150 *     done_arg - arbitrary argument passed to done funciton
151 *
152 * RETURNS:
153 *     RTEMS_SUCCESSFUL if transfer initiated successfully, or error
154 *     code if something failed.
155 */
156rtems_status_code
157i2c_transfer(i2c_bus_number bus, int nmsg, i2c_message *msg,
158             i2c_transfer_done done, uint32_t         done_arg)
159{
160    i2c_qel qel;
161    rtems_interrupt_level level;
162
163    if (bus >= I2C_NUMBER_OF_BUSES)
164    {
165        return RTEMS_INVALID_NUMBER;
166    }
167
168    if (msg == NULL)
169    {
170        return RTEMS_INVALID_ADDRESS;
171    }
172
173    qel.bus = bus;
174    qel.msg = msg;
175    qel.nmsg = nmsg;
176    qel.done = done;
177    qel.done_arg = done_arg;
178    rtems_interrupt_disable(level);
179    if ((tqueue_head + 1) % tqueue_size == tqueue_tail)
180    {
181        rtems_interrupt_enable(level);
182        return RTEMS_TOO_MANY;
183    }
184    memcpy(tqueue + tqueue_head, &qel, sizeof(qel));
185    tqueue_head = (tqueue_head + 1) % tqueue_size;
186    rtems_interrupt_enable(level);
187    i2cdrv_unload();
188    return RTEMS_SUCCESSFUL;
189}
190
191/* i2cdrv_initialize --
192 *     I2C driver initialization (rtems I/O driver primitive)
193 */
194rtems_device_driver
195i2cdrv_initialize(rtems_device_major_number major,
196                  rtems_device_minor_number minor,
197                  void *arg)
198{
199    int i;
200    rtems_status_code sc;
201    mbus_busy = false;
202    tqueue_tail = tqueue_head = 0;
203    tqueue_size = 32;
204    tqueue = calloc(tqueue_size, sizeof(i2c_qel));
205
206    sc = mcfmbus_initialize(&mbus, MBAR);
207    if (sc != RTEMS_SUCCESSFUL)
208        return sc;
209
210    for (i = 0; i < I2C_NUMBER_OF_BUSES; i++)
211    {
212        sc = i2c_select_clock_rate(i, 4096);
213        if (sc != RTEMS_SUCCESSFUL)
214            return sc;
215    }
216    i2cdrv_bus_clock_div_current = -1;
217    return RTEMS_SUCCESSFUL;
218}
219
220/* i2c_select_clock_rate --
221 *     select I2C bus clock rate for specified bus. Some bus controller do not
222 *     allow to select arbitrary clock rate; in this case nearest possible
223 *     slower clock rate is selected.
224 *
225 * PARAMETERS:
226 *     bus - I2C bus number
227 *     bps - data transfer rate for this bytes in bits per second
228 *
229 * RETURNS:
230 *     RTEMS_SUCCESSFUL, if operation performed successfully,
231 *     RTEMS_INVALID_NUMBER, if wrong bus number is specified,
232 *     RTEMS_UNSATISFIED, if bus do not support data transfer rate selection
233 *     or specified data transfer rate could not be used.
234 */
235rtems_status_code
236i2c_select_clock_rate(i2c_bus_number bus, int bps)
237{
238    int div;
239    if (bus >= I2C_NUMBER_OF_BUSES)
240        return RTEMS_INVALID_NUMBER;
241
242    if (bps == 0)
243        return RTEMS_UNSATISFIED;
244
245    div = BSP_SYSTEM_FREQUENCY / bps;
246    i2cdrv_bus_clock_div[bus] = div;
247    return RTEMS_SUCCESSFUL;
248}
249
250/* i2c_poll --
251 *     Poll I2C bus controller for events and hanle it. This function is
252 *     used when I2C driver operates in poll-driven mode.
253 *
254 * PARAMETERS:
255 *     bus - bus number to be polled
256 *
257 * RETURNS:
258 *     none
259 */
260void
261i2c_poll(i2c_bus_number bus)
262{
263    mcfmbus_poll(&mbus);
264}
Note: See TracBrowser for help on using the repository browser.