#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;
/*
* 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);
/*
* 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);
}
#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 */