From 84523f28117b4da64044d60bafe3fde782d71c45 Mon Sep 17 00:00:00 2001 From: Lasse Dalegaard Date: Thu, 15 Jul 2021 16:36:42 +0200 Subject: [PATCH] rp2040: implement SPI This implements SPI for the rp2040 target. All output groupings of both SPI blocks are available for use. Signed-off-by: Lasse Dalegaard --- src/rp2040/Kconfig | 1 + src/rp2040/Makefile | 1 + src/rp2040/gpio.h | 9 ++++ src/rp2040/spi.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 src/rp2040/spi.c diff --git a/src/rp2040/Kconfig b/src/rp2040/Kconfig index 937d8f24..1682fa9a 100644 --- a/src/rp2040/Kconfig +++ b/src/rp2040/Kconfig @@ -7,6 +7,7 @@ config RP2040_SELECT default y select HAVE_GPIO select HAVE_GPIO_ADC + select HAVE_GPIO_SPI select HAVE_GPIO_BITBANGING select HAVE_STRICT_TIMING select HAVE_CHIPID diff --git a/src/rp2040/Makefile b/src/rp2040/Makefile index 1d419a07..b40df32d 100644 --- a/src/rp2040/Makefile +++ b/src/rp2040/Makefile @@ -20,6 +20,7 @@ src-$(CONFIG_USBSERIAL) += rp2040/usbserial.c generic/usb_cdc.c src-$(CONFIG_USBSERIAL) += rp2040/chipid.c src-$(CONFIG_SERIAL) += rp2040/serial.c generic/serial_irq.c src-$(CONFIG_HAVE_GPIO_HARD_PWM) += rp2040/hard_pwm.c +src-$(CONFIG_HAVE_GPIO_SPI) += rp2040/spi.c # rp2040 stage2 building $(OUT)stage2.o: lib/rp2040/boot_stage2/boot2_w25q080.S diff --git a/src/rp2040/gpio.h b/src/rp2040/gpio.h index 5f6a836a..90680c6e 100644 --- a/src/rp2040/gpio.h +++ b/src/rp2040/gpio.h @@ -35,4 +35,13 @@ uint32_t gpio_adc_sample(struct gpio_adc g); uint16_t gpio_adc_read(struct gpio_adc g); void gpio_adc_cancel_sample(struct gpio_adc g); +struct spi_config { + void *spi; + uint32_t cr0, cpsr; +}; +struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate); +void spi_prepare(struct spi_config config); +void spi_transfer(struct spi_config config, uint8_t receive_data + , uint8_t len, uint8_t *data); + #endif // gpio.h diff --git a/src/rp2040/spi.c b/src/rp2040/spi.c new file mode 100644 index 00000000..e6aafa00 --- /dev/null +++ b/src/rp2040/spi.c @@ -0,0 +1,110 @@ +// SPI functions on rp2040 +// +// Copyright (C) 2021 Lasse Dalegaard +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "gpio.h" // spi_setup, spi_prepare, spi_transfer +#include "command.h" // shutdown" +#include "sched.h" // sched_shutdown" +#include "internal.h" // pclock, gpio_peripheral +#include "hardware/structs/spi.h" // spi_hw_t +#include "hardware/regs/resets.h" // RESETS_RESET_SPI*_BITS + +DECL_ENUMERATION("spi_bus", "spi0a", 0); +DECL_CONSTANT_STR("BUS_PINS_spi0a", "gpio0,gpio3,gpio2"); +DECL_ENUMERATION("spi_bus", "spi0b", 1); +DECL_CONSTANT_STR("BUS_PINS_spi0b", "gpio4,gpio7,gpio6"); +DECL_ENUMERATION("spi_bus", "spi0c", 2); +DECL_CONSTANT_STR("BUS_PINS_spi0c", "gpio16,gpio19,gpio18"); +DECL_ENUMERATION("spi_bus", "spi0d", 3); +DECL_CONSTANT_STR("BUS_PINS_spi0d", "gpio20,gpio23,gpio22"); + +DECL_ENUMERATION("spi_bus", "spi1a", 4); +DECL_CONSTANT_STR("BUS_PINS_spi1a", "gpio8,gpio11,gpio10"); +DECL_ENUMERATION("spi_bus", "spi1b", 5); +DECL_CONSTANT_STR("BUS_PINS_spi1b", "gpio12,gpio15,gpio14"); +DECL_ENUMERATION("spi_bus", "spi1c", 6); +DECL_CONSTANT_STR("BUS_PINS_spi1c", "gpio24,gpio27,gpio26"); + +struct spi_info { + spi_hw_t *spi; + uint8_t miso_pin, mosi_pin, sck_pin; + uint32_t pclk; +}; + +static const struct spi_info spi_bus[] = { + {spi0_hw, 0, 3, 2, RESETS_RESET_SPI0_BITS}, + {spi0_hw, 4, 7, 6, RESETS_RESET_SPI0_BITS}, + {spi0_hw, 16, 19, 18, RESETS_RESET_SPI0_BITS}, + {spi0_hw, 20, 23, 22, RESETS_RESET_SPI0_BITS}, + + {spi1_hw, 8, 11, 10, RESETS_RESET_SPI1_BITS}, + {spi1_hw, 12, 15, 14, RESETS_RESET_SPI1_BITS}, + {spi1_hw, 24, 27, 26, RESETS_RESET_SPI1_BITS}, +}; + +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_enabled_pclock(spi_bus[bus].pclk)) + enable_pclock(spi_bus[bus].pclk); + + gpio_peripheral(spi_bus[bus].miso_pin, 1, 1); + gpio_peripheral(spi_bus[bus].mosi_pin, 1, 0); + gpio_peripheral(spi_bus[bus].sck_pin, 1, 0); + + uint32_t pclk = get_pclock_frequency(spi_bus[bus].pclk); + + struct spi_config res = {spi_bus[bus].spi, 0, 0}; + + uint8_t prescale; + for (prescale = 2; prescale <= 254; prescale += 2) { + if (pclk < (prescale + 2) * 256 * rate) + break; + } + + uint8_t postdiv; + for (postdiv = 255; postdiv > 0; --postdiv) { + if ((pclk / (prescale * postdiv)) > rate) + break; + } + + res.cr0 |= postdiv << SPI_SSPCR0_SCR_LSB; + res.cr0 |= ((mode & 2) != 0) << SPI_SSPCR0_SPO_LSB; + res.cr0 |= ((mode & 1) != 0) << SPI_SSPCR0_SPH_LSB; + res.cr0 |= 7 << SPI_SSPCR0_DSS_LSB; // 8bit mode + res.cpsr = prescale; + + // Enable the peripheral + spi_bus[bus].spi->cr1 = SPI_SSPCR1_SSE_BITS; + + return res; +} + +void +spi_prepare(struct spi_config config) +{ + spi_hw_t *spi = config.spi; + spi->cr0 = config.cr0; + spi->cpsr = config.cpsr; +} + +void +spi_transfer(struct spi_config config, uint8_t receive_data, + uint8_t len, uint8_t *data) +{ + spi_hw_t *spi = config.spi; + while (len--) { + spi->dr = *data; + while (!(spi->sr & SPI_SSPSR_RNE_BITS)) + ; + uint8_t rdata = spi->dr; + if(receive_data) + *data = rdata; + data++; + } +}