diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2018-12-28 20:51:34 -0500 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2019-01-07 19:33:26 -0500 |
commit | 212813906a7ae3b81b47ac12648f3080ba26dbde (patch) | |
tree | fd0a890a7c84fcade4698a5844b53928b6a7b706 /src/sam3/spi.c | |
parent | 4683036f98b319d1c36853c1a200d63c36b470ff (diff) | |
download | kutter-212813906a7ae3b81b47ac12648f3080ba26dbde.tar.gz kutter-212813906a7ae3b81b47ac12648f3080ba26dbde.tar.xz kutter-212813906a7ae3b81b47ac12648f3080ba26dbde.zip |
sam3: Merge sam4e_spi.c into spi.c
Determine at runtime if the SPI or USART devices should be used.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src/sam3/spi.c')
-rw-r--r-- | src/sam3/spi.c | 227 |
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); +} |