aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/sam3x8e/Kconfig1
-rw-r--r--src/sam3x8e/Makefile2
-rw-r--r--src/sam3x8e/gpio.h7
-rw-r--r--src/sam3x8e/spi.c123
4 files changed, 132 insertions, 1 deletions
diff --git a/src/sam3x8e/Kconfig b/src/sam3x8e/Kconfig
index 92da8e58..b2dcb254 100644
--- a/src/sam3x8e/Kconfig
+++ b/src/sam3x8e/Kconfig
@@ -7,6 +7,7 @@ config SAM_SELECT
default y
select HAVE_GPIO
select HAVE_GPIO_ADC
+ select HAVE_GPIO_SPI
select HAVE_USER_INTERFACE
config BOARD_DIRECTORY
diff --git a/src/sam3x8e/Makefile b/src/sam3x8e/Makefile
index 43aef609..289ffb2e 100644
--- a/src/sam3x8e/Makefile
+++ b/src/sam3x8e/Makefile
@@ -15,7 +15,7 @@ CFLAGS_klipper.elf += -T lib/cmsis-sam3x8e/source/gcc/sam3x8e_flash.ld
CFLAGS_klipper.elf += --specs=nano.specs --specs=nosys.specs
# Add source files
-src-y += sam3x8e/main.c sam3x8e/timer.c sam3x8e/gpio.c
+src-y += sam3x8e/main.c sam3x8e/timer.c sam3x8e/gpio.c sam3x8e/spi.c
src-y += generic/crc16_ccitt.c generic/alloc.c
src-y += generic/armcm_irq.c generic/timer_irq.c
src-y += ../lib/cmsis-sam3x8e/source/system_sam3xa.c
diff --git a/src/sam3x8e/gpio.h b/src/sam3x8e/gpio.h
index 0c75f642..1cab4614 100644
--- a/src/sam3x8e/gpio.h
+++ b/src/sam3x8e/gpio.h
@@ -28,4 +28,11 @@ 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 {
+ uint32_t cfg;
+};
+struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate);
+void spi_transfer(struct spi_config config, uint8_t receive_data
+ , uint8_t len, uint8_t *data);
+
#endif // gpio.h
diff --git a/src/sam3x8e/spi.c b/src/sam3x8e/spi.c
new file mode 100644
index 00000000..791d71b3
--- /dev/null
+++ b/src/sam3x8e/spi.c
@@ -0,0 +1,123 @@
+// SPI transmissions on sam3x8e
+//
+// Copyright (C) 2018 Petri Honkala <cruwaller@gmail.com>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include <stddef.h> // NULL
+#include "autoconf.h"
+#include "command.h" // shutdown
+#include "gpio.h"
+#include "sched.h"
+#include <sam3x8e.h>
+#include <string.h>
+
+#define REGPTR SPI0
+#define PERIPH_ID ID_SPI0
+
+#define CHANNEL 0 // Use same channel for all
+
+static void
+spi_init(void)
+{
+ /* Configure SCK, MISO and MOSI */
+ gpio_peripheral('A', PIO_PA25A_SPI0_MISO, 'A', 0); // Arduino 74
+ gpio_peripheral('A', PIO_PA26A_SPI0_MOSI, 'A', 0); // Arduino 75
+ gpio_peripheral('A', PIO_PA27A_SPI0_SPCK, 'A', 0); // Arduino 76
+
+ // Enable SPI clocks
+ if (!(PMC->PMC_PCSR0 & (1u << PERIPH_ID))) {
+ PMC->PMC_PCER0 = (1 << PERIPH_ID);
+ }
+
+ /* Disable SPI */
+ REGPTR->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;
+
+ 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
+
+ /* Clear Chip Select Registers */
+ REGPTR->SPI_CSR[0] = 0;
+ REGPTR->SPI_CSR[1] = 0;
+ REGPTR->SPI_CSR[2] = 0;
+ REGPTR->SPI_CSR[3] = 0;
+
+ /* Set basic channel config */
+ REGPTR->SPI_CSR[CHANNEL] = 0;
+ /* Enable SPI */
+ REGPTR->SPI_CR = SPI_CR_SPIEN;
+}
+
+struct spi_config
+spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
+{
+ if (bus != CHANNEL || mode > 3)
+ shutdown("Invalid spi_setup parameters");
+
+ // Make sure bus is enabled
+ spi_init();
+
+ 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){.cfg = config};
+}
+
+void
+spi_transfer(struct spi_config config, uint8_t receive_data
+ , uint8_t len, uint8_t *data)
+{
+ REGPTR->SPI_CSR[CHANNEL] = config.cfg;
+
+ Spi* const pSpi = REGPTR;
+ if (receive_data) {
+ while (len--) {
+ pSpi->SPI_TDR = *data;
+ // wait for receive register
+ while (!(pSpi->SPI_SR & SPI_SR_RDRF)) { asm volatile("nop"); };
+ // get data
+ *data++ = pSpi->SPI_RDR;
+ }
+ } else {
+ while (len--) {
+ pSpi->SPI_TDR = *data++;
+ // wait for receive register
+ while (!(pSpi->SPI_SR & SPI_SR_RDRF)) { asm volatile("nop"); };
+ }
+ }
+}