From c8d7e28a35bc3d3699fa5b77327ad194fd3c3105 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 19 Feb 2019 00:32:37 -0500 Subject: [PATCH] atsamd: Add support for programming SERCOM pins Signed-off-by: Florian Heilmann Signed-off-by: Kevin O'Connor --- config/example-extras.cfg | 21 ++ docs/Config_Changes.md | 3 + klippy/extras/samd_sercom.py | 46 +++++ src/atsamd/Kconfig | 6 +- src/atsamd/Makefile | 1 + src/atsamd/gpio.h | 2 + src/atsamd/i2c.c | 34 ++-- src/atsamd/internal.h | 5 + src/atsamd/sercom.c | 380 +++++++++++++++++++++++++++++++++++ src/atsamd/spi.c | 43 ++-- 10 files changed, 500 insertions(+), 41 deletions(-) create mode 100644 klippy/extras/samd_sercom.py create mode 100644 src/atsamd/sercom.c diff --git a/config/example-extras.cfg b/config/example-extras.cfg index 7e239f09..2645c6cb 100644 --- a/config/example-extras.cfg +++ b/config/example-extras.cfg @@ -721,6 +721,27 @@ # parameter must be provided. +# SAMD SERCOM configuration to specify which pins to use on a given SERCOM. +# One may define one section with the "samd_sercom" prefix per +# SERCOM available. Each SERCOM must be configured prior to using it as +# SPI or I2C peripheral. Place this config section above any other section +# that makes use of SPI or I2C buses. +#[samd_sercom 0] +#tx_pin: +# MOSI pin for SPI communication, or SDA (data) pin for I2C +# communication. The pin must have a valid pinmux configuration +# for the given SERCOM peripheral. This parameter must be provided. +#rx_pin: +# MISO pin for SPI communication. This pin is not used for I2C +# communication (I2C uses tx_pin for both sending and receiving). +# The pin must have a valid pinmux configuration for the given +# SERCOM peripheral. This parameter is optional. +#clk_pin: +# CLK pin for SPI communication, or SCL (clock) pin for I2C +# communication. The pin must have a valid pinmux configuration +# for the given SERCOM peripheral. This parameter must be provided. + + # Statically configured AD5206 digipots connected via SPI bus (one may # define any number of sections with an "ad5206" prefix). #[ad5206 my_digipot] diff --git a/docs/Config_Changes.md b/docs/Config_Changes.md index 1f0a6445..707adce6 100644 --- a/docs/Config_Changes.md +++ b/docs/Config_Changes.md @@ -6,6 +6,9 @@ All dates in this document are approximate. # Changes +20190228: Users of SPI or I2C on SAMD21 boards must now specify the +bus pins via a [samd_sercom] config section. + 20190224: The bed_shape option has been removed from bed_mesh. The radius option has been renamed to bed_radius. Users with round beds should supply the bed_radius and round_probe_count options. diff --git a/klippy/extras/samd_sercom.py b/klippy/extras/samd_sercom.py new file mode 100644 index 00000000..2861cf6a --- /dev/null +++ b/klippy/extras/samd_sercom.py @@ -0,0 +1,46 @@ +# SAMD Sercom configuration +# +# Copyright (C) 2019 Florian Heilmann +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +class SamdSERCOM: + def __init__(self, config): + self.printer = config.get_printer() + self.name = config.get_name().split()[1] + ppins = self.printer.lookup_object("pins") + try: + int(self.name) + except ValueError: + raise ppins.error("%s: SERCOM name must be an integer" % ( + config.get_name(),)) + + self.tx_pin = config.get("tx_pin") + self.rx_pin = config.get("rx_pin", None) + self.clk_pin = config.get("clk_pin") + + tx_pin_params = ppins.lookup_pin(self.tx_pin) + self.mcu = tx_pin_params['chip'] + self.mcu.add_config_cmd( + "set_sercom_pin sercom_id=%s pin_type=%d pin=%s" % ( + self.name, 0, self.tx_pin)) + + clk_pin_params = ppins.lookup_pin(self.clk_pin) + if self.mcu is not clk_pin_params['chip']: + raise ppins.error("%s: SERCOM pins must be on same mcu" % ( + config.get_name(),)) + self.mcu.add_config_cmd( + "set_sercom_pin sercom_id=%s pin_type=%d pin=%s" % ( + self.name, 2, self.clk_pin)) + + if self.rx_pin: + rx_pin_params = ppins.lookup_pin(self.rx_pin) + if self.mcu is not rx_pin_params['chip']: + raise ppins.error("%s: SERCOM pins must be on same mcu" % ( + config.get_name(),)) + self.mcu.add_config_cmd( + "set_sercom_pin sercom_id=%s pin_type=%d pin=%s" % ( + self.name, 1, self.rx_pin)) + +def load_config_prefix(config): + return SamdSERCOM(config) diff --git a/src/atsamd/Kconfig b/src/atsamd/Kconfig index af516685..c77f57bb 100644 --- a/src/atsamd/Kconfig +++ b/src/atsamd/Kconfig @@ -8,7 +8,7 @@ config ATSAMD_SELECT select HAVE_GPIO select HAVE_GPIO_ADC select HAVE_GPIO_I2C - select HAVE_GPIO_SPI if MACH_SAMD21 + select HAVE_GPIO_SPI select HAVE_GPIO_HARD_PWM if MACH_SAMD21 select HAVE_GPIO_BITBANGING @@ -85,5 +85,9 @@ config SERIAL depends on !USBSERIAL bool default y +config HAVE_SERCOM + depends on HAVE_GPIO_I2C || HAVE_GPIO_SPI + bool + default y endif diff --git a/src/atsamd/Makefile b/src/atsamd/Makefile index 7546d1ce..3b749d5d 100644 --- a/src/atsamd/Makefile +++ b/src/atsamd/Makefile @@ -27,6 +27,7 @@ src-$(CONFIG_SERIAL) += atsamd/serial.c generic/serial_irq.c src-$(CONFIG_HAVE_GPIO_ADC) += atsamd/adc.c src-$(CONFIG_HAVE_GPIO_I2C) += atsamd/i2c.c src-$(CONFIG_HAVE_GPIO_SPI) += atsamd/spi.c +src-$(CONFIG_HAVE_SERCOM) += atsamd/sercom.c src-$(CONFIG_HAVE_GPIO_HARD_PWM) += atsamd/hard_pwm.c src-$(CONFIG_MACH_SAMD21) += atsamd/watchdog.c src-$(CONFIG_MACH_SAMD21) += atsamd/clock.c atsamd/timer.c generic/timer_irq.c diff --git a/src/atsamd/gpio.h b/src/atsamd/gpio.h index ba15e719..b8cb3e86 100644 --- a/src/atsamd/gpio.h +++ b/src/atsamd/gpio.h @@ -37,6 +37,7 @@ uint16_t gpio_adc_read(struct gpio_adc g); void gpio_adc_cancel_sample(struct gpio_adc g); struct spi_config { + void *ss; uint32_t ctrla, baud; }; struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate); @@ -45,6 +46,7 @@ void spi_transfer(struct spi_config config, uint8_t receive_data , uint8_t len, uint8_t *data); struct i2c_config { + void *si; uint8_t addr; }; diff --git a/src/atsamd/i2c.c b/src/atsamd/i2c.c index 03a75f11..89b190fa 100644 --- a/src/atsamd/i2c.c +++ b/src/atsamd/i2c.c @@ -1,5 +1,6 @@ // i2c support on samd // +// Copyright (C) 2019 Florian Heilmann // Copyright (C) 2018-2019 Kevin O'Connor // // This file may be distributed under the terms of the GNU GPLv3 license. @@ -13,22 +14,14 @@ #define I2C_FREQ 100000 static void -i2c_init(void) +i2c_init(uint32_t bus, SercomI2cm *si) { - static int have_run_init; - if (have_run_init) + static int have_run_init[8]; + if (have_run_init[bus]) return; - have_run_init = 1; - - // Setup clock - enable_pclock(SERCOM3_GCLK_ID_CORE, ID_SERCOM3); - - // Configure SDA, SCL pins - gpio_peripheral(GPIO('A', 22), 'C', 0); - gpio_peripheral(GPIO('A', 23), 'C', 0); + have_run_init[bus] = 1; // Configure i2c - SercomI2cm *si = &SERCOM3->I2CM; si->CTRLA.reg = 0; uint32_t areg = (SERCOM_I2CM_CTRLA_LOWTOUTEN | SERCOM_I2CM_CTRLA_INACTOUT(3) @@ -36,7 +29,7 @@ i2c_init(void) | SERCOM_I2CM_STATUS_MEXTTOUT | SERCOM_I2CM_CTRLA_MODE(5)); si->CTRLA.reg = areg; - uint32_t freq = get_pclock_frequency(SERCOM3_GCLK_ID_CORE); + uint32_t freq = sercom_get_pclock_frequency(bus); uint32_t baud = (freq/I2C_FREQ - 10 - freq*TIME_RISE/1000000000) / 2; si->BAUD.reg = baud; si->CTRLA.reg = areg | SERCOM_I2CM_CTRLA_ENABLE; @@ -52,10 +45,17 @@ i2c_init(void) struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) { - if (bus) +#ifdef SERCOM7 + if (bus > 7) +#else + if (bus > 5) +#endif shutdown("Unsupported i2c bus"); - i2c_init(); - return (struct i2c_config){ .addr=addr<<1 }; + Sercom *sercom = sercom_enable_pclock(bus); + sercom_i2c_pins(bus); + SercomI2cm *si = &sercom->I2CM; + i2c_init(bus, si); + return (struct i2c_config){ .si=si, .addr=addr<<1 }; } static void @@ -97,7 +97,7 @@ i2c_stop(SercomI2cm *si) void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) { - SercomI2cm *si = &SERCOM3->I2CM; + SercomI2cm *si = (SercomI2cm *)config.si; i2c_start(si, config.addr); while (write_len--) i2c_send_byte(si, *write++); diff --git a/src/atsamd/internal.h b/src/atsamd/internal.h index 45d478b0..f31be95c 100644 --- a/src/atsamd/internal.h +++ b/src/atsamd/internal.h @@ -22,4 +22,9 @@ void enable_pclock(uint32_t pclk_id, uint32_t pm_id); uint32_t get_pclock_frequency(uint32_t pclk_id); void gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up); +Sercom * sercom_enable_pclock(uint32_t sercom_id); +uint32_t sercom_get_pclock_frequency(uint32_t sercom_id); +uint32_t sercom_spi_pins(uint32_t sercom_id); +void sercom_i2c_pins(uint32_t sercom_id); + #endif // internal.h diff --git a/src/atsamd/sercom.c b/src/atsamd/sercom.c new file mode 100644 index 00000000..3b80f889 --- /dev/null +++ b/src/atsamd/sercom.c @@ -0,0 +1,380 @@ +// Handling of sercom pins +// +// Copyright (C) 2019 Florian Heilmann +// Copyright (C) 2019 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "internal.h" // sercom_enable +#include "command.h" // shutdown +#include "compiler.h" // ARRAY_SIZE +#include "sched.h" // sched_shutdown + + +/**************************************************************** + * Available sercom blocks + ****************************************************************/ + +struct sercom_bus { + Sercom *sercom; + uint32_t pclk_id, pm_id; +}; + +static const struct sercom_bus sercoms[] = { + { SERCOM0, SERCOM0_GCLK_ID_CORE, ID_SERCOM0 }, + { SERCOM1, SERCOM1_GCLK_ID_CORE, ID_SERCOM1 }, + { SERCOM2, SERCOM2_GCLK_ID_CORE, ID_SERCOM2 }, + { SERCOM3, SERCOM3_GCLK_ID_CORE, ID_SERCOM3 }, + { SERCOM4, SERCOM4_GCLK_ID_CORE, ID_SERCOM4 }, + { SERCOM5, SERCOM5_GCLK_ID_CORE, ID_SERCOM5 }, +#ifdef SERCOM6 + { SERCOM6, SERCOM6_GCLK_ID_CORE, ID_SERCOM6 }, + { SERCOM7, SERCOM7_GCLK_ID_CORE, ID_SERCOM7 }, +#endif +}; + +Sercom * +sercom_enable_pclock(uint32_t sercom_id) +{ + if (sercom_id >= ARRAY_SIZE(sercoms)) + shutdown("Invalid sercom bus"); + const struct sercom_bus *sb = &sercoms[sercom_id]; + enable_pclock(sb->pclk_id, sb->pm_id); + return sb->sercom; +} + +uint32_t +sercom_get_pclock_frequency(uint32_t sercom_id) +{ + const struct sercom_bus *sb = &sercoms[sercom_id]; + return get_pclock_frequency(sb->pclk_id); +} + + +/**************************************************************** + * Pin configurations + ****************************************************************/ + +struct sercom_pad { + uint8_t sercom_id, gpio, pad, ptype; +}; + + +static const struct sercom_pad sercom_pads[] = { +#if CONFIG_MACH_SAMD21 + { 0, GPIO('A', 8), 0, 'C'}, + { 0, GPIO('A', 9), 1, 'C'}, + { 0, GPIO('A', 10), 2, 'C'}, + { 0, GPIO('A', 11), 3, 'C'}, + { 0, GPIO('A', 4), 0, 'D'}, + { 0, GPIO('A', 1), 1, 'D'}, + { 0, GPIO('A', 6), 2, 'D'}, + { 0, GPIO('A', 7), 3, 'D'}, + { 1, GPIO('A', 16), 0, 'C'}, + { 1, GPIO('A', 17), 1, 'C'}, + { 1, GPIO('A', 18), 2, 'C'}, + { 1, GPIO('A', 19), 3, 'C'}, + { 1, GPIO('A', 0), 0, 'D'}, + { 1, GPIO('A', 1), 1, 'D'}, + { 1, GPIO('A', 30), 2, 'D'}, + { 1, GPIO('A', 31), 3, 'D'}, + { 2, GPIO('A', 12), 0, 'C'}, + { 2, GPIO('A', 13), 1, 'C'}, + { 2, GPIO('A', 14), 2, 'C'}, + { 2, GPIO('A', 15), 3, 'C'}, + { 2, GPIO('A', 8), 0, 'D'}, + { 2, GPIO('A', 9), 1, 'D'}, + { 2, GPIO('A', 10), 2, 'D'}, + { 2, GPIO('A', 11), 3, 'D'}, + { 3, GPIO('A', 22), 0, 'C'}, + { 3, GPIO('A', 23), 1, 'C'}, + { 3, GPIO('A', 24), 2, 'C'}, + { 3, GPIO('A', 25), 3, 'C'}, + { 3, GPIO('A', 16), 0, 'D'}, + { 3, GPIO('A', 17), 1, 'D'}, + { 3, GPIO('A', 18), 2, 'D'}, + { 3, GPIO('A', 19), 3, 'D'}, + { 3, GPIO('A', 20), 2, 'D'}, + { 3, GPIO('A', 21), 3, 'D'}, + { 4, GPIO('B', 12), 0, 'C'}, + { 4, GPIO('B', 13), 1, 'C'}, + { 4, GPIO('B', 14), 2, 'C'}, + { 4, GPIO('B', 15), 3, 'C'}, + { 4, GPIO('B', 8), 0, 'D'}, + { 4, GPIO('B', 9), 1, 'D'}, + { 4, GPIO('B', 10), 2, 'D'}, + { 4, GPIO('B', 11), 3, 'D'}, + { 4, GPIO('A', 12), 0, 'D'}, + { 4, GPIO('A', 13), 1, 'D'}, + { 4, GPIO('A', 14), 2, 'D'}, + { 4, GPIO('A', 15), 3, 'D'}, + { 5, GPIO('B', 16), 0, 'C'}, + { 5, GPIO('B', 17), 1, 'C'}, + { 5, GPIO('A', 20), 2, 'C'}, + { 5, GPIO('A', 21), 3, 'C'}, + { 5, GPIO('A', 22), 0, 'D'}, + { 5, GPIO('A', 23), 1, 'D'}, + { 5, GPIO('A', 24), 2, 'D'}, + { 5, GPIO('A', 25), 3, 'D'}, + { 5, GPIO('B', 30), 0, 'D'}, + { 5, GPIO('B', 31), 1, 'D'}, + { 5, GPIO('B', 22), 2, 'D'}, + { 5, GPIO('B', 23), 3, 'D'}, + { 5, GPIO('B', 2), 0, 'D'}, + { 5, GPIO('B', 3), 1, 'D'}, + { 5, GPIO('B', 0), 2, 'D'}, + { 5, GPIO('B', 1), 3, 'D'}, +#elif CONFIG_MACH_SAMD51 + { 0, GPIO('A', 8), 0, 'C'}, + { 0, GPIO('A', 9), 1, 'C'}, + { 0, GPIO('A', 10), 2, 'C'}, + { 0, GPIO('A', 11), 3, 'C'}, + { 0, GPIO('B', 24), 0, 'C'}, + { 0, GPIO('B', 25), 1, 'C'}, + { 0, GPIO('C', 24), 2, 'C'}, + { 0, GPIO('C', 25), 3, 'C'}, + { 0, GPIO('A', 4), 0, 'D'}, + { 0, GPIO('A', 5), 1, 'D'}, + { 0, GPIO('A', 6), 2, 'D'}, + { 0, GPIO('A', 7), 3, 'D'}, + { 0, GPIO('C', 17), 0, 'D'}, + { 0, GPIO('C', 16), 1, 'D'}, + { 0, GPIO('C', 18), 2, 'D'}, + { 0, GPIO('C', 19), 3, 'D'}, + + { 1, GPIO('A', 16), 0, 'C'}, + { 1, GPIO('A', 17), 1, 'C'}, + { 1, GPIO('A', 18), 2, 'C'}, + { 1, GPIO('A', 19), 3, 'C'}, + { 1, GPIO('C', 22), 0, 'C'}, + { 1, GPIO('C', 23), 1, 'C'}, + { 1, GPIO('D', 20), 2, 'C'}, + { 1, GPIO('D', 21), 3, 'C'}, + { 1, GPIO('C', 27), 0, 'C'}, + { 1, GPIO('C', 28), 1, 'C'}, + { 1, GPIO('B', 22), 2, 'C'}, + { 1, GPIO('B', 23), 3, 'C'}, + { 1, GPIO('A', 0), 0, 'D'}, + { 1, GPIO('A', 1), 1, 'D'}, + { 1, GPIO('A', 30), 2, 'D'}, + { 1, GPIO('A', 31), 3, 'D'}, + + { 2, GPIO('A', 12), 0, 'C'}, + { 2, GPIO('A', 13), 1, 'C'}, + { 2, GPIO('A', 14), 2, 'C'}, + { 2, GPIO('A', 15), 3, 'C'}, + { 2, GPIO('B', 26), 0, 'C'}, + { 2, GPIO('B', 27), 1, 'C'}, + { 2, GPIO('B', 28), 2, 'C'}, + { 2, GPIO('B', 29), 3, 'C'}, + { 2, GPIO('A', 9), 0, 'D'}, + { 2, GPIO('A', 8), 1, 'D'}, + { 2, GPIO('A', 10), 2, 'D'}, + { 2, GPIO('A', 11), 3, 'D'}, + { 2, GPIO('B', 25), 0, 'D'}, + { 2, GPIO('B', 24), 1, 'D'}, + { 2, GPIO('C', 24), 2, 'D'}, + { 2, GPIO('C', 25), 3, 'D'}, + + { 3, GPIO('A', 22), 0, 'C'}, + { 3, GPIO('A', 23), 1, 'C'}, + { 3, GPIO('A', 24), 2, 'C'}, + { 3, GPIO('A', 25), 3, 'C'}, + { 3, GPIO('B', 20), 0, 'C'}, + { 3, GPIO('B', 21), 1, 'C'}, + { 3, GPIO('A', 20), 2, 'D'}, + { 3, GPIO('A', 21), 3, 'D'}, + { 3, GPIO('A', 17), 0, 'D'}, + { 3, GPIO('A', 16), 1, 'D'}, + { 3, GPIO('A', 18), 2, 'D'}, + { 3, GPIO('A', 19), 3, 'D'}, + { 3, GPIO('C', 23), 0, 'D'}, + { 3, GPIO('C', 22), 1, 'D'}, + { 3, GPIO('D', 20), 2, 'D'}, + { 3, GPIO('D', 21), 3, 'D'}, + + { 4, GPIO('B', 12), 0, 'C'}, + { 4, GPIO('B', 13), 1, 'C'}, + { 4, GPIO('B', 14), 2, 'C'}, + { 4, GPIO('B', 15), 3, 'C'}, + { 4, GPIO('B', 8), 0, 'D'}, + { 4, GPIO('B', 9), 1, 'D'}, + { 4, GPIO('B', 10), 2, 'D'}, + { 4, GPIO('B', 11), 3, 'D'}, + { 4, GPIO('A', 13), 0, 'D'}, + { 4, GPIO('A', 12), 1, 'D'}, + { 4, GPIO('A', 14), 2, 'D'}, + { 4, GPIO('A', 15), 3, 'D'}, + { 4, GPIO('B', 27), 0, 'D'}, + { 4, GPIO('B', 26), 1, 'D'}, + { 4, GPIO('B', 28), 2, 'D'}, + { 4, GPIO('B', 29), 3, 'D'}, + + { 5, GPIO('B', 16), 0, 'C'}, + { 5, GPIO('B', 17), 1, 'C'}, + { 5, GPIO('B', 18), 2, 'C'}, + { 5, GPIO('B', 19), 3, 'C'}, + { 5, GPIO('A', 23), 0, 'D'}, + { 5, GPIO('A', 22), 1, 'D'}, + { 5, GPIO('A', 20), 2, 'D'}, + { 5, GPIO('A', 21), 3, 'D'}, + { 5, GPIO('A', 24), 2, 'D'}, + { 5, GPIO('A', 25), 3, 'D'}, + { 5, GPIO('B', 22), 2, 'D'}, + { 5, GPIO('B', 23), 3, 'D'}, + { 5, GPIO('B', 31), 0, 'D'}, + { 5, GPIO('B', 30), 1, 'D'}, + { 5, GPIO('B', 0), 2, 'D'}, + { 5, GPIO('B', 1), 3, 'D'}, + { 5, GPIO('B', 2), 0, 'D'}, + { 5, GPIO('B', 3), 1, 'D'}, + #ifdef SERCOM6 + { 6, GPIO('C', 16), 0, 'C'}, + { 6, GPIO('C', 17), 1, 'C'}, + { 6, GPIO('C', 18), 2, 'C'}, + { 6, GPIO('C', 19), 3, 'C'}, + { 6, GPIO('C', 4), 0, 'C'}, + { 6, GPIO('C', 5), 1, 'C'}, + { 6, GPIO('C', 6), 2, 'C'}, + { 6, GPIO('C', 7), 3, 'C'}, + { 6, GPIO('D', 9), 0, 'D'}, + { 6, GPIO('D', 8), 1, 'D'}, + { 6, GPIO('D', 10), 2, 'D'}, + { 6, GPIO('D', 11), 3, 'D'}, + { 6, GPIO('C', 13), 0, 'D'}, + { 6, GPIO('C', 12), 1, 'D'}, + { 6, GPIO('C', 14), 2, 'D'}, + { 6, GPIO('C', 15), 3, 'D'}, + { 6, GPIO('C', 10), 2, 'C'}, + { 6, GPIO('C', 11), 3, 'C'}, + + { 7, GPIO('C', 12), 0, 'C'}, + { 7, GPIO('C', 13), 1, 'C'}, + { 7, GPIO('C', 14), 2, 'C'}, + { 7, GPIO('C', 15), 3, 'C'}, + { 7, GPIO('D', 8), 0, 'C'}, + { 7, GPIO('D', 9), 1, 'C'}, + { 7, GPIO('D', 10), 2, 'C'}, + { 7, GPIO('D', 11), 3, 'C'}, + { 7, GPIO('C', 10), 2, 'D'}, + { 7, GPIO('C', 11), 3, 'D'}, + { 7, GPIO('B', 21), 0, 'D'}, + { 7, GPIO('B', 20), 1, 'D'}, + { 7, GPIO('B', 18), 2, 'D'}, + { 7, GPIO('B', 19), 3, 'D'}, + { 7, GPIO('B', 30), 0, 'C'}, + { 7, GPIO('B', 31), 1, 'C'}, + { 7, GPIO('A', 30), 2, 'C'}, + { 7, GPIO('A', 31), 3, 'C'}, + #endif +#endif +}; + +static const struct sercom_pad * +sercom_lookup_pad(uint32_t sercom_id, uint8_t pin) +{ + const struct sercom_pad *sp = sercom_pads; + for (; ; sp++) { + if (sp >= &sercom_pads[ARRAY_SIZE(sercom_pads)]) + shutdown("Invalid SERCOM configuration"); + if (sp->sercom_id == sercom_id && sp->gpio == pin) + return sp; + } +} + + +/**************************************************************** + * Runtime configuration + ****************************************************************/ + +enum { TX_PIN, RX_PIN, CLK_PIN }; + +// Runtime configuration +struct sercom_pin { + uint8_t pins[3]; +}; + +static struct sercom_pin sercom_pins[ARRAY_SIZE(sercoms)]; + +void +command_set_sercom_pin(uint32_t *args) +{ + uint8_t sercom_id = args[0], pin_type = args[1], pin = args[2]; + if (sercom_id >= ARRAY_SIZE(sercom_pins) + || pin_type >= ARRAY_SIZE(sercom_pins[0].pins)) + shutdown("Invalid SERCOM bus"); + sercom_pins[sercom_id].pins[pin_type] = pin; +} +DECL_COMMAND(command_set_sercom_pin, + "set_sercom_pin sercom_id=%u pin_type=%u pin=%u"); + + +/**************************************************************** + * SPI dopo flag mapping + ****************************************************************/ + +struct sercom_spi_map { + uint8_t tx_pad, clk_pad, dopo; +}; + + +static const struct sercom_spi_map sercom_spi[] = { + { 0, 1, 0 }, + { 3, 1, 2 }, +#if CONFIG_MACH_SAMD21 + { 2, 3, 1 }, + { 0, 3, 3 }, +#endif +}; + +static uint8_t +sercom_lookup_spi_dopo(uint8_t tx_pad, uint8_t clk_pad) +{ + const struct sercom_spi_map *sm = sercom_spi; + for (; ; sm++) { + if (sm >= &sercom_spi[ARRAY_SIZE(sercom_spi)]) + shutdown("Invalid combination of TX pin and CLK pin"); + if (sm->tx_pad == tx_pad && sm->clk_pad == clk_pad) + return sm->dopo; + } +} + + +/**************************************************************** + * Pin setup + ****************************************************************/ + +uint32_t +sercom_spi_pins(uint32_t sercom_id) +{ + uint8_t tx_pin = sercom_pins[sercom_id].pins[TX_PIN]; + const struct sercom_pad *tx_sp = sercom_lookup_pad(sercom_id, tx_pin); + uint8_t rx_pin = sercom_pins[sercom_id].pins[RX_PIN]; + const struct sercom_pad *rx_sp = sercom_lookup_pad(sercom_id, rx_pin); + uint8_t clk_pin = sercom_pins[sercom_id].pins[CLK_PIN]; + const struct sercom_pad *clk_sp = sercom_lookup_pad(sercom_id, clk_pin); + + uint8_t dopo = sercom_lookup_spi_dopo(tx_sp->pad, clk_sp->pad); + if (rx_sp->pad == tx_sp->pad || rx_sp->pad == clk_sp->pad) + shutdown("Sercom RX pad collides with TX or CLK pad"); + + gpio_peripheral(tx_pin, tx_sp->ptype, 0); + gpio_peripheral(rx_pin, rx_sp->ptype, 0); + gpio_peripheral(clk_pin, clk_sp->ptype, 0); + return SERCOM_SPI_CTRLA_DIPO(rx_sp->pad) | SERCOM_SPI_CTRLA_DOPO(dopo); +} + +void +sercom_i2c_pins(uint32_t sercom_id) +{ + uint8_t tx_pin = sercom_pins[sercom_id].pins[TX_PIN]; + const struct sercom_pad *tx_sp = sercom_lookup_pad(sercom_id, tx_pin); + uint8_t clk_pin = sercom_pins[sercom_id].pins[CLK_PIN]; + const struct sercom_pad *clk_sp = sercom_lookup_pad(sercom_id, clk_pin); + + if (tx_sp->pad != 0 || clk_sp->pad != 1) + shutdown("TX pin not on PAD0 or CLK pin not on PAD1"); + + gpio_peripheral(tx_pin, tx_sp->ptype, 0); + gpio_peripheral(clk_pin, clk_sp->ptype, 0); +} diff --git a/src/atsamd/spi.c b/src/atsamd/spi.c index 2037f312..4bf281bc 100644 --- a/src/atsamd/spi.c +++ b/src/atsamd/spi.c @@ -1,5 +1,6 @@ // spi support on samd // +// Copyright (C) 2019 Florian Heilmann // Copyright (C) 2018-2019 Kevin O'Connor // // This file may be distributed under the terms of the GNU GPLv3 license. @@ -9,24 +10,14 @@ #include "gpio.h" // spi_setup #include "sched.h" // sched_shutdown -static void -spi_init(uint32_t ctrla, uint32_t baud) +void +spi_init(uint32_t bus, SercomSpi *ss, uint32_t ctrla, uint32_t baud) { - static int have_run_init; - if (have_run_init) + static int have_run_init[8]; + if (have_run_init[bus]) return; - have_run_init = 1; + have_run_init[bus] = 1; - // Setup clock - enable_pclock(SERCOM4_GCLK_ID_CORE, ID_SERCOM4); - - // Configure MISO, MOSI, SCK pins - gpio_peripheral(GPIO('A', 12), 'D', 0); - gpio_peripheral(GPIO('B', 10), 'D', 0); - gpio_peripheral(GPIO('B', 11), 'D', 0); - - // Configure spi - SercomSpi *ss = &SERCOM4->SPI; ss->CTRLA.reg = 0; ss->CTRLA.reg = ctrla & ~SERCOM_SPI_CTRLA_ENABLE; ss->CTRLB.reg = SERCOM_SPI_CTRLB_RXEN; @@ -37,24 +28,30 @@ spi_init(uint32_t ctrla, uint32_t baud) struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate) { - if (bus) +#ifdef SERCOM7 + if (bus > 7) +#else + if (bus > 5) +#endif shutdown("Invalid spi bus"); + uint32_t dipo_dopo = sercom_spi_pins(bus); uint32_t ctrla = (SERCOM_SPI_CTRLA_MODE(3) | (mode << SERCOM_SPI_CTRLA_CPHA_Pos) - | SERCOM_SPI_CTRLA_DIPO(0) - | SERCOM_SPI_CTRLA_DOPO(1) + | dipo_dopo | SERCOM_SPI_CTRLA_ENABLE); - uint32_t baud = get_pclock_frequency(SERCOM4_GCLK_ID_CORE) / (2 * rate) - 1; - spi_init(ctrla, baud); - return (struct spi_config){ .ctrla = ctrla, .baud = baud }; + Sercom *sercom = sercom_enable_pclock(bus); + SercomSpi *ss = &sercom->SPI; + uint32_t baud = sercom_get_pclock_frequency(bus) / (2 * rate) - 1; + spi_init(bus, ss, ctrla, baud); + return (struct spi_config){ .ss = ss, .ctrla = ctrla, .baud = baud }; } void spi_prepare(struct spi_config config) { uint32_t ctrla = config.ctrla, baud = config.baud; - SercomSpi *ss = &SERCOM4->SPI; + SercomSpi *ss = (SercomSpi *)config.ss; if (ctrla == ss->CTRLA.reg && baud == ss->BAUD.reg) return; ss->CTRLA.reg = ctrla & ~SERCOM_SPI_CTRLA_ENABLE; @@ -67,7 +64,7 @@ void spi_transfer(struct spi_config config, uint8_t receive_data , uint8_t len, uint8_t *data) { - SercomSpi *ss = &SERCOM4->SPI; + SercomSpi *ss = (SercomSpi *)config.ss; if (receive_data) { while (len--) { ss->DATA.reg = *data;