aboutsummaryrefslogtreecommitdiffstats
path: root/src/atsam/spi.c
diff options
context:
space:
mode:
authorFlorian Heilmann <Florian.Heilmann@gmx.net>2019-01-13 02:14:50 +0100
committerKevinOConnor <kevin@koconnor.net>2019-01-12 20:14:50 -0500
commit6256599a6dfc0c52a3c5e019c470d4cc46feeb82 (patch)
tree4453913a6178a06e3f58dbfc530e6d65291473d3 /src/atsam/spi.c
parent432e6c490a229f71d608ec0391773ab7f90534d1 (diff)
downloadkutter-6256599a6dfc0c52a3c5e019c470d4cc46feeb82.tar.gz
kutter-6256599a6dfc0c52a3c5e019c470d4cc46feeb82.tar.xz
kutter-6256599a6dfc0c52a3c5e019c470d4cc46feeb82.zip
src: Rename source folders for atsam and atsamd architectures
Signed-off-by: Florian Heilmann <Florian.Heilmann@gmx.net>
Diffstat (limited to 'src/atsam/spi.c')
-rw-r--r--src/atsam/spi.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/src/atsam/spi.c b/src/atsam/spi.c
new file mode 100644
index 00000000..5f79a02c
--- /dev/null
+++ b/src/atsam/spi.c
@@ -0,0 +1,285 @@
+// 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.
+
+#include "command.h" // shutdown
+#include "gpio.h" // spi_setup
+#include "internal.h" // gpio_peripheral
+#include "sched.h" // sched_shutdown
+
+
+/****************************************************************
+ * 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' },
+ { USART0, ID_USART0, GPIO('A', 10), GPIO('A', 11), GPIO('A', 17), 'A', 'B'},
+ { USART1, ID_USART1, GPIO('A', 12), GPIO('A', 13), GPIO('A', 16), 'A', 'A'},
+ { USART2, ID_USART2, GPIO('B', 21), GPIO('B', 20), GPIO('B', 24), 'A', 'A'},
+#elif CONFIG_MACH_SAM4S8C
+ { SPI, ID_SPI, GPIO('A', 12), GPIO('A', 13), GPIO('A', 14), 'A', 'A' },
+ { USART0, ID_USART0, GPIO('A', 5), GPIO('A', 6), GPIO('A', 2), 'A', 'B' },
+ { USART1, ID_USART1, GPIO('A', 21), GPIO('A', 22), GPIO('A', 23), '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'},
+ { SPI, ID_SPI, GPIO('A', 12), GPIO('A', 13), GPIO('A', 14), 'A', 'A' },
+#endif
+};
+
+static int
+is_spihw(void *dev)
+{
+#if CONFIG_MACH_SAM3X8E
+ return dev == SPI0;
+#else
+ return dev == SPI;
+#endif
+}
+
+static void
+init_pins(uint32_t bus)
+{
+ 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);
+}
+
+
+/****************************************************************
+ * 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 */
+ pSpi->SPI_CR = SPI_CR_SPIDIS;
+ /* Execute a software reset of the SPI twice */
+ pSpi->SPI_CR = SPI_CR_SWRST;
+ pSpi->SPI_CR = SPI_CR_SWRST;
+
+ 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 */
+ pSpi->SPI_CSR[0] = 0;
+ pSpi->SPI_CSR[1] = 0;
+ pSpi->SPI_CSR[2] = 0;
+ pSpi->SPI_CSR[3] = 0;
+
+ /* Set basic channel config */
+ pSpi->SPI_CSR[CHANNEL] = 0;
+ /* Enable SPI */
+ pSpi->SPI_CR = SPI_CR_SPIEN;
+}
+
+static struct spi_config
+spihw_setup(uint32_t bus, uint8_t mode, uint32_t rate)
+{
+ // Make sure bus is enabled
+ spihw_init(bus);
+
+ uint32_t config = 0;
+ uint32_t clockDiv;
+ if (rate < (CHIP_FREQ_CPU_MAX / 255)) {
+ clockDiv = 255;
+ } else if (rate >= (CHIP_FREQ_CPU_MAX / 2)) {
+ clockDiv = 2;
+ } else {
+ clockDiv = (CHIP_FREQ_CPU_MAX / (rate + 1)) + 1;
+ }
+
+ /****** Will be written to SPI_CSRx register ******/
+ // CSAAT : Chip Select Active After Transfer
+ config = SPI_CSR_CSAAT;
+ config |= SPI_CSR_BITS_8_BIT; // TODO: support for SPI_CSR_BITS_16_BIT
+ // NOTE: NCPHA is inverted, CPHA normal!!
+ switch(mode) {
+ case 0:
+ config |= SPI_CSR_NCPHA;
+ break;
+ case 1:
+ config |= 0;
+ break;
+ case 2:
+ config |= SPI_CSR_NCPHA;
+ config |= SPI_CSR_CPOL;
+ break;
+ case 3:
+ config |= SPI_CSR_CPOL;
+ break;
+ };
+
+ config |= ((clockDiv & 0xffu) << SPI_CSR_SCBR_Pos);
+ return (struct spi_config){ .spidev = spi_bus[bus].dev, .cfg = config };
+}
+
+static void
+spihw_prepare(struct spi_config config)
+{
+ Spi *pSpi = config.spidev;
+ pSpi->SPI_CSR[CHANNEL] = config.cfg;
+}
+
+static void
+spihw_transfer(struct spi_config config, uint8_t receive_data
+ , uint8_t len, uint8_t *data)
+{
+ Spi *pSpi = config.spidev;
+ if (receive_data) {
+ while (len--) {
+ pSpi->SPI_TDR = *data;
+ // wait for receive register
+ while (!(pSpi->SPI_SR & SPI_SR_RDRF))
+ ;
+ // get data
+ *data++ = pSpi->SPI_RDR;
+ }
+ } else {
+ while (len--) {
+ pSpi->SPI_TDR = *data++;
+ // wait for receive register
+ while (!(pSpi->SPI_SR & SPI_SR_RDRF))
+ ;
+ // read data (to clear RDRF)
+ pSpi->SPI_RDR;
+ }
+ }
+}
+
+
+/****************************************************************
+ * 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);
+}