aboutsummaryrefslogtreecommitdiffstats
path: root/src/sam3/spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sam3/spi.c')
-rw-r--r--src/sam3/spi.c227
1 files changed, 189 insertions, 38 deletions
diff --git a/src/sam3/spi.c b/src/sam3/spi.c
index a92a766b..0c96baca 100644
--- a/src/sam3/spi.c
+++ b/src/sam3/spi.c
@@ -1,6 +1,8 @@
// SPI transmissions on sam3
//
// Copyright (C) 2018 Petri Honkala <cruwaller@gmail.com>
+// Copyright (C) 2018 Florian Heilmann <Florian.Heilmann@gmx.net>
+// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@@ -9,54 +11,88 @@
#include "internal.h" // gpio_peripheral
#include "sched.h" // sched_shutdown
-#define REGPTR SPI0
-#define PERIPH_ID ID_SPI0
-#define CHANNEL 0 // Use same channel for all
+/****************************************************************
+ * SPI/USART buses and pins
+ ****************************************************************/
+
+struct spi_info {
+ void *dev;
+ uint32_t dev_id;
+ uint8_t miso_pin, mosi_pin, sck_pin, rxtx_periph, sck_periph;
+};
+
+static const struct spi_info spi_bus[] = {
+#if CONFIG_MACH_SAM3X8E
+ { SPI0, ID_SPI0, GPIO('A', 25), GPIO('A', 26), GPIO('A', 27), 'A', 'A' },
+#elif CONFIG_MACH_SAM4E8E
+ { USART0, ID_USART0, GPIO('B', 0), GPIO('B', 1), GPIO('B', 13), 'C', 'C' },
+ { USART1, ID_USART1, GPIO('A', 21), GPIO('A', 22), GPIO('A', 23), 'A', 'A'},
+#endif
+};
+
+static int
+is_spihw(void *dev)
+{
+#if CONFIG_MACH_SAM3X8E
+ return dev == SPI0;
+#else
+ return dev == SPI;
+#endif
+}
static void
-spi_init(void)
+init_pins(uint32_t bus)
{
- /* Configure SCK, MISO and MOSI */
- gpio_peripheral(GPIO('A', 25), 'A', 0); // Arduino 74
- gpio_peripheral(GPIO('A', 26), 'A', 0); // Arduino 75
- gpio_peripheral(GPIO('A', 27), 'A', 0); // Arduino 76
+ const struct spi_info *si = &spi_bus[bus];
+ gpio_peripheral(si->sck_pin, si->sck_periph, 0);
+ gpio_peripheral(si->miso_pin, si->rxtx_periph, 1);
+ gpio_peripheral(si->mosi_pin, si->rxtx_periph, 0);
+ enable_pclock(si->dev_id);
+}
- // Enable SPI clocks
- enable_pclock(PERIPH_ID);
+
+/****************************************************************
+ * SPI hardware
+ ****************************************************************/
+
+#define CHANNEL 0 // Use same channel for all
+
+static void
+spihw_init(uint32_t bus)
+{
+ init_pins(bus);
+ Spi *pSpi = spi_bus[bus].dev;
/* Disable SPI */
- REGPTR->SPI_CR = SPI_CR_SPIDIS;
+ pSpi->SPI_CR = SPI_CR_SPIDIS;
/* Execute a software reset of the SPI twice */
- REGPTR->SPI_CR = SPI_CR_SWRST;
- REGPTR->SPI_CR = SPI_CR_SWRST;
+ pSpi->SPI_CR = SPI_CR_SWRST;
+ pSpi->SPI_CR = SPI_CR_SWRST;
- REGPTR->SPI_MR = ( SPI_MR_MSTR | // Set master mode
- SPI_MR_MODFDIS | // Disable fault detection
- SPI_MR_PCS(CHANNEL) // Fixes peripheral select
- );
- REGPTR->SPI_IDR = 0xffffffff; // Disable ISRs
+ pSpi->SPI_MR = ( SPI_MR_MSTR | // Set master mode
+ SPI_MR_MODFDIS | // Disable fault detection
+ SPI_MR_PCS(CHANNEL) // Fixes peripheral select
+ );
+ pSpi->SPI_IDR = 0xffffffff; // Disable ISRs
/* Clear Chip Select Registers */
- REGPTR->SPI_CSR[0] = 0;
- REGPTR->SPI_CSR[1] = 0;
- REGPTR->SPI_CSR[2] = 0;
- REGPTR->SPI_CSR[3] = 0;
+ pSpi->SPI_CSR[0] = 0;
+ pSpi->SPI_CSR[1] = 0;
+ pSpi->SPI_CSR[2] = 0;
+ pSpi->SPI_CSR[3] = 0;
/* Set basic channel config */
- REGPTR->SPI_CSR[CHANNEL] = 0;
+ pSpi->SPI_CSR[CHANNEL] = 0;
/* Enable SPI */
- REGPTR->SPI_CR = SPI_CR_SPIEN;
+ pSpi->SPI_CR = SPI_CR_SPIEN;
}
-struct spi_config
-spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
+static struct spi_config
+spihw_setup(uint32_t bus, uint8_t mode, uint32_t rate)
{
- if (bus != CHANNEL)
- shutdown("Invalid spi_setup parameters");
-
// Make sure bus is enabled
- spi_init();
+ spihw_init(bus);
uint32_t config = 0;
uint32_t clockDiv;
@@ -90,20 +126,21 @@ spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
};
config |= ((clockDiv & 0xffu) << SPI_CSR_SCBR_Pos);
- return (struct spi_config){.cfg = config};
+ return (struct spi_config){ .spidev = spi_bus[bus].dev, .cfg = config };
}
-void
-spi_prepare(struct spi_config config)
+static void
+spihw_prepare(struct spi_config config)
{
- REGPTR->SPI_CSR[CHANNEL] = config.cfg;
+ Spi *pSpi = config.spidev;
+ pSpi->SPI_CSR[CHANNEL] = config.cfg;
}
-void
-spi_transfer(struct spi_config config, uint8_t receive_data
- , uint8_t len, uint8_t *data)
+static void
+spihw_transfer(struct spi_config config, uint8_t receive_data
+ , uint8_t len, uint8_t *data)
{
- Spi* const pSpi = REGPTR;
+ Spi *pSpi = config.spidev;
if (receive_data) {
while (len--) {
pSpi->SPI_TDR = *data;
@@ -124,3 +161,117 @@ spi_transfer(struct spi_config config, uint8_t receive_data
}
}
}
+
+
+/****************************************************************
+ * USART hardware
+ ****************************************************************/
+
+static struct spi_config
+usart_setup(uint32_t bus, uint8_t mode, uint32_t rate)
+{
+ init_pins(bus);
+ Usart *p_usart = spi_bus[bus].dev;
+
+ p_usart->US_MR = 0;
+ p_usart->US_RTOR = 0;
+ p_usart->US_TTGR = 0;
+
+ p_usart->US_CR = US_CR_RSTTX | US_CR_RSTRX | US_CR_TXDIS | US_CR_RXDIS;
+
+ uint32_t br = DIV_ROUND_UP(CHIP_FREQ_CPU_MAX, rate);
+ p_usart->US_BRGR = br << US_BRGR_CD_Pos;
+
+ uint32_t reg = US_MR_CHRL_8_BIT |
+ US_MR_USART_MODE_SPI_MASTER |
+ US_MR_CLKO |
+ US_MR_CHMODE_NORMAL;
+ switch (mode) {
+ case 0:
+ reg |= US_MR_CPHA;
+ reg &= ~US_MR_CPOL;
+ break;
+ case 1:
+ reg &= ~US_MR_CPHA;
+ reg &= ~US_MR_CPOL;
+ break;
+ case 2:
+ reg |= US_MR_CPHA;
+ reg |= US_MR_CPOL;
+ break;
+ case 3:
+ reg &= ~US_MR_CPHA;
+ reg |= US_MR_CPOL;
+ break;
+ }
+
+ p_usart->US_MR |= reg;
+ p_usart->US_CR = US_CR_RXEN | US_CR_TXEN;
+ return (struct spi_config){ .spidev=p_usart, .cfg=p_usart->US_MR };
+}
+
+static void
+usart_prepare(struct spi_config config)
+{
+}
+
+static void
+usart_transfer(struct spi_config config, uint8_t receive_data
+ , uint8_t len, uint8_t *data)
+{
+ Usart *p_usart = config.spidev;
+ if (receive_data) {
+ for (uint32_t i = 0; i < len; ++i) {
+ uint32_t co = (uint32_t)*data & 0x000000FF;
+ while(!(p_usart->US_CSR & US_CSR_TXRDY)) {}
+ p_usart->US_THR = US_THR_TXCHR(co);
+ uint32_t ci = 0;
+ while(!(p_usart->US_CSR & US_CSR_RXRDY)) {}
+ ci = p_usart->US_RHR & US_RHR_RXCHR_Msk;
+ *data++ = (uint8_t)ci;
+ }
+ } else {
+ for (uint32_t i = 0; i < len; ++i) {
+ uint32_t co = (uint32_t)*data & 0x000000FF;
+ while(!(p_usart->US_CSR & US_CSR_TXRDY)) {}
+ p_usart->US_THR = US_THR_TXCHR(co);
+ while(!(p_usart->US_CSR & US_CSR_RXRDY)) {}
+ (void)(p_usart->US_RHR & US_RHR_RXCHR_Msk);
+ (void)*data++;
+ }
+ }
+}
+
+
+/****************************************************************
+ * Interface
+ ****************************************************************/
+
+struct spi_config
+spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
+{
+ if (bus >= ARRAY_SIZE(spi_bus))
+ shutdown("Invalid spi bus");
+ if (is_spihw(spi_bus[bus].dev))
+ return spihw_setup(bus, mode, rate);
+ return usart_setup(bus, mode, rate);
+}
+
+void
+spi_prepare(struct spi_config config)
+{
+ if (is_spihw(config.spidev))
+ spihw_prepare(config);
+ else
+ usart_prepare(config);
+}
+
+void
+spi_transfer(struct spi_config config, uint8_t receive_data
+ , uint8_t len, uint8_t *data)
+{
+ if (is_spihw(config.spidev))
+ spihw_transfer(config, receive_data, len, data);
+ else
+ usart_transfer(config, receive_data, len, data);
+}