altos: Add LPC spi driver
authorKeith Packard <keithp@keithp.com>
Sat, 18 May 2013 10:13:17 +0000 (03:13 -0700)
committerKeith Packard <keithp@keithp.com>
Sat, 18 May 2013 10:13:17 +0000 (03:13 -0700)
Signed-off-by: Keith Packard <keithp@keithp.com>
src/lpc/ao_arch.h
src/lpc/ao_arch_funcs.h
src/lpc/ao_spi_lpc.c [new file with mode: 0644]

index 99c646f9361018a3f55cee9713caf7c8e2e38e53..d9f72e1a1c75a5345d5a28389f121fab4dc723bd 100644 (file)
@@ -28,6 +28,8 @@
 
 #define AO_LED_TYPE    uint16_t
 
+#define AO_PORT_TYPE   uint32_t
+
 #ifndef AO_TICK_TYPE
 #define AO_TICK_TYPE   uint16_t
 #define AO_TICK_SIGNED int16_t
@@ -123,4 +125,17 @@ ao_adc_init(void);
 void
 ao_serial_init(void);
 
+/* SPI definitions */
+
+#define AO_SPI_SPEED_12MHz             2
+#define AO_SPI_SPEED_6MHz              4
+#define AO_SPI_SPEED_4MHz              6
+#define AO_SPI_SPEED_2MHz              12
+#define AO_SPI_SPEED_1MHz              24
+#define AO_SPI_SPEED_500kHz            48
+#define AO_SPI_SPEED_250kHz            96
+#define AO_SPI_SPEED_125kHz            192
+
+#define AO_SPI_SPEED_FAST      AO_SPI_SPEED_12MHz
+
 #endif /* _AO_ARCH_H_ */
index 96ae0366f85bee03eb9caa0a5bf8718ade483975..94d876f6676f9eb4084849ed3df81028183651fa 100644 (file)
@@ -144,4 +144,55 @@ static inline void ao_arch_restore_stack(void) {
                ao_arch_release_interrupts();                   \
        } while (0)
 
+/*
+ * SPI
+ */
+
+#define ao_spi_set_cs(port,mask) (lpc_gpio.clr[port] = (mask))
+#define ao_spi_clr_cs(port,mask) (lpc_gpio.set[port] = (mask))
+
+#define ao_spi_get_mask(port,mask,bus,speed) do {      \
+               ao_spi_get(bus, speed);                 \
+               ao_spi_set_cs(port, mask);              \
+       } while (0)
+
+#define ao_spi_put_mask(reg,mask,bus) do {     \
+               ao_spi_clr_cs(reg,mask);        \
+               ao_spi_put(bus);                \
+       } while (0)
+
+#define ao_spi_get_bit(reg,bit,pin,bus,speed) ao_spi_get_mask(reg,(1<<bit),bus,speed)
+#define ao_spi_put_bit(reg,bit,pin,bus) ao_spi_put_mask(reg,(1<<bit),bus)
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed);
+
+void
+ao_spi_put(uint8_t spi_index);
+
+void
+ao_spi_send(void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index);
+
+extern uint16_t        ao_spi_speed[LPC_NUM_SPI];
+
+void
+ao_spi_init(void);
+
+#define ao_spi_init_cs(port, mask) do {                                        \
+               uint8_t __bit__;                                        \
+               for (__bit__ = 0; __bit__ < 32; __bit__++) {            \
+                       if (mask & (1 << __bit__))                      \
+                               ao_enable_output(port, __bit__, PIN, 1); \
+               }                                                       \
+       } while (0)
+
 #endif /* _AO_ARCH_FUNCS_H_ */
