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

4.115
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

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