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

4.104.114.84.95
Last change on this file since e56c3546 was e56c3546, checked in by Joel Sherrill <joel.sherrill@…>, on 10/26/01 at 19:30:11

2001-10-26 Victor V. Vengerov <vvv@…>

  • New BSP for MCF5206eLITE evaluation board BSP.
  • ChangeLog?, README, bsp_specs, configure.ac, console/console.c, console/.cvsignore, i2c/i2c.c, i2c/i2cdrv.c, i2c/.cvsignore, include/bsp.h, include/bspopts.h.in, include/coverhd.h, include/ds1307.h, include/i2c.h, include/i2cdrv.h, include/nvram.h, include/.cvsignore, nvram/nvram.c, nvram/.cvsignore, start/start.S, start/.cvsignore, startup/bspclean.c, startup/bspstart.c, startup/gdbinit, startup/init5206e.c, startup/linkcmds, startup/linkcmds.flash, startup/.cvsignore, times, tod/ds1307.c, tod/todcfg.c, tod/.cvsignore, tools/.cvsignore, tools/configure.ac, tools/runtest, tools/changes, wrapup/.cvsignore, .cvsignore: New files.
  • 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.OARcorp.com/rtems/license.html.
14 *
15 * @(#) $Id$
16 */
17
18#include <rtems.h>
19#include <bsp.h>
20#include <stdlib.h>
21
22#include "i2c.h"
23#include "i2cdrv.h"
24#include "mcf5206/mcfmbus.h"
25
26#ifndef I2C_NUMBER_OF_BUSES
27#define I2C_NUMBER_OF_BUSES (1)
28#endif
29
30#ifndef I2C_SELECT_BUS
31#define I2C_SELECT_BUS(bus)
32#endif
33
34/*
35 * Few I2C transfers may be posted simultaneously, but MBUS driver is able
36 * to process it one-by-one. To serialize transfers, function i2c_transfer
37 * put transfer information to the queue and initiate new transfers if MBUS
38 * driver is not busy. When driver is busy, next transfer is dequeued
39 * when current active transfer is finished.
40 */
41
42/*
43 * i2c_qel - I2C transfers queue element; contain information about
44 * delayed transfer
45 */
46typedef struct i2c_qel {
47    i2c_bus_number    bus;      /* I2C bus number */
48    i2c_message      *msg;      /* pointer to the transfer' messages array */
49    int               nmsg;     /* number of messages in transfer */
50    i2c_transfer_done done;     /* transfer done callback function */
51    rtems_unsigned32  done_arg; /* arbitrary argument to done callback */
52} i2c_qel;
53
54/* Memory for I2C transfer queue. This queue represented like a ring buffer */
55static i2c_qel *tqueue;
56
57/* Maximum number of elements in transfer queue */
58static int tqueue_size;
59
60/* Position of next free element in a ring buffer */
61static volatile int tqueue_head;
62
63/* Position of the first element in transfer queue */
64static volatile int tqueue_tail;
65
66/* MBus I2C bus controller busy flag */
67static volatile rtems_boolean mbus_busy;
68
69/* MBus I2C bus controller descriptor */
70static mcfmbus mbus;
71
72/* Clock rate selected for each of bus */
73static int i2cdrv_bus_clock_div[I2C_NUMBER_OF_BUSES];
74
75/* Currently selected I2C bus clock rate */
76static int i2cdrv_bus_clock_div_current;
77
78/* Forward function declaration */
79static void i2cdrv_unload(void);
80
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(rtems_unsigned32 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 = 0;
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 = 1;
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                                  (rtems_unsigned32)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((rtems_unsigned32)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, rtems_unsigned32 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
192/* i2cdrv_initialize --
193 *     I2C driver initialization (rtems I/O driver primitive)
194 */
195rtems_device_driver
196i2cdrv_initialize(rtems_device_major_number major,
197                  rtems_device_minor_number minor,
198                  void *arg)
199{
200    int i;
201    rtems_status_code sc;
202    mbus_busy = 0;
203    tqueue_tail = tqueue_head = 0;
204    tqueue_size = 32;
205    tqueue = calloc(tqueue_size, sizeof(i2c_qel));
206   
207    sc = mcfmbus_initialize(&mbus, MBAR);
208    if (sc != RTEMS_SUCCESSFUL)
209        return sc;
210   
211    for (i = 0; i < I2C_NUMBER_OF_BUSES; i++)
212    {
213        sc = i2c_select_clock_rate(i, 4096);
214        if (sc != RTEMS_SUCCESSFUL)
215            return sc;
216    }
217    i2cdrv_bus_clock_div_current = -1;
218    return RTEMS_SUCCESSFUL;
219}
220
221/* i2c_select_clock_rate --
222 *     select I2C bus clock rate for specified bus. Some bus controller do not
223 *     allow to select arbitrary clock rate; in this case nearest possible
224 *     slower clock rate is selected.
225 *
226 * PARAMETERS:
227 *     bus - I2C bus number
228 *     bps - data transfer rate for this bytes in bits per second
229 *
230 * RETURNS:
231 *     RTEMS_SUCCESSFUL, if operation performed successfully,
232 *     RTEMS_INVALID_NUMBER, if wrong bus number is specified,
233 *     RTEMS_UNSATISFIED, if bus do not support data transfer rate selection
234 *     or specified data transfer rate could not be used.
235 */
236rtems_status_code
237i2c_select_clock_rate(i2c_bus_number bus, int bps)
238{
239    int div;
240    if (bus >= I2C_NUMBER_OF_BUSES)
241        return RTEMS_INVALID_NUMBER;
242   
243    if (bps == 0)
244        return RTEMS_UNSATISFIED;
245   
246    div = BSP_SYSTEM_FREQUENCY / bps;
247    i2cdrv_bus_clock_div[bus] = div;
248    return RTEMS_SUCCESSFUL;
249}
250
251
252/* i2c_poll --
253 *     Poll I2C bus controller for events and hanle it. This function is
254 *     used when I2C driver operates in poll-driven mode.
255 *
256 * PARAMETERS:
257 *     bus - bus number to be polled
258 *
259 * RETURNS:
260 *     none
261 */
262void
263i2c_poll(i2c_bus_number bus)
264{
265    mcfmbus_poll(&mbus);
266}
Note: See TracBrowser for help on using the repository browser.