diff --git a/src/lpc/ao_spi_lpc.c b/src/lpc/ao_spi_lpc.c
new file mode 100644 (file)
index 0000000..05688f5
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+static uint8_t         ao_spi_mutex[LPC_NUM_SPI];
+
+static struct lpc_ssp * const ao_lpc_ssp[LPC_NUM_SPI] = { &lpc_ssp0, &lpc_ssp1 };
+
+static uint8_t spi_dev_null;
+
+#define spi_loop(len, put, get) do {                                   \
+               while (len--) {                                         \
+                       /* Wait for space in the fifo */                \
+                       while ((lpc_ssp->sr & (1 << LPC_SSP_SR_TNF)) == 0) \
+                               ;                                       \
+                       /* send a byte */                               \
+                       lpc_ssp->dr = put;                              \
+                                                                       \
+                       /* recv a byte */                               \
+                       get lpc_ssp->dr;                                \
+               }                                                       \
+                                                                       \
+               /* Wait for the fifo to drain */                        \
+               while ((lpc_ssp->sr & (1 << LPC_SSP_SR_BSY)))           \
+                       ;                                               \
+       } while (0);
+
+void
+ao_spi_send(void *block, uint16_t len, uint8_t id)
+{
+       uint8_t *b = block;
+       struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+
+       spi_loop(len, *b++, (void));
+}
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t id)
+{
+       struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+
+       spi_loop(len, value, (void));
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t id)
+{
+       uint8_t *b = block;
+       struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+
+       spi_loop(len, 0xff, *b++ =);
+}
+
+void
+ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t id)
+{
+       uint8_t *o = out;
+       uint8_t *i = in;
+       struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+
+       spi_loop(len, *o++, *i++ =);
+}
+
+void
+ao_spi_get(uint8_t id, uint32_t speed)
+{
+       struct lpc_ssp  *lpc_ssp = ao_lpc_ssp[id];
+
+       ao_mutex_get(&ao_spi_mutex[id]);
+       
+       /* Set the clock prescale */
+       lpc_ssp->cpsr = speed;
+
+       /* Enable the device */
+       lpc_ssp->cr1 = ((0 << LPC_SSP_CR1_LBM) |
+                       (1 << LPC_SSP_CR1_SSE) |
+                       (LPC_SSP_CR1_MS_MASTER << LPC_SSP_CR1_MS) |
+                       (0 << LPC_SSP_CR1_SOD));
+}
+
+void
+ao_spi_put(uint8_t id)
+{
+       struct lpc_ssp  *lpc_ssp = ao_lpc_ssp[id];
+
+       /* Disable the device */
+       lpc_ssp->cr1 = ((0 << LPC_SSP_CR1_LBM) |
+                       (0 << LPC_SSP_CR1_SSE) |
+                       (LPC_SSP_CR1_MS_MASTER << LPC_SSP_CR1_MS) |
+                       (0 << LPC_SSP_CR1_SOD));
+       ao_mutex_put(&ao_spi_mutex[id]);
+}
+
+static void
+ao_spi_channel_init(uint8_t id)
+{
+       struct lpc_ssp  *lpc_ssp = ao_lpc_ssp[id];
+       uint8_t d;
+
+       lpc_ssp->cr0 = ((LPC_SSP_CR0_DSS_8 << LPC_SSP_CR0_DSS) |
+                       (LPC_SSP_CR0_FRF_SPI << LPC_SSP_CR0_FRF) |
+                       (0 << LPC_SSP_CR0_CPOL) |
+                       (0 << LPC_SSP_CR0_CPHA) |
+                       (0 << LPC_SSP_CR0_SCR));
+       /* Drain the receive fifo */
+       for (d = 0; d < LPC_SSP_FIFOSIZE; d++)
+               (void) lpc_ssp->dr;
+}
+
+void
+ao_spi_init(void)
+{
+#if HAS_SPI_0
+       /* Configure pins */
+       lpc_ioconf.pio0_6 = ao_lpc_alternate(LPC_IOCONF_FUNC_SCK0);
+       lpc_ioconf.pio0_8 = ao_lpc_alternate(LPC_IOCONF_FUNC_MISO0);
+       lpc_ioconf.pio0_9 = ao_lpc_alternate(LPC_IOCONF_FUNC_MOSI0);
+
+       /* Enable the device */
+       lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP0);
+
+       /* Turn on the clock */
+       lpc_scb.ssp0clkdiv = 1;
+
+       /* Reset the device */
+       lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
+       lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
+       ao_spi_channel_init(0);
+#endif                    
+
+#if HAS_SPI_1
+
+#if SPI_SCK1_P1_15
+       lpc_ioconf.pio1_15 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_15_SCK1);
+#define HAS_SCK1
+#endif
+#if SPI_SCK1_P1_20
+       lpc_ioconf.pio1_20 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_20_SCK1);
+#define HAS_SCK1
+#endif
+#ifndef HAS_SCK1
+#error "No pin specified for SCK1"
+#endif
+
+#if SPI_MISO1_P0_22
+       lpc_ioconf.pio0_22 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_22_MISO1);
+#define HAS_MISO1
+#endif
+#if SPI_MISO1_P1_21
+       lpc_ioconf.pio1_21 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_21_MISO1);
+#define HAS_MISO1
+#endif
+#ifndef HAS_MISO1
+#error "No pin specified for MISO1"
+#endif
+
+#if SPI_MOSI1_P0_21
+       lpc_ioconf.pio1_21 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO0_21_MOSI1);
+#define HAS_MOSI1
+#endif
+#if SPI_MOSI1_P1_22
+       lpc_ioconf.pio1_22 = ao_lpc_alternate(LPC_IOCONF_FUNC_PIO1_22_MOSI1);
+#define HAS_MOSI1
+#endif
+#ifndef HAS_MOSI1
+#error "No pin specified for MOSI1"
+#endif
+               
+       /* Enable the device */
+       lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP1);
+
+       /* Turn on the clock */
+       lpc_scb.ssp1clkdiv = 1;
+
+       /* De-assert reset */
+       lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP1_RST_N);
+       ao_spi_channel_init(1);
+#endif /* HAS_SPI_1 */
+}