aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLasse Dalegaard <dalegaard@gmail.com>2021-07-15 16:36:42 +0200
committerKevinOConnor <kevin@koconnor.net>2021-07-15 13:30:20 -0400
commit84523f28117b4da64044d60bafe3fde782d71c45 (patch)
tree62aa423c08d8028a508bf87ca1270a5de985c0c7
parent4cf25c714ae177c166d85961fcad3c4898eed7c3 (diff)
downloadkutter-84523f28117b4da64044d60bafe3fde782d71c45.tar.gz
kutter-84523f28117b4da64044d60bafe3fde782d71c45.tar.xz
kutter-84523f28117b4da64044d60bafe3fde782d71c45.zip
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 <dalegaard@gmail.com>
-rw-r--r--src/rp2040/Kconfig1
-rw-r--r--src/rp2040/Makefile1
-rw-r--r--src/rp2040/gpio.h9
-rw-r--r--src/rp2040/spi.c110
4 files changed, 121 insertions, 0 deletions
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 <dalegaard@gmail.com>
+//
+// 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++;
+ }
+}