lm3s1968: generic KFile-compliant SSI bus implementation.
authorarighi <arighi@38d2e660-2303-0410-9eaa-f027e97ec537>
Fri, 9 Apr 2010 14:27:31 +0000 (14:27 +0000)
committerarighi <arighi@38d2e660-2303-0410-9eaa-f027e97ec537>
Fri, 9 Apr 2010 14:27:31 +0000 (14:27 +0000)
git-svn-id: https://src.develer.com/svnoss/bertos/trunk@3405 38d2e660-2303-0410-9eaa-f027e97ec537

bertos/cpu/cortex-m3/drv/ssi_lm3s.c
bertos/cpu/cortex-m3/drv/ssi_lm3s.h

index f67f1a1cbc37cb28c73ac16ef33ba23e577f2088..965683b2270028f2cd808287d7496d88d09ce54b 100644 (file)
 
 #include <cfg/compiler.h>
 #include <cfg/debug.h>
-#include "io/lm3s.h"
-#include "drv/ssi_lm3s.h"
+#include <string.h> /* memset() */
+#include "ssi_lm3s.h"
 
 /* SSI clocking informations (CPSDVSR + SCR) */
-struct ssi_clock
+struct SSIClock
 {
        unsigned int cpsdvsr;
        unsigned int scr;
@@ -50,10 +50,10 @@ struct ssi_clock
 /*
  * Evaluate the SSI clock prescale (SSICPSR) and SSI serial clock rate (SCR).
  */
-INLINE struct ssi_clock
-lm3s_ssi_prescale(unsigned int bitrate)
+INLINE struct SSIClock
+lm3s_ssiPrescale(unsigned int bitrate)
 {
-       struct ssi_clock ret;
+       struct SSIClock ret;
 
        for (ret.cpsdvsr = 2, ret.scr = CPU_FREQ / bitrate / ret.cpsdvsr - 1;
                        ret.scr > 255; ret.cpsdvsr += 2);
@@ -65,133 +65,141 @@ lm3s_ssi_prescale(unsigned int bitrate)
 /*
  * Initialize the SSI interface.
  *
- * @base: the SSI port base address.
- * @frame: the data transfer protocol (SSI_FRF_MOTO_MODE_0,
- * SSI_FRF_MOTO_MODE_1, SSI_FRF_MOTO_MODE_2, SSI_FRF_MOTO_MODE_3, SSI_FRF_TI or
- * SSI_FRF_NMW)
- * @mode: the mode of operation (SSI_MODE_MASTER, SSI_MODE_SLAVE,
- * SSI_MODE_SLAVE_OD)
- * @bitrate: the SSI clock rate
- * @data_width: number of bits per frame
- *
  * Return 0 in case of success, a negative value otherwise.
  */
-int lm3s_ssi_init(uint32_t base, uint32_t frame, int mode,
-                   unsigned int bitrate, unsigned int data_width)
+int lm3s_ssiOpen(uint32_t addr, uint32_t frame, int mode,
+                       int bitrate, uint32_t data_width)
 {
-       struct ssi_clock ssi_clock;
+       struct SSIClock ssi_clock;
 
-       ASSERT(base == SSI0_BASE || base == SSI1_BASE);
+       ASSERT(addr == SSI0_BASE || addr == SSI1_BASE);
        /* Configure the SSI operating mode */
        switch (mode)
        {
                /* SSI Slave Mode Output Disable */
                case SSI_MODE_SLAVE_OD:
-                       HWREG(base + SSI_O_CR1) = SSI_CR1_SOD;
+                       HWREG(addr + SSI_O_CR1) = SSI_CR1_SOD;
                        break;
                /* SSI Slave */
                case SSI_MODE_SLAVE:
-                       HWREG(base + SSI_O_CR1) = SSI_CR1_MS;
+                       HWREG(addr + SSI_O_CR1) = SSI_CR1_MS;
                        break;
                /* SSI Master */
                case SSI_MODE_MASTER:
-                       HWREG(base + SSI_O_CR1) = 0;
+                       HWREG(addr + SSI_O_CR1) = 0;
                        break;
                default:
                        ASSERT(0);
                        return -1;
        }
        /* Configure the peripheral clock and frame format */
-       ssi_clock = lm3s_ssi_prescale(bitrate);
-       HWREG(base + SSI_O_CPSR) = ssi_clock.cpsdvsr;
-       HWREG(base + SSI_O_CR0) =
-                       (ssi_clock.scr << 8)    |
-                       ((frame & 3) << 6)      |
-                       (frame & SSI_CR0_FRF_M) |
+       ssi_clock = lm3s_ssiPrescale(bitrate);
+       HWREG(addr + SSI_O_CPSR) = ssi_clock.cpsdvsr;
+       HWREG(addr + SSI_O_CR0) =
+                       (ssi_clock.scr << 8)            |
+                       ((frame & 3) << 6)              |
+                       (frame & SSI_CR0_FRF_M)         |
                        (data_width - 1);
-       return 0;
-}
+       /* Enable the SSI interface */
+       HWREG(addr + SSI_O_CR1) |= SSI_CR1_SSE;
 
-/* Enable the SSI interface */
-void lm3s_ssi_enable(uint32_t base)
-{
-       HWREG(base + SSI_O_CR1) |= SSI_CR1_SSE;
-}
-
-/* Disable the SSI interface */
-void lm3s_ssi_disable(uint32_t base)
-{
-       HWREG(base + SSI_O_CR1) &= ~SSI_CR1_SSE;
+       return 0;
 }
 
 /*
- * Put a frame into the SSI transmit FIFO.
+ * Write data to the SSI bus.
  *
- * NOTE: the upper bits of the frame will be automatically discarded by the
- * hardware according to the frame data width, configured by lm3s_ssi_init().
+ * Return the number of bytes written to the bus.
  */
-void lm3s_ssi_write_frame(uint32_t base, uint32_t val)
+static size_t lm3s_ssiWrite(struct KFile *fd, const void *buf, size_t size)
 {
-       /* Wait for available space in the TX FIFO */
-       while (!(HWREG(base + SSI_O_SR) & SSI_SR_TNF))
-               cpu_relax();
-       /* Enqueue data to the TX FIFO */
-       HWREG(base + SSI_O_DR) = val;
+       LM3SSSI *fds = LM3SSSI_CAST(fd);
+       const char *p = (const char *)buf;
+       uint32_t frame;
+       size_t count = 0;
+
+       while (count < size)
+       {
+               frame = p[count];
+               if (fds->flags & LM3S_SSI_NONBLOCK)
+               {
+                       if (!lm3s_ssiWriteFrameNonBlocking(fds->addr,
+                                                               frame))
+                               break;
+               }
+               else
+                       lm3s_ssiWriteFrame(fds->addr, frame);
+               count++;
+       }
+       return count;
 }
 
 /*
- * Put a frame into the SSI transmit FIFO without blocking.
- *
- * NOTE: the upper bits of the frame will be automatically discarded by the
- * hardware according to the frame data width, configured by lm3s_ssi_init().
+ * Read data from the SSI bus.
  *
- * Return the number of frames written to the TX FIFO.
+ * Return the number of bytes read from the bus.
  */
-int lm3s_ssi_write_frame_nonblocking(uint32_t base, uint32_t val)
+static size_t lm3s_ssiRead(struct KFile *fd, void *buf, size_t size)
 {
-       /* Check for available space in the TX FIFO */
-       if (!(HWREG(base + SSI_O_SR) & SSI_SR_TNF))
-               return 0;
-       /* Enqueue data to the TX FIFO */
-       HWREG(base + SSI_O_DR) = val;
-       return 1;
+       LM3SSSI *fds = LM3SSSI_CAST(fd);
+
+       uint8_t *p = (uint8_t *)buf;
+       uint32_t frame;
+       size_t count = 0;
+
+       while (count < size)
+       {
+               if (fds->flags & LM3S_SSI_NONBLOCK)
+               {
+                       if (!lm3s_ssiReadFrameNonBlocking(fds->addr, &frame))
+                               break;
+               }
+               else
+                       lm3s_ssiReadFrame(fds->addr, &frame);
+               *p++ = (uint8_t)frame;
+               count++;
+       }
+       return count;
 }
 
-/*
- * Get a frame from the SSI receive FIFO.
- */
-void lm3s_ssi_read_frame(uint32_t base, uint32_t *val)
+
+/* Wait for data in the TX FIFO being actually transmitted */
+static int lm3s_ssiFlush(struct KFile *fd)
 {
-       /* Wait for data available in the RX FIFO */
-       while (!(HWREG(base + SSI_O_SR) & SSI_SR_RNE))
+       LM3SSSI *fds = LM3SSSI_CAST(fd);
+
+       while (!lm3s_ssiTxDone(fds->addr))
                cpu_relax();
-       /* Read data from SSI RX FIFO */
-       *val = HWREG(base + SSI_O_DR);
+       return 0;
 }
 
-/*
- * Get a frame into the SSI receive FIFO without blocking.
- *
- * Return the number of frames read from the RX FIFO.
- */
-int lm3s_ssi_read_frame_nonblocking(uint32_t base, uint32_t *val)
+/* Disable the SSI interface */
+static int lm3s_ssiClose(struct KFile *fd)
 {
-       /* Check for data available in the RX FIFO */
-       if (!(HWREG(base + SSI_O_SR) & SSI_SR_RNE))
-               return 0;
-       /* Read data from SSI RX FIFO */
-       *val = HWREG(base + SSI_O_DR);
-       return 1;
+       LM3SSSI *fds = LM3SSSI_CAST(fd);
+
+       lm3s_ssiFlush(fd);
+       HWREG(fds->addr + SSI_O_CR1) &= ~SSI_CR1_SSE;
+       return 0;
 }
 
-/*
- * Check if the SSI transmitter is busy or not
- *
- * This allows to determine whether the TX FIFO have been cleared by the
- * hardware, so the transmission can be safely considered completed.
+/**
+ * Initialize a LM3S SSI driver.
  */
-bool lm3s_ssi_txdone(uint32_t base)
+void lm3s_ssiInit(struct LM3SSSI *fds, uint32_t addr, uint32_t frame, int mode,
+                       int bitrate, uint32_t data_width)
 {
-       /* Check if the SSI is busy */
-       return (HWREG(base + SSI_O_SR) & SSI_SR_BSY) ? true : false;
+       memset(fds, 0, sizeof(*fds));
+       DB(fds->fd._type = KFT_LM3SSSI);
+
+       /* TODO: only 8-bit frame size is supported */
+       ASSERT(data_width == 8);
+
+       fds->fd.write = lm3s_ssiWrite;
+       fds->fd.read = lm3s_ssiRead;
+       fds->fd.close = lm3s_ssiClose;
+       fds->fd.flush = lm3s_ssiFlush;
+
+       fds->addr = addr;
+       lm3s_ssiOpen(addr, frame, mode, bitrate, data_width);
 }
index 901f62885bb6e06bc8059df0c97123d6c23d7cf7..1bbceee36e2f129793ae5575e4cbe35f0fc5c9c7 100644 (file)
@@ -37,6 +37,8 @@
 #define SSI_LM3S_H
 
 #include <cpu/power.h> /* cpu_relax() */
+#include <kern/kfile.h> /* KFile */
+#include <io/lm3s.h>
 
 /**
  * LM3S1968 SSI frame format
 #define SSI_MODE_SLAVE_OD       0x00000002  //< SSI slave with output disabled
 /*\}*/
 
-int lm3s_ssi_init(uint32_t base, uint32_t frame, int mode,
-                   unsigned int bitrate, unsigned int data_width);
-void lm3s_ssi_enable(uint32_t base);
-void lm3s_ssi_disable(uint32_t base);
-void lm3s_ssi_write_frame(uint32_t base, uint32_t val);
-int lm3s_ssi_write_frame_nonblocking(uint32_t base, uint32_t val);
-void lm3s_ssi_read_frame(uint32_t base, uint32_t *val);
-int lm3s_ssi_read_frame_nonblocking(uint32_t base, uint32_t *val);
-bool lm3s_ssi_txdone(uint32_t base);
-
-INLINE void lm3s_ssi_wait_txdone(uint32_t base)
+/* LM3S SSI handle properties */
+enum
 {
-       while (!lm3s_ssi_txdone(base))
+       /* Non-blocking I/O */
+       LM3S_SSI_NONBLOCK = 1,
+};
+
+/** LM3S1968 SSI handle structure */
+typedef struct LM3SSSI
+{
+       /* SSI Kfile structure */
+       KFile fd;
+
+       /* Handle properties */
+       uint32_t flags;
+
+       /* SSI port address */
+       uint32_t addr;
+} LM3SSSI;
+
+/**
+ * ID for LM3S SSI.
+ */
+#define KFT_LM3SSSI MAKE_ID('L', 'S', 'S', 'I')
+
+INLINE LM3SSSI *LM3SSSI_CAST(KFile *fd)
+{
+       ASSERT(fd->_type == KFT_LM3SSSI);
+       return (LM3SSSI *)fd;
+}
+
+/* KFile interface to LM3S SSI */
+void lm3s_ssiInit(struct LM3SSSI *fds, uint32_t addr, uint32_t frame, int mode,
+                       int bitrate, uint32_t data_width);
+
+/* Raw interface to LM3S SSI */
+int lm3s_ssiOpen(uint32_t addr, uint32_t frame, int mode,
+                       int bitrate, uint32_t data_width);
+
+/*
+ * Check if the SSI transmitter is busy or not
+ *
+ * This allows to determine whether the TX FIFO have been cleared by the
+ * hardware, so the transmission can be safely considered completed.
+ */
+INLINE bool lm3s_ssiTxDone(uint32_t base)
+{
+       return (HWREG(base + SSI_O_SR) & SSI_SR_BSY) ? true : false;
+}
+
+/*
+ * Check if the SSI TX FIFO is full
+ */
+INLINE bool lm3s_ssiTxReady(uint32_t base)
+{
+       return (HWREG(base + SSI_O_SR) & SSI_SR_TNF) ? true : false;
+}
+
+/*
+ * Check for data available in the RX FIFO
+ */
+INLINE bool lm3s_ssiRxReady(uint32_t base)
+{
+       return (HWREG(base + SSI_O_SR) & SSI_SR_RNE) ? true : false;
+}
+
+/*
+ * Get a frame into the SSI receive FIFO without blocking.
+ *
+ * Return the number of frames read from the RX FIFO.
+ */
+INLINE int lm3s_ssiReadFrameNonBlocking(uint32_t base, uint32_t *val)
+{
+       /* Check for data available in the RX FIFO */
+       if (!lm3s_ssiRxReady(base))
+               return 0;
+       /* Read data from SSI RX FIFO */
+       *val = HWREG(base + SSI_O_DR);
+       return 1;
+}
+
+/*
+ * Get a frame from the SSI receive FIFO.
+ */
+INLINE void lm3s_ssiReadFrame(uint32_t base, uint32_t *val)
+{
+       /* Wait for data available in the RX FIFO */
+       while (!lm3s_ssiRxReady(base))
+               cpu_relax();
+       /* Read data from SSI RX FIFO */
+       *val = HWREG(base + SSI_O_DR);
+}
+
+/*
+ * Put a frame into the SSI transmit FIFO without blocking.
+ *
+ * NOTE: the upper bits of the frame will be automatically discarded by the
+ * hardware according to the frame data width.
+ *
+ * Return the number of frames written to the TX FIFO.
+ */
+INLINE int lm3s_ssiWriteFrameNonBlocking(uint32_t base, uint32_t val)
+{
+       /* Check for available space in the TX FIFO */
+       if (!lm3s_ssiTxReady(base))
+               return 0;
+       /* Enqueue data to the TX FIFO */
+       HWREG(base + SSI_O_DR) = val;
+       return 1;
+}
+
+/*
+ * Put a frame into the SSI transmit FIFO.
+ *
+ * NOTE: the upper bits of the frame will be automatically discarded by the
+ * hardware according to the frame data width.
+ */
+INLINE void lm3s_ssiWriteFrame(uint32_t base, uint32_t val)
+{
+       /* Wait for available space in the TX FIFO */
+       while (!lm3s_ssiTxReady(base))
                cpu_relax();
+       /* Enqueue data to the TX FIFO */
+       HWREG(base + SSI_O_DR) = val;
 }
 
 #endif /* SSI_LM3S_H */