source: rtems/c/src/lib/libbsp/powerpc/gen5200/i2c/i2cdrv.c @ ca680bc5

4.104.114.84.95
Last change on this file since ca680bc5 was ca680bc5, checked in by Ralf Corsepius <ralf.corsepius@…>, on 12/31/05 at 05:09:26

New (CVS import Thomas Doerfler <Thomas.Doerfler@…>'s
submission).

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