source: rtems/bsps/powerpc/gen5200/i2c/i2cdrv.c @ 3fb2a815

Last change on this file since 3fb2a815 was 3fb2a815, checked in by Christian Mauderer <christian.mauderer@…>, on 03/03/22 at 08:17:22

bsps/powerpc/gen5200: Manual file header clean up

Updates #4625.

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