summaryrefslogtreecommitdiffstats
path: root/bsps/m68k
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-23 09:45:28 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-23 15:18:43 +0200
commita2dad96ab736f66ed54421cad53caf31f250e181 (patch)
tree1935320b5b52276ed2e52741cdae71637f209f77 /bsps/m68k
parentbsps/arm: Remove unused stm32f* files (diff)
downloadrtems-a2dad96ab736f66ed54421cad53caf31f250e181.tar.bz2
bsps: Move I2C drivers to bsps
This patch is a part of the BSP source reorganization. Update #3285.
Diffstat (limited to 'bsps/m68k')
-rw-r--r--bsps/m68k/mcf5206elite/i2c/i2c.c320
-rw-r--r--bsps/m68k/mcf5206elite/i2c/i2cdrv.c262
2 files changed, 582 insertions, 0 deletions
diff --git a/bsps/m68k/mcf5206elite/i2c/i2c.c b/bsps/m68k/mcf5206elite/i2c/i2c.c
new file mode 100644
index 0000000000..f77fe1ebcc
--- /dev/null
+++ b/bsps/m68k/mcf5206elite/i2c/i2c.c
@@ -0,0 +1,320 @@
+/* I2C bus common (driver-independent) primitives implementation.
+ *
+ * Copyright (C) 2000 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ *
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <bsp.h>
+#include <i2c.h>
+#include <rtems/score/sysstate.h>
+
+/* i2c_transfer_sema_done_func --
+ * This function called from I2C driver layer to signal that I2C
+ * transfer is finished. This function resumes of task execution which
+ * has invoked blocking I2C primitive.
+ *
+ * PARAMETERS:
+ * arg - done function argument; it is RTEMS semaphore ID.
+ */
+static void
+i2c_transfer_sema_done_func(void * arg)
+{
+ rtems_id sema = *(rtems_id *)arg;
+ rtems_semaphore_release(sema);
+}
+
+/* i2c_transfer_poll_done_func --
+ * This function called from I2C driver layer to signal that I2C
+ * transfer is finished. This function set the flag polled by waiting
+ * function.
+ *
+ * PARAMETERS:
+ * arg - done function argument; address of poll_done_flag
+ */
+static void
+i2c_transfer_poll_done_func(void *arg)
+{
+ bool *poll_done_flag = (bool *)arg;
+ *poll_done_flag = true;
+}
+
+/* i2c_transfer_wait_sema --
+ * Initiate I2C bus transfer and block on temporary created semaphore
+ * until this transfer will be finished.
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * msg - pointer to transfer messages array
+ * nmsg - number of messages in transfer
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL, if tranfer finished successfully,
+ * or RTEMS status code if semaphore operations has failed.
+ */
+static i2c_message_status
+i2c_transfer_wait_sema(i2c_bus_number bus, i2c_message *msg, int nmsg)
+{
+ rtems_status_code sc;
+ rtems_id sema;
+ sc = rtems_semaphore_create(
+ rtems_build_name('I', '2', 'C', 'S'),
+ 0,
+ RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
+ RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
+ 0,
+ &sema
+ );
+ if (sc != RTEMS_SUCCESSFUL)
+ return I2C_RESOURCE_NOT_AVAILABLE;
+ sc = i2c_transfer(bus, nmsg, msg,
+ i2c_transfer_sema_done_func, &sema);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ rtems_semaphore_delete(sema);
+ return sc;
+ }
+ rtems_semaphore_obtain(sema, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ sc = rtems_semaphore_delete(sema);
+ return sc;
+}
+
+/* i2c_transfer_wait_poll --
+ * Initiate I2C bus transfer and wait by poll transaction done flag until
+ * this transfer will be finished.
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * msg - pointer to transfer messages array
+ * nmsg - number of messages in transfer
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL
+ */
+static rtems_status_code
+i2c_transfer_wait_poll(i2c_bus_number bus, i2c_message *msg, int nmsg)
+{
+ /*
+ * this looks nasty, but is correct:
+ * we wait in this function, until the poll_done_flag is
+ * set deep inside the i2c_poll() function
+ */
+ volatile bool poll_done_flag;
+ rtems_status_code sc;
+ poll_done_flag = false;
+ sc = i2c_transfer(bus, nmsg, msg,
+ i2c_transfer_poll_done_func,(void *)&poll_done_flag);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+ while (poll_done_flag == false)
+ {
+ i2c_poll(bus);
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+/* i2c_transfer_wait --
+ * Initiate I2C bus transfer and block until this transfer will be
+ * finished. This function wait the semaphore if system in
+ * SYSTEM_STATE_UP state, or poll done flag in other states.
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * msg - pointer to transfer messages array
+ * nmsg - number of messages in transfer
+ *
+ * RETURNS:
+ * I2C_SUCCESSFUL, if tranfer finished successfully,
+ * I2C_RESOURCE_NOT_AVAILABLE, if semaphore operations has failed,
+ * value of status field of first error-finished message in transfer,
+ * if something wrong.
+ */
+i2c_message_status
+i2c_transfer_wait(i2c_bus_number bus, i2c_message *msg, int nmsg)
+{
+ rtems_status_code sc;
+ int i;
+ if (_System_state_Is_up(_System_state_Get()))
+ {
+ sc = i2c_transfer_wait_sema(bus, msg, nmsg);
+ }
+ else
+ {
+ sc = i2c_transfer_wait_poll(bus, msg, nmsg);
+ }
+
+ if (sc != RTEMS_SUCCESSFUL)
+ return I2C_RESOURCE_NOT_AVAILABLE;
+
+ for (i = 0; i < nmsg; i++)
+ {
+ if (msg[i].status != I2C_SUCCESSFUL)
+ {
+ return msg[i].status;
+ }
+ }
+ return I2C_SUCCESSFUL;
+}
+
+/* i2c_write --
+ * Send single message over specified I2C bus to addressed device and
+ * wait while transfer is finished.
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * addr - address of I2C device
+ * buf - data to be sent to device
+ * size - data buffer size
+ *
+ * RETURNS:
+ * transfer status
+ */
+i2c_message_status
+i2c_write(i2c_bus_number bus, i2c_address addr, void *buf, int size)
+{
+ i2c_message msg;
+ msg.addr = addr;
+ msg.flags = I2C_MSG_WR;
+ if (addr > 0xff)
+ msg.flags |= I2C_MSG_ADDR_10;
+ msg.status = 0;
+ msg.len = size;
+ msg.buf = buf;
+ return i2c_transfer_wait(bus, &msg, 1);
+}
+
+/* i2c_wrbyte --
+ * Send single one-byte long message over specified I2C bus to
+ * addressed device and wait while transfer is finished.
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * addr - address of I2C device
+ * cmd - byte message to be sent to device
+ *
+ * RETURNS:
+ * transfer status
+ */
+i2c_message_status
+i2c_wrbyte(i2c_bus_number bus, i2c_address addr, uint8_t cmd)
+{
+ i2c_message msg;
+ uint8_t data = cmd;
+ msg.addr = addr;
+ msg.flags = I2C_MSG_WR;
+ if (addr > 0xff)
+ msg.flags |= I2C_MSG_ADDR_10;
+ msg.status = 0;
+ msg.len = sizeof(data);
+ msg.buf = &data;
+ return i2c_transfer_wait(bus, &msg, 1);
+}
+
+/* i2c_read --
+ * receive single message over specified I2C bus from addressed device.
+ * This call will wait while transfer is finished.
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * addr - address of I2C device
+ * buf - buffer for received message
+ * size - receive buffer size
+ *
+ * RETURNS:
+ * transfer status
+ */
+i2c_message_status
+i2c_read(i2c_bus_number bus, i2c_address addr, void *buf, int size)
+{
+ i2c_message msg;
+ msg.addr = addr;
+ msg.flags = 0;
+ if (addr > 0xff)
+ msg.flags |= I2C_MSG_ADDR_10;
+ msg.status = 0;
+ msg.len = size;
+ msg.buf = buf;
+ return i2c_transfer_wait(bus, &msg, 1);
+}
+
+/* i2c_wrrd --
+ * Send message over I2C bus to specified device and receive message
+ * from the same device during single transfer.
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * addr - address of I2C device
+ * bufw - data to be sent to device
+ * sizew - send data buffer size
+ * bufr - buffer for received message
+ * sizer - receive buffer size
+ *
+ * RETURNS:
+ * transfer status
+ */
+i2c_message_status
+i2c_wrrd(i2c_bus_number bus, i2c_address addr, void *bufw, int sizew,
+ void *bufr, int sizer)
+{
+ i2c_message msg[2];
+ msg[0].addr = addr;
+ msg[0].flags = I2C_MSG_WR | I2C_MSG_ERRSKIP;
+ if (addr > 0xff)
+ msg[0].flags |= I2C_MSG_ADDR_10;
+ msg[0].status = 0;
+ msg[0].len = sizew;
+ msg[0].buf = bufw;
+
+ msg[1].addr = addr;
+ msg[1].flags = 0;
+ if (addr > 0xff)
+ msg[1].flags |= I2C_MSG_ADDR_10;
+ msg[1].status = 0;
+ msg[1].len = sizer;
+ msg[1].buf = bufr;
+
+ return i2c_transfer_wait(bus, msg, 2);
+}
+
+/* i2c_wbrd --
+ * Send one-byte message over I2C bus to specified device and receive
+ * message from the same device during single transfer.
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * addr - address of I2C device
+ * cmd - one-byte message to be sent over I2C bus
+ * bufr - buffer for received message
+ * sizer - receive buffer size
+ *
+ * RETURNS:
+ * transfer status
+ */
+i2c_message_status
+i2c_wbrd(i2c_bus_number bus, i2c_address addr, uint8_t cmd,
+ void *bufr, int sizer)
+{
+ i2c_message msg[2];
+ uint8_t bufw = cmd;
+ msg[0].addr = addr;
+ msg[0].flags = I2C_MSG_WR | I2C_MSG_ERRSKIP;
+ if (addr > 0xff)
+ msg[0].flags |= I2C_MSG_ADDR_10;
+ msg[0].status = 0;
+ msg[0].len = sizeof(bufw);
+ msg[0].buf = &bufw;
+
+ msg[1].addr = addr;
+ msg[1].flags = I2C_MSG_ERRSKIP;
+ if (addr > 0xff)
+ msg[1].flags |= I2C_MSG_ADDR_10;
+ msg[1].status = 0;
+ msg[1].len = sizer;
+ msg[1].buf = bufr;
+
+ return i2c_transfer_wait(bus, msg, 2);
+}
diff --git a/bsps/m68k/mcf5206elite/i2c/i2cdrv.c b/bsps/m68k/mcf5206elite/i2c/i2cdrv.c
new file mode 100644
index 0000000000..838b0b26b1
--- /dev/null
+++ b/bsps/m68k/mcf5206elite/i2c/i2cdrv.c
@@ -0,0 +1,262 @@
+/* I2C driver for MCF5206eLITE board. I2C bus accessed through on-chip
+ * MCF5206e MBUS controller.
+ *
+ * The purpose of this module is to perform I2C driver initialization
+ * and serialize I2C transfers.
+ *
+ * Copyright (C) 2000 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ *
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <bsp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "i2c.h"
+#include "i2cdrv.h"
+#include "mcf5206/mcfmbus.h"
+
+#ifndef I2C_NUMBER_OF_BUSES
+#define I2C_NUMBER_OF_BUSES (1)
+#endif
+
+#ifndef I2C_SELECT_BUS
+#define I2C_SELECT_BUS(bus)
+#endif
+
+/*
+ * Few I2C transfers may be posted simultaneously, but MBUS driver is able
+ * to process it one-by-one. To serialize transfers, function i2c_transfer
+ * put transfer information to the queue and initiate new transfers if MBUS
+ * driver is not busy. When driver is busy, next transfer is dequeued
+ * when current active transfer is finished.
+ */
+
+/*
+ * i2c_qel - I2C transfers queue element; contain information about
+ * delayed transfer
+ */
+typedef struct i2c_qel {
+ i2c_bus_number bus; /* I2C bus number */
+ i2c_message *msg; /* pointer to the transfer' messages array */
+ int nmsg; /* number of messages in transfer */
+ i2c_transfer_done done; /* transfer done callback function */
+ void * done_arg_ptr; /* arbitrary arg pointer to done callback */
+} i2c_qel;
+
+/* Memory for I2C transfer queue. This queue represented like a ring buffer */
+static i2c_qel *tqueue;
+
+/* Maximum number of elements in transfer queue */
+static int tqueue_size;
+
+/* Position of next free element in a ring buffer */
+static volatile int tqueue_head;
+
+/* Position of the first element in transfer queue */
+static volatile int tqueue_tail;
+
+/* MBus I2C bus controller busy flag */
+static volatile bool mbus_busy;
+
+/* MBus I2C bus controller descriptor */
+static mcfmbus mbus;
+
+/* Clock rate selected for each of bus */
+static int i2cdrv_bus_clock_div[I2C_NUMBER_OF_BUSES];
+
+/* Currently selected I2C bus clock rate */
+static int i2cdrv_bus_clock_div_current;
+
+/* Forward function declaration */
+static void i2cdrv_unload(void);
+
+/* i2cdrv_done --
+ * Callback function which is called from MBus low-level driver when
+ * transfer is finished.
+ */
+static void
+i2cdrv_done(void * arg_ptr)
+{
+ rtems_interrupt_level level;
+ i2c_qel *qel = tqueue + tqueue_tail;
+ qel->done(qel->done_arg_ptr);
+ rtems_interrupt_disable(level);
+ tqueue_tail = (tqueue_tail + 1) % tqueue_size;
+ mbus_busy = false;
+ rtems_interrupt_enable(level);
+ i2cdrv_unload();
+}
+
+/* i2cdrv_unload --
+ * If MBUS controller is not busy and transfer waiting in a queue,
+ * initiate processing of the next transfer in queue.
+ */
+static void
+i2cdrv_unload(void)
+{
+ rtems_interrupt_level level;
+ i2c_qel *qel;
+ rtems_status_code sc;
+ rtems_interrupt_disable(level);
+ if (!mbus_busy && (tqueue_head != tqueue_tail))
+ {
+ mbus_busy = true;
+ rtems_interrupt_enable(level);
+ qel = tqueue + tqueue_tail;
+
+ I2C_SELECT_BUS(qel->bus);
+ if (i2cdrv_bus_clock_div[qel->bus] != i2cdrv_bus_clock_div_current)
+ {
+ i2cdrv_bus_clock_div_current = i2cdrv_bus_clock_div[qel->bus];
+ mcfmbus_select_clock_divider(&mbus, i2cdrv_bus_clock_div_current);
+ }
+ sc = mcfmbus_i2c_transfer(&mbus, qel->nmsg, qel->msg,
+ i2cdrv_done,qel);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ int i;
+ for (i = 0; i < qel->nmsg; i++)
+ {
+ qel->msg[i].status = I2C_RESOURCE_NOT_AVAILABLE;
+ }
+ i2cdrv_done(qel);
+ }
+ }
+ else
+ {
+ rtems_interrupt_enable(level);
+ }
+}
+
+/* i2c_transfer --
+ * Initiate multiple-messages transfer over specified I2C bus or
+ * put request into queue if bus or some other resource is busy. (This
+ * is non-blocking function).
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * nmsg - number of messages
+ * msg - pointer to messages array
+ * done - function which is called when transfer is finished
+ * done_arg_ptr - arbitrary argument pointer passed to done funciton
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if transfer initiated successfully, or error
+ * code if something failed.
+ */
+rtems_status_code
+i2c_transfer(i2c_bus_number bus, int nmsg, i2c_message *msg,
+ i2c_transfer_done done, void * done_arg_ptr)
+{
+ i2c_qel qel;
+ rtems_interrupt_level level;
+
+ if (bus >= I2C_NUMBER_OF_BUSES)
+ {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ if (msg == NULL)
+ {
+ return RTEMS_INVALID_ADDRESS;
+ }
+
+ qel.bus = bus;
+ qel.msg = msg;
+ qel.nmsg = nmsg;
+ qel.done = done;
+ qel.done_arg_ptr = done_arg_ptr;
+ rtems_interrupt_disable(level);
+ if ((tqueue_head + 1) % tqueue_size == tqueue_tail)
+ {
+ rtems_interrupt_enable(level);
+ return RTEMS_TOO_MANY;
+ }
+ memcpy(tqueue + tqueue_head, &qel, sizeof(qel));
+ tqueue_head = (tqueue_head + 1) % tqueue_size;
+ rtems_interrupt_enable(level);
+ i2cdrv_unload();
+ return RTEMS_SUCCESSFUL;
+}
+
+/* i2cdrv_initialize --
+ * I2C driver initialization (rtems I/O driver primitive)
+ */
+rtems_device_driver
+i2cdrv_initialize(rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg)
+{
+ int i;
+ rtems_status_code sc;
+ mbus_busy = false;
+ tqueue_tail = tqueue_head = 0;
+ tqueue_size = 32;
+ tqueue = calloc(tqueue_size, sizeof(i2c_qel));
+
+ sc = mcfmbus_initialize(&mbus, MBAR);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ for (i = 0; i < I2C_NUMBER_OF_BUSES; i++)
+ {
+ sc = i2c_select_clock_rate(i, 4096);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+ }
+ i2cdrv_bus_clock_div_current = -1;
+ return RTEMS_SUCCESSFUL;
+}
+
+/* i2c_select_clock_rate --
+ * select I2C bus clock rate for specified bus. Some bus controller do not
+ * allow to select arbitrary clock rate; in this case nearest possible
+ * slower clock rate is selected.
+ *
+ * PARAMETERS:
+ * bus - I2C bus number
+ * bps - data transfer rate for this bytes in bits per second
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL, if operation performed successfully,
+ * RTEMS_INVALID_NUMBER, if wrong bus number is specified,
+ * RTEMS_UNSATISFIED, if bus do not support data transfer rate selection
+ * or specified data transfer rate could not be used.
+ */
+rtems_status_code
+i2c_select_clock_rate(i2c_bus_number bus, int bps)
+{
+ int div;
+ if (bus >= I2C_NUMBER_OF_BUSES)
+ return RTEMS_INVALID_NUMBER;
+
+ if (bps == 0)
+ return RTEMS_UNSATISFIED;
+
+ div = BSP_SYSTEM_FREQUENCY / bps;
+ i2cdrv_bus_clock_div[bus] = div;
+ return RTEMS_SUCCESSFUL;
+}
+
+/* i2c_poll --
+ * Poll I2C bus controller for events and hanle it. This function is
+ * used when I2C driver operates in poll-driven mode.
+ *
+ * PARAMETERS:
+ * bus - bus number to be polled
+ *
+ * RETURNS:
+ * none
+ */
+void
+i2c_poll(i2c_bus_number bus)
+{
+ mcfmbus_poll(&mbus);
+}