From 6256599a6dfc0c52a3c5e019c470d4cc46feeb82 Mon Sep 17 00:00:00 2001 From: Florian Heilmann Date: Sun, 13 Jan 2019 02:14:50 +0100 Subject: src: Rename source folders for atsam and atsamd architectures Signed-off-by: Florian Heilmann --- src/Kconfig | 8 +- src/atsam/Kconfig | 53 ++++++++ src/atsam/Makefile | 58 +++++++++ src/atsam/adc.c | 96 +++++++++++++++ src/atsam/gpio.c | 142 ++++++++++++++++++++++ src/atsam/gpio.h | 51 ++++++++ src/atsam/i2c.c | 175 +++++++++++++++++++++++++++ src/atsam/internal.h | 24 ++++ src/atsam/main.c | 77 ++++++++++++ src/atsam/sam4_cache.c | 16 +++ src/atsam/sam4_usb.c | 238 ++++++++++++++++++++++++++++++++++++ src/atsam/sam4e_afec.c | 203 +++++++++++++++++++++++++++++++ src/atsam/sam4s_sysinit.c | 65 ++++++++++ src/atsam/sam4s_timer.c | 118 ++++++++++++++++++ src/atsam/serial.c | 77 ++++++++++++ src/atsam/spi.c | 285 ++++++++++++++++++++++++++++++++++++++++++++ src/atsam/timer.c | 65 ++++++++++ src/atsam/usb_cdc_ep.h | 10 ++ src/atsamd/Kconfig | 57 +++++++++ src/atsamd/Makefile | 54 +++++++++ src/atsamd/adc.c | 121 +++++++++++++++++++ src/atsamd/clock.c | 77 ++++++++++++ src/atsamd/gpio.c | 141 ++++++++++++++++++++++ src/atsamd/gpio.h | 55 +++++++++ src/atsamd/hard_pwm.c | 102 ++++++++++++++++ src/atsamd/i2c.c | 114 ++++++++++++++++++ src/atsamd/internal.h | 14 +++ src/atsamd/main.c | 52 ++++++++ src/atsamd/serial.c | 61 ++++++++++ src/atsamd/spi.c | 92 ++++++++++++++ src/atsamd/timer.c | 66 ++++++++++ src/atsamd/usbserial.c | 246 ++++++++++++++++++++++++++++++++++++++ src/sam3/Kconfig | 53 -------- src/sam3/Makefile | 58 --------- src/sam3/adc.c | 96 --------------- src/sam3/gpio.c | 142 ---------------------- src/sam3/gpio.h | 51 -------- src/sam3/i2c.c | 175 --------------------------- src/sam3/internal.h | 24 ---- src/sam3/main.c | 77 ------------ src/sam3/sam4_cache.c | 16 --- src/sam3/sam4_usb.c | 238 ------------------------------------ src/sam3/sam4e_afec.c | 203 ------------------------------- src/sam3/sam4s_sysinit.c | 65 ---------- src/sam3/sam4s_timer.c | 118 ------------------ src/sam3/serial.c | 77 ------------ src/sam3/spi.c | 285 -------------------------------------------- src/sam3/timer.c | 65 ---------- src/sam3/usb_cdc_ep.h | 10 -- src/samd21/Kconfig | 51 -------- src/samd21/Makefile | 54 --------- src/samd21/adc.c | 121 ------------------- src/samd21/clock.c | 77 ------------ src/samd21/gpio.c | 141 ---------------------- src/samd21/gpio.h | 55 --------- src/samd21/hard_pwm.c | 102 ---------------- src/samd21/i2c.c | 114 ------------------ src/samd21/internal.h | 14 --- src/samd21/main.c | 52 -------- src/samd21/serial.c | 61 ---------- src/samd21/spi.c | 92 -------------- src/samd21/timer.c | 66 ---------- src/samd21/usbserial.c | 246 -------------------------------------- test/configs/sam3x8e.config | 2 +- test/configs/sam4e8e.config | 2 +- test/configs/sam4s8c.config | 2 +- test/configs/samd21.config | 1 + 67 files changed, 3013 insertions(+), 3006 deletions(-) create mode 100644 src/atsam/Kconfig create mode 100644 src/atsam/Makefile create mode 100644 src/atsam/adc.c create mode 100644 src/atsam/gpio.c create mode 100644 src/atsam/gpio.h create mode 100644 src/atsam/i2c.c create mode 100644 src/atsam/internal.h create mode 100644 src/atsam/main.c create mode 100644 src/atsam/sam4_cache.c create mode 100644 src/atsam/sam4_usb.c create mode 100644 src/atsam/sam4e_afec.c create mode 100644 src/atsam/sam4s_sysinit.c create mode 100644 src/atsam/sam4s_timer.c create mode 100644 src/atsam/serial.c create mode 100644 src/atsam/spi.c create mode 100644 src/atsam/timer.c create mode 100644 src/atsam/usb_cdc_ep.h create mode 100644 src/atsamd/Kconfig create mode 100644 src/atsamd/Makefile create mode 100644 src/atsamd/adc.c create mode 100644 src/atsamd/clock.c create mode 100644 src/atsamd/gpio.c create mode 100644 src/atsamd/gpio.h create mode 100644 src/atsamd/hard_pwm.c create mode 100644 src/atsamd/i2c.c create mode 100644 src/atsamd/internal.h create mode 100644 src/atsamd/main.c create mode 100644 src/atsamd/serial.c create mode 100644 src/atsamd/spi.c create mode 100644 src/atsamd/timer.c create mode 100644 src/atsamd/usbserial.c delete mode 100644 src/sam3/Kconfig delete mode 100644 src/sam3/Makefile delete mode 100644 src/sam3/adc.c delete mode 100644 src/sam3/gpio.c delete mode 100644 src/sam3/gpio.h delete mode 100644 src/sam3/i2c.c delete mode 100644 src/sam3/internal.h delete mode 100644 src/sam3/main.c delete mode 100644 src/sam3/sam4_cache.c delete mode 100644 src/sam3/sam4_usb.c delete mode 100644 src/sam3/sam4e_afec.c delete mode 100644 src/sam3/sam4s_sysinit.c delete mode 100644 src/sam3/sam4s_timer.c delete mode 100644 src/sam3/serial.c delete mode 100644 src/sam3/spi.c delete mode 100644 src/sam3/timer.c delete mode 100644 src/sam3/usb_cdc_ep.h delete mode 100644 src/samd21/Kconfig delete mode 100644 src/samd21/Makefile delete mode 100644 src/samd21/adc.c delete mode 100644 src/samd21/clock.c delete mode 100644 src/samd21/gpio.c delete mode 100644 src/samd21/gpio.h delete mode 100644 src/samd21/hard_pwm.c delete mode 100644 src/samd21/i2c.c delete mode 100644 src/samd21/internal.h delete mode 100644 src/samd21/main.c delete mode 100644 src/samd21/serial.c delete mode 100644 src/samd21/spi.c delete mode 100644 src/samd21/timer.c delete mode 100644 src/samd21/usbserial.c diff --git a/src/Kconfig b/src/Kconfig index 16ce3531..6add97a3 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -13,9 +13,9 @@ choice prompt "Micro-controller Architecture" config MACH_AVR bool "Atmega AVR" - config MACH_SAM3 + config MACH_ATSAM bool "SAM3/SAM4 (Due and Duet)" - config MACH_SAMD21 + config MACH_ATSAMD bool "SAMD21 (Arduino Zero)" config MACH_LPC176X bool "LPC176x (Smoothieboard)" @@ -30,8 +30,8 @@ choice endchoice source "src/avr/Kconfig" -source "src/sam3/Kconfig" -source "src/samd21/Kconfig" +source "src/atsam/Kconfig" +source "src/atsamd/Kconfig" source "src/lpc176x/Kconfig" source "src/stm32f1/Kconfig" source "src/pru/Kconfig" diff --git a/src/atsam/Kconfig b/src/atsam/Kconfig new file mode 100644 index 00000000..0ea82241 --- /dev/null +++ b/src/atsam/Kconfig @@ -0,0 +1,53 @@ +# Kconfig settings for Atmel SAM processors + +if MACH_ATSAM + +config ATSAM_SELECT + bool + default y + select HAVE_GPIO + select HAVE_GPIO_ADC + select HAVE_GPIO_I2C + select HAVE_GPIO_SPI + select HAVE_GPIO_BITBANGING + +config BOARD_DIRECTORY + string + default "atsam" + +choice + prompt "Processor model" + config MACH_SAM3X8E + bool "SAM3x8e (Arduino Due)" + config MACH_SAM4S8C + bool "SAM4s8c (Duet Maestro)" + config MACH_SAM4E8E + bool "SAM4e8e (Duet Wifi/Eth)" +endchoice + +config MCU + string + default "sam3x8e" if MACH_SAM3X8E + default "sam4s8c" if MACH_SAM4S8C + default "sam4e8e" if MACH_SAM4E8E + +config CLOCK_FREQ + int + default 42000000 if MACH_SAM3X8E # 84000000/2 + default 15000000 if MACH_SAM4S8C # 120000000/8 + default 60000000 if MACH_SAM4E8E # 120000000/2 + +config USBSERIAL + depends on MACH_SAM4S8C || MACH_SAM4E8E + bool "Use USB for communication (instead of serial)" + default y +config SERIAL + depends on !USBSERIAL + bool + default y +config SERIAL_BAUD + depends on SERIAL + int "Baud rate for serial port" + default 250000 + +endif diff --git a/src/atsam/Makefile b/src/atsam/Makefile new file mode 100644 index 00000000..4aece952 --- /dev/null +++ b/src/atsam/Makefile @@ -0,0 +1,58 @@ +# Additional ATSAM build rules + +# Setup the toolchain +CROSS_PREFIX=arm-none-eabi- + +dirs-y += src/atsam src/generic +dirs-$(CONFIG_MACH_SAM3X8E) += lib/sam3x/gcc/gcc +dirs-$(CONFIG_MACH_SAM4S8C) += lib/sam4s/gcc/gcc +dirs-$(CONFIG_MACH_SAM4E8E) += lib/sam4e/gcc/gcc + +CFLAGS-$(CONFIG_MACH_SAM3X8E) += -mcpu=cortex-m3 -falign-loops=16 +CFLAGS-$(CONFIG_MACH_SAM3X8E) += -Ilib/sam3x/include -D__SAM3X8E__ +CFLAGS-$(CONFIG_MACH_SAM4S8C) += -mcpu=cortex-m4 +CFLAGS-$(CONFIG_MACH_SAM4S8C) += -Ilib/sam4s/include -D__SAM4S8C__ +CFLAGS-$(CONFIG_MACH_SAM4E8E) += -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard +CFLAGS-$(CONFIG_MACH_SAM4E8E) += -Ilib/sam4e/include -D__SAM4E8E__ +CFLAGS += -mthumb $(CFLAGS-y) -Ilib/cmsis-core + +eflags-$(CONFIG_MACH_SAM3X8E) += -Llib/sam3x/gcc/gcc +eflags-$(CONFIG_MACH_SAM3X8E) += -T lib/sam3x/gcc/gcc/sam3x8e_flash.ld +eflags-$(CONFIG_MACH_SAM4S8C) += -Llib/sam4s/gcc/gcc +eflags-$(CONFIG_MACH_SAM4S8C) += -T lib/sam4s/gcc/gcc/sam4s8c_flash.ld +eflags-$(CONFIG_MACH_SAM4E8E) += -Llib/sam4e/gcc/gcc +eflags-$(CONFIG_MACH_SAM4E8E) += -T lib/sam4e/gcc/gcc/sam4e8e_flash.ld +CFLAGS_klipper.elf += $(eflags-y) --specs=nano.specs --specs=nosys.specs + +# Add source files +src-y += atsam/main.c atsam/gpio.c atsam/i2c.c atsam/spi.c +src-y += generic/crc16_ccitt.c generic/alloc.c +src-y += generic/armcm_irq.c generic/timer_irq.c +src-$(CONFIG_USBSERIAL) += atsam/sam4_usb.c generic/usb_cdc.c +src-$(CONFIG_SERIAL) += atsam/serial.c generic/serial_irq.c +src-$(CONFIG_MACH_SAM3X8E) += atsam/adc.c atsam/timer.c +src-$(CONFIG_MACH_SAM3X8E) += ../lib/sam3x/gcc/system_sam3xa.c +src-$(CONFIG_MACH_SAM3X8E) += ../lib/sam3x/gcc/gcc/startup_sam3xa.c +src-$(CONFIG_MACH_SAM4S8C) += atsam/adc.c atsam/sam4s_timer.c atsam/sam4s_sysinit.c +src-$(CONFIG_MACH_SAM4S8C) += ../lib/sam4s/gcc/gcc/startup_sam4s.c +src-$(CONFIG_MACH_SAM4E8E) += atsam/sam4e_afec.c atsam/timer.c atsam/sam4_cache.c +src-$(CONFIG_MACH_SAM4E8E) += ../lib/sam4e/gcc/system_sam4e.c +src-$(CONFIG_MACH_SAM4E8E) += ../lib/sam4e/gcc/gcc/startup_sam4e.c + +# Build the additional hex output file +target-y += $(OUT)klipper.bin + +$(OUT)klipper.bin: $(OUT)klipper.elf + @echo " Creating bin file $@" + $(Q)$(OBJCOPY) -O binary $< $@ + +# Flash rules +lib/bossac/bin/bossac: + @echo " Building bossac" + $(Q)make -C lib/bossac bin/bossac + +flash: $(OUT)klipper.bin lib/bossac/bin/bossac + @echo " Flashing $^ to $(FLASH_DEVICE) via bossac" + $(Q)if [ -z $(FLASH_DEVICE) ]; then echo "Please specify FLASH_DEVICE"; exit 1; fi + $(Q)lib/bossac/bin/bossac -U -p "$(FLASH_DEVICE)" -a -e -w $(OUT)klipper.bin -v -b + $(Q)lib/bossac/bin/bossac -p "$(FLASH_DEVICE)" -b -R > /dev/null 2>&1 || true diff --git a/src/atsam/adc.c b/src/atsam/adc.c new file mode 100644 index 00000000..425d60a9 --- /dev/null +++ b/src/atsam/adc.c @@ -0,0 +1,96 @@ +// Analog to digital support +// +// Copyright (C) 2016-2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_CLOCK_FREQ +#include "board/irq.h" // irq_save +#include "command.h" // shutdown +#include "compiler.h" // ARRAY_SIZE +#include "gpio.h" // gpio_adc_setup +#include "internal.h" // GPIO +#include "sched.h" // sched_shutdown + +static const uint8_t adc_pins[] = { +#if CONFIG_MACH_SAM3X8E + GPIO('A', 2), GPIO('A', 3), GPIO('A', 4), GPIO('A', 6), + GPIO('A', 22), GPIO('A', 23), GPIO('A', 24), GPIO('A', 16), + GPIO('B', 12), GPIO('B', 13), GPIO('B', 17), GPIO('B', 18), + GPIO('B', 19), GPIO('B', 20) +#elif CONFIG_MACH_SAM4S8C + GPIO('A', 17), GPIO('A', 18), GPIO('A', 19), GPIO('A', 20), + GPIO('B', 0), GPIO('B', 1), GPIO('B', 2), GPIO('B', 3), + GPIO('A', 21), GPIO('A', 22), GPIO('C', 13), GPIO('C', 15), + GPIO('C', 12), GPIO('C', 29), GPIO('C', 30) +#endif +}; + +#define ADC_FREQ_MAX 20000000 +DECL_CONSTANT(ADC_MAX, 4095); + +struct gpio_adc +gpio_adc_setup(uint8_t pin) +{ + // Find pin in adc_pins table + int chan; + for (chan=0; ; chan++) { + if (chan >= ARRAY_SIZE(adc_pins)) + shutdown("Not a valid ADC pin"); + if (adc_pins[chan] == pin) + break; + } + + if (!is_enabled_pclock(ID_ADC)) { + // Setup ADC + enable_pclock(ID_ADC); + uint32_t prescal = SystemCoreClock / (2 * ADC_FREQ_MAX) - 1; + ADC->ADC_MR = (ADC_MR_PRESCAL(prescal) + | ADC_MR_STARTUP_SUT768 + | ADC_MR_TRANSFER(1)); + } + return (struct gpio_adc){ .chan = 1 << chan }; +} + +// Try to sample a value. Returns zero if sample ready, otherwise +// returns the number of clock ticks the caller should wait before +// retrying this function. +uint32_t +gpio_adc_sample(struct gpio_adc g) +{ + uint32_t chsr = ADC->ADC_CHSR & 0xffff; + if (!chsr) { + // Start sample + ADC->ADC_CHER = g.chan; + ADC->ADC_CR = ADC_CR_START; + goto need_delay; + } + if (chsr != g.chan) + // Sampling in progress on another channel + goto need_delay; + if (!(ADC->ADC_ISR & ADC_ISR_DRDY)) + // Conversion still in progress + goto need_delay; + // Conversion ready + return 0; +need_delay: + return ADC_FREQ_MAX * 1000ULL / CONFIG_CLOCK_FREQ; +} + +// Read a value; use only after gpio_adc_sample() returns zero +uint16_t +gpio_adc_read(struct gpio_adc g) +{ + ADC->ADC_CHDR = g.chan; + return ADC->ADC_LCDR; +} + +// Cancel a sample that may have been started with gpio_adc_sample() +void +gpio_adc_cancel_sample(struct gpio_adc g) +{ + irqstatus_t flag = irq_save(); + if ((ADC->ADC_CHSR & 0xffff) == g.chan) + gpio_adc_read(g); + irq_restore(flag); +} diff --git a/src/atsam/gpio.c b/src/atsam/gpio.c new file mode 100644 index 00000000..7e6a1dab --- /dev/null +++ b/src/atsam/gpio.c @@ -0,0 +1,142 @@ +// GPIO functions on sam3/sam4 +// +// Copyright (C) 2016-2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "board/irq.h" // irq_save +#include "command.h" // shutdown +#include "compiler.h" // ARRAY_SIZE +#include "gpio.h" // gpio_out_setup +#include "internal.h" // gpio_peripheral +#include "sched.h" // sched_shutdown + +static Pio * const digital_regs[] = { +#if CONFIG_MACH_SAM3X8E + PIOA, PIOB, PIOC, PIOD +#elif CONFIG_MACH_SAM4S8C + PIOA, PIOB, PIOC +#elif CONFIG_MACH_SAM4E8E + PIOA, PIOB, PIOC, PIOD, PIOE +#endif +}; + + +/**************************************************************** + * Pin multiplexing + ****************************************************************/ + +void +gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up) +{ + uint32_t bank = GPIO2PORT(gpio), bit = GPIO2BIT(gpio), pt = ptype - 'A'; + Pio *regs = digital_regs[bank]; + +#if CONFIG_MACH_SAM3X8E + regs->PIO_ABSR = (regs->PIO_ABSR & ~bit) | (pt & 0x01 ? bit : 0); +#else + regs->PIO_ABCDSR[0] = (regs->PIO_ABCDSR[0] & ~bit) | (pt & 0x01 ? bit : 0); + regs->PIO_ABCDSR[1] = (regs->PIO_ABCDSR[1] & ~bit) | (pt & 0x02 ? bit : 0); +#endif + + if (pull_up > 0) + regs->PIO_PUER = bit; + else + regs->PIO_PUDR = bit; + regs->PIO_PDR = bit; +} + + +/**************************************************************** + * General Purpose Input Output (GPIO) pins + ****************************************************************/ + +struct gpio_out +gpio_out_setup(uint8_t pin, uint8_t val) +{ + if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs)) + goto fail; + Pio *regs = digital_regs[GPIO2PORT(pin)]; + struct gpio_out g = { .regs=regs, .bit=GPIO2BIT(pin) }; + gpio_out_reset(g, val); + return g; +fail: + shutdown("Not an output pin"); +} + +void +gpio_out_reset(struct gpio_out g, uint8_t val) +{ + Pio *regs = g.regs; + irqstatus_t flag = irq_save(); + if (val) + regs->PIO_SODR = g.bit; + else + regs->PIO_CODR = g.bit; + regs->PIO_OER = g.bit; + regs->PIO_OWER = g.bit; + regs->PIO_PER = g.bit; + regs->PIO_PUDR = g.bit; + irq_restore(flag); +} + +void +gpio_out_toggle_noirq(struct gpio_out g) +{ + Pio *regs = g.regs; + regs->PIO_ODSR ^= g.bit; +} + +void +gpio_out_toggle(struct gpio_out g) +{ + irqstatus_t flag = irq_save(); + gpio_out_toggle_noirq(g); + irq_restore(flag); +} + +void +gpio_out_write(struct gpio_out g, uint8_t val) +{ + Pio *regs = g.regs; + if (val) + regs->PIO_SODR = g.bit; + else + regs->PIO_CODR = g.bit; +} + + +struct gpio_in +gpio_in_setup(uint8_t pin, int8_t pull_up) +{ + if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs)) + goto fail; + uint32_t port = GPIO2PORT(pin); + enable_pclock(ID_PIOA + port); + struct gpio_in g = { .regs=digital_regs[port], .bit=GPIO2BIT(pin) }; + gpio_in_reset(g, pull_up); + return g; +fail: + shutdown("Not an input pin"); +} + +void +gpio_in_reset(struct gpio_in g, int8_t pull_up) +{ + Pio *regs = g.regs; + irqstatus_t flag = irq_save(); + if (pull_up) + regs->PIO_PUER = g.bit; + else + regs->PIO_PUDR = g.bit; + regs->PIO_ODR = g.bit; + regs->PIO_PER = g.bit; + irq_restore(flag); +} + +uint8_t +gpio_in_read(struct gpio_in g) +{ + Pio *regs = g.regs; + return !!(regs->PIO_PDSR & g.bit); +} diff --git a/src/atsam/gpio.h b/src/atsam/gpio.h new file mode 100644 index 00000000..e106e094 --- /dev/null +++ b/src/atsam/gpio.h @@ -0,0 +1,51 @@ +#ifndef __SAM3_GPIO_H +#define __SAM3_GPIO_H + +#include // uint32_t + +struct gpio_out { + void *regs; + uint32_t bit; +}; +struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val); +void gpio_out_reset(struct gpio_out g, uint8_t val); +void gpio_out_toggle_noirq(struct gpio_out g); +void gpio_out_toggle(struct gpio_out g); +void gpio_out_write(struct gpio_out g, uint8_t val); + +struct gpio_in { + void *regs; + uint32_t bit; +}; +struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up); +void gpio_in_reset(struct gpio_in g, int8_t pull_up); +uint8_t gpio_in_read(struct gpio_in g); + +struct gpio_adc { + uint32_t chan; +}; +struct gpio_adc gpio_adc_setup(uint8_t pin); +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 *spidev; + uint32_t cfg; +}; +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); + +struct i2c_config { + void *twi; + uint8_t addr; +}; + +struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); +void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); + +#endif // gpio.h diff --git a/src/atsam/i2c.c b/src/atsam/i2c.c new file mode 100644 index 00000000..910471fd --- /dev/null +++ b/src/atsam/i2c.c @@ -0,0 +1,175 @@ +// SAM4 I2C Port +// +// Copyright (C) 2018 Florian Heilmann +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "board/misc.h" // timer_from_us +#include "command.h" // shutdown +#include "gpio.h" // i2c_setup +#include "internal.h" // gpio_peripheral +#include "sched.h" // sched_shutdown + +// I2C pin definitions +#if CONFIG_MACH_SAM3X8E +#define TWI0_SCL_GPIO GPIO('A', 18) +#define TWI0_SDA_GPIO GPIO('A', 17) +#define TWI1_SCL_GPIO GPIO('B', 13) +#define TWI1_SDA_GPIO GPIO('B', 12) +#elif CONFIG_MACH_SAM4S8C || CONFIG_MACH_SAM4E8E +#define TWI0_SCL_GPIO GPIO('A', 4) +#define TWI0_SDA_GPIO GPIO('A', 3) +#define TWI1_SCL_GPIO GPIO('B', 5) +#define TWI1_SDA_GPIO GPIO('B', 4) +#endif + +static void +i2c_init(Twi *p_twi, uint32_t rate) +{ + enable_pclock(p_twi == TWI0 ? ID_TWI0 : ID_TWI1); + if (p_twi == TWI0) { + gpio_peripheral(TWI0_SCL_GPIO, 'A', 0); + gpio_peripheral(TWI0_SDA_GPIO, 'A', 0); + } else { + gpio_peripheral(TWI1_SCL_GPIO, 'A', 0); + gpio_peripheral(TWI1_SDA_GPIO, 'A', 0); + } + p_twi->TWI_IDR = 0xFFFFFFFF; + (void)p_twi->TWI_SR; + p_twi->TWI_CR = TWI_CR_SWRST; + (void)p_twi->TWI_RHR; + p_twi->TWI_CR = TWI_CR_MSDIS; + p_twi->TWI_CR = TWI_CR_SVDIS; + p_twi->TWI_CR = TWI_CR_MSEN; + + uint32_t cldiv = 0; + uint32_t chdiv = 0; + uint32_t ckdiv = 0; + + cldiv = SystemCoreClock / ((rate > 384000 ? 384000 : rate) * 4) - 4; + + while((cldiv > 255) && (ckdiv < 7)) { + ckdiv++; + cldiv /= 2; + } + + if (rate > 348000) { + chdiv = SystemCoreClock / ((2 * rate - 384000) * 4) - 4; + while((chdiv > 255) && (ckdiv < 7)) { + ckdiv++; + chdiv /= 2; + } + } else { + chdiv = cldiv; + } + p_twi->TWI_CWGR = TWI_CWGR_CLDIV(cldiv) | \ + TWI_CWGR_CHDIV(chdiv) | \ + TWI_CWGR_CKDIV(ckdiv); +} + +static uint32_t +addr_to_u32(uint8_t addr_len, uint8_t *addr) +{ + uint32_t address = addr[0]; + if (addr_len > 1) { + address <<= 8; + address |= addr[1]; + } + if (addr_len > 2) { + address <<= 8; + address |= addr[2]; + } + if (addr_len > 3) { + shutdown("Addresses larger than 3 bytes are not supported"); + } + return address; +} + +struct i2c_config +i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) +{ + if ((bus > 1) | (rate > 400000)) + shutdown("Invalid i2c_setup parameters!"); + Twi *p_twi = (bus == 0) ? TWI0 : TWI1; + i2c_init(p_twi, rate); + return (struct i2c_config){ .twi=p_twi, .addr=addr}; +} + +void +i2c_write(struct i2c_config config, + uint8_t write_len, uint8_t *write) +{ + Twi *p_twi = config.twi; + uint32_t status; + uint32_t bytes_to_send = write_len; + p_twi->TWI_MMR = TWI_MMR_DADR(config.addr); + for(;;) { + status = p_twi->TWI_SR; + if (status & TWI_SR_NACK) + shutdown("I2C NACK error encountered!"); + if (!(status & TWI_SR_TXRDY)) + continue; + if (!bytes_to_send) + break; + p_twi->TWI_THR = *write++; + bytes_to_send--; + } + p_twi->TWI_CR = TWI_CR_STOP; + while(!(p_twi->TWI_SR& TWI_SR_TXCOMP)) { + } +} + +static void +i2c_wait(Twi* p_twi, uint32_t bit, uint32_t timeout) +{ + for (;;) { + uint32_t flags = p_twi->TWI_SR; + if (flags & bit) + break; + if (!timer_is_before(timer_read_time(), timeout)) + shutdown("I2C timeout occured"); + } +} + +void +i2c_read(struct i2c_config config, + uint8_t reg_len, uint8_t *reg, + uint8_t read_len, uint8_t *read) +{ + Twi *p_twi = config.twi; + uint32_t status; + uint32_t bytes_to_send=read_len; + uint8_t stop = 0; + p_twi->TWI_MMR = 0; + p_twi->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_DADR(config.addr) | + ((reg_len << TWI_MMR_IADRSZ_Pos) & + TWI_MMR_IADRSZ_Msk); + p_twi->TWI_IADR = 0; + p_twi->TWI_IADR = addr_to_u32(reg_len, reg); + if (bytes_to_send == 1) { + p_twi->TWI_CR = TWI_CR_START | TWI_CR_STOP; + stop = 1; + } else { + p_twi->TWI_CR = TWI_CR_START; + stop = 0; + } + while (bytes_to_send > 0) { + status = p_twi->TWI_SR; + if (status & TWI_SR_NACK) { + shutdown("I2C NACK error encountered!"); + } + if (bytes_to_send == 1 && !stop) { + p_twi->TWI_CR = TWI_CR_STOP; + stop = 1; + } + i2c_wait(p_twi, TWI_SR_RXRDY, timer_read_time() + timer_from_us(5000)); + if (!(status & TWI_SR_RXRDY)) { + continue; + } + *read++ = p_twi->TWI_RHR; + bytes_to_send--; + } + while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) {} + (void)p_twi->TWI_SR; +} diff --git a/src/atsam/internal.h b/src/atsam/internal.h new file mode 100644 index 00000000..dd94a609 --- /dev/null +++ b/src/atsam/internal.h @@ -0,0 +1,24 @@ +#ifndef __SAM3_INTERNAL_H +#define __SAM3_INTERNAL_H +// Local definitions for sam3/sam4 code + +#include // uint32_t +#include "autoconf.h" // CONFIG_MACH_SAM3X8E + +#if CONFIG_MACH_SAM3X8E +#include "sam3x8e.h" +#elif CONFIG_MACH_SAM4S8C +#include "sam4s.h" +#elif CONFIG_MACH_SAM4E8E +#include "sam4e.h" +#endif + +#define GPIO(PORT, NUM) (((PORT)-'A') * 32 + (NUM)) +#define GPIO2PORT(PIN) ((PIN) / 32) +#define GPIO2BIT(PIN) (1<<((PIN) % 32)) + +void gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up); +int is_enabled_pclock(uint32_t id); +void enable_pclock(uint32_t id); + +#endif // internal.h diff --git a/src/atsam/main.c b/src/atsam/main.c new file mode 100644 index 00000000..1b9375db --- /dev/null +++ b/src/atsam/main.c @@ -0,0 +1,77 @@ +// Main starting point for SAM3/SAM4 boards +// +// Copyright (C) 2016-2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "board/irq.h" // irq_disable +#include "command.h" // DECL_CONSTANT +#include "internal.h" // WDT +#include "sched.h" // sched_main + +DECL_CONSTANT(MCU, CONFIG_MCU); + + +/**************************************************************** + * watchdog handler + ****************************************************************/ + +void +watchdog_reset(void) +{ + WDT->WDT_CR = 0xA5000001; +} +DECL_TASK(watchdog_reset); + +void +watchdog_init(void) +{ + uint32_t timeout = 500 * 32768 / 128 / 1000; // 500ms timeout + WDT->WDT_MR = WDT_MR_WDRSTEN | WDT_MR_WDV(timeout) | WDT_MR_WDD(timeout); +} +DECL_INIT(watchdog_init); + + +/**************************************************************** + * misc functions + ****************************************************************/ + +// Check if a peripheral clock has been enabled +int +is_enabled_pclock(uint32_t id) +{ + if (id < 32) + return !!(PMC->PMC_PCSR0 & (1 << id)); + else + return !!(PMC->PMC_PCSR1 & (1 << (id - 32))); +} + +// Enable a peripheral clock +void +enable_pclock(uint32_t id) +{ + if (id < 32) + PMC->PMC_PCER0 = 1 << id; + else + PMC->PMC_PCER1 = 1 << (id - 32); +} + +void +command_reset(uint32_t *args) +{ + irq_disable(); + RSTC->RSTC_CR = ((0xA5 << RSTC_CR_KEY_Pos) | RSTC_CR_PROCRST + | RSTC_CR_PERRST); + for (;;) + ; +} +DECL_COMMAND_FLAGS(command_reset, HF_IN_SHUTDOWN, "reset"); + +// Main entry point +int +main(void) +{ + SystemInit(); + sched_main(); + return 0; +} diff --git a/src/atsam/sam4_cache.c b/src/atsam/sam4_cache.c new file mode 100644 index 00000000..75459660 --- /dev/null +++ b/src/atsam/sam4_cache.c @@ -0,0 +1,16 @@ +// SAM4 cache enable +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "sam4e.h" // CMCC +#include "sched.h" // DECL_INIT + +void +sam4_cache_init(void) +{ + if (!(CMCC->CMCC_SR & CMCC_SR_CSTS)) + CMCC->CMCC_CTRL = CMCC_CTRL_CEN; +} +DECL_INIT(sam4_cache_init); diff --git a/src/atsam/sam4_usb.c b/src/atsam/sam4_usb.c new file mode 100644 index 00000000..ecf8e544 --- /dev/null +++ b/src/atsam/sam4_usb.c @@ -0,0 +1,238 @@ +// Hardware interface to SAM4 USB Device Port +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include // NULL +#include "board/irq.h" // irq_disable +#include "board/usb_cdc.h" // usb_notify_ep0 +#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN +#include "internal.h" // UDP +#include "sched.h" // DECL_INIT + +#define CSR_EP0 (UDP_CSR_EPTYPE_CTRL | UDP_CSR_EPEDS) +#define CSR_ACM (UDP_CSR_EPTYPE_INT_IN | UDP_CSR_EPEDS) +#define CSR_BULK_OUT (UDP_CSR_EPTYPE_BULK_OUT | UDP_CSR_EPEDS) +#define CSR_BULK_IN (UDP_CSR_EPTYPE_BULK_IN | UDP_CSR_EPEDS) + +static void +usb_write_packet(uint32_t ep, const uint8_t *data, uint32_t len) +{ + while (len--) + UDP->UDP_FDR[ep] = *data++; +} + +static uint32_t +usb_read_packet(uint32_t ep, uint32_t csr, uint8_t *data, uint32_t max_len) +{ + uint32_t pk_len = (csr & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos; + uint32_t len = pk_len < max_len ? pk_len : max_len, orig_len = len; + while (len--) + *data++ = UDP->UDP_FDR[ep]; + return orig_len; +} + +int_fast8_t +usb_read_bulk_out(void *data, uint_fast8_t max_len) +{ + static uint32_t next_bk = UDP_CSR_RX_DATA_BK0; + const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT + | UDP_CSR_TXCOMP); + uint32_t csr = UDP->UDP_CSR[USB_CDC_EP_BULK_OUT]; + uint32_t bk = csr & (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); + if (!bk) { + // Not ready to receive data + if (csr & other_irqs) + UDP->UDP_CSR[USB_CDC_EP_BULK_OUT] = ( + CSR_BULK_OUT | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); + UDP->UDP_IER = 1<UDP_CSR[USB_CDC_EP_BULK_OUT] = CSR_BULK_OUT | next_bk; + return len; +} + +int_fast8_t +usb_send_bulk_in(void *data, uint_fast8_t len) +{ + const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT + | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); + uint32_t csr = UDP->UDP_CSR[USB_CDC_EP_BULK_IN]; + if (csr & UDP_CSR_TXPKTRDY) { + // Not ready to send + if (csr & other_irqs) + UDP->UDP_CSR[USB_CDC_EP_BULK_IN] = CSR_BULK_IN | UDP_CSR_TXCOMP; + UDP->UDP_IER = 1<UDP_CSR[USB_CDC_EP_BULK_IN] = CSR_BULK_IN | UDP_CSR_TXPKTRDY; + return len; +} + +int_fast8_t +usb_read_ep0(void *data, uint_fast8_t max_len) +{ + const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT + | UDP_CSR_TXCOMP | UDP_CSR_RX_DATA_BK1); + uint32_t csr = UDP->UDP_CSR[0]; + if (csr & other_irqs) + return -2; + if (!(csr & UDP_CSR_RX_DATA_BK0)) { + // Not ready to receive data + UDP->UDP_IER = 1<<0; + return -1; + } + uint32_t len = usb_read_packet(0, csr, data, max_len); + if (UDP->UDP_CSR[0] & other_irqs) + return -2; + UDP->UDP_CSR[0] = CSR_EP0 | other_irqs; + return len; +} + +int_fast8_t +usb_read_ep0_setup(void *data, uint_fast8_t max_len) +{ + const uint32_t other_irqs = (UDP_CSR_STALLSENT | UDP_CSR_TXCOMP + | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); + uint32_t csr = UDP->UDP_CSR[0]; + if (!(csr & UDP_CSR_RXSETUP)) { + // No data ready to read + if (csr & other_irqs) + UDP->UDP_CSR[0] = CSR_EP0 | UDP_CSR_RXSETUP; + UDP->UDP_IER = 1<<0; + return -1; + } + uint32_t len = usb_read_packet(0, csr, data, max_len); + uint32_t dir = *(uint8_t*)data & 0x80 ? UDP_CSR_DIR : 0; + UDP->UDP_CSR[0] = CSR_EP0 | dir; + return len; +} + +int_fast8_t +usb_send_ep0(const void *data, uint_fast8_t len) +{ + const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT + | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); + uint32_t csr = UDP->UDP_CSR[0]; + if (csr & other_irqs) + return -2; + if (csr & UDP_CSR_TXPKTRDY) { + // Not ready to send + UDP->UDP_IER = 1<<0; + return -1; + } + usb_write_packet(0, data, len); + uint32_t dir = csr & UDP_CSR_DIR; + UDP->UDP_CSR[0] = CSR_EP0 | dir | UDP_CSR_TXPKTRDY | other_irqs; + return len; +} + +void +usb_stall_ep0(void) +{ + UDP->UDP_CSR[0] = CSR_EP0 | UDP_CSR_FORCESTALL; + UDP->UDP_IER = 1<<0; +} + +static uint32_t set_address; + +void +usb_set_address(uint_fast8_t addr) +{ + set_address = addr | UDP_FADDR_FEN; + usb_send_ep0(NULL, 0); + UDP->UDP_IER = 1<<0; +} + +void +usb_set_configure(void) +{ + UDP->UDP_CSR[USB_CDC_EP_ACM] = CSR_ACM; + UDP->UDP_CSR[USB_CDC_EP_BULK_OUT] = CSR_BULK_OUT; + UDP->UDP_CSR[USB_CDC_EP_BULK_IN] = CSR_BULK_IN; + UDP->UDP_GLB_STAT |= UDP_GLB_STAT_CONFG; +} + +#if CONFIG_MACH_SAM4S8C +#define EFC_HW EFC0 +#elif CONFIG_MACH_SAM4E8E +#define EFC_HW EFC +#endif + +void noinline __aligned(16) // align for predictable flash code access +usb_request_bootloader(void) +{ + irq_disable(); + // Request boot from ROM (instead of boot from flash) + while ((EFC_HW->EEFC_FSR & EEFC_FSR_FRDY) == 0) + ; + EFC_HW->EEFC_FCR = (EEFC_FCR_FCMD_CGPB | EEFC_FCR_FARG(1) + | EEFC_FCR_FKEY_PASSWD); + while ((EFC_HW->EEFC_FSR & EEFC_FSR_FRDY) == 0) + ; + // Reboot + RSTC->RSTC_CR = RSTC_CR_KEY(0xA5) | RSTC_CR_PROCRST | RSTC_CR_PERRST; + for (;;) + ; +} + +void +usbserial_init(void) +{ + // Enable clocks + enable_pclock(ID_UDP); + PMC->PMC_USB = PMC_USB_USBDIV(5 - 1); // PLLA=240Mhz; divide by 5 for 48Mhz + PMC->PMC_SCER = PMC_SCER_UDP; + + // Enable USB pullup + UDP->UDP_TXVC = UDP_TXVC_PUON | UDP_TXVC_TXVDIS; + + // Enable interrupts + UDP->UDP_ICR = 0xffffffff; + NVIC_SetPriority(UDP_IRQn, 1); + NVIC_EnableIRQ(UDP_IRQn); +} +DECL_INIT(usbserial_init); + +// Configure endpoint 0 after usb reset completes +static void +handle_end_reset(void) +{ + UDP->UDP_ICR = UDP_ISR_ENDBUSRES; + + UDP->UDP_CSR[0] = CSR_EP0; + UDP->UDP_IER = 1<<0; + + UDP->UDP_TXVC = UDP_TXVC_PUON; +} + +void __visible +UDP_Handler(void) +{ + uint32_t s = UDP->UDP_ISR; + UDP->UDP_IDR = s; + if (s & UDP_ISR_ENDBUSRES) + handle_end_reset(); + if (s & UDP_ISR_RXRSM) + UDP->UDP_ICR = UDP_ISR_RXRSM; + if (s & (1<<0)) { + usb_notify_ep0(); + + if (set_address && UDP->UDP_CSR[0] & UDP_CSR_TXCOMP) { + // Ack from set_address command sent - now update address + UDP->UDP_FADDR = set_address; + UDP->UDP_GLB_STAT |= UDP_GLB_STAT_FADDEN; + set_address = 0; + } + } + if (s & (1< +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_CLOCK_FREQ +#include "command.h" // shutdown +#include "gpio.h" // gpio_adc_setup +#include "internal.h" // GPIO +#include "sam4e.h" // AFEC0 +#include "sched.h" // sched_shutdown + +static const uint8_t afec_pins[] = { + //remove first channel, since it offsets the channel number: GPIO('A', 8), + GPIO('A', 17), GPIO('A', 18), GPIO('A', 19), + GPIO('A', 20), GPIO('B', 0), GPIO('B', 1), GPIO('C', 13), + GPIO('C', 15), GPIO('C', 12), GPIO('C', 29), GPIO('C', 30), + GPIO('C', 31), GPIO('C', 26), GPIO('C', 27), GPIO('C',0), + // AFEC1 + GPIO('B', 2), GPIO('B', 3), GPIO('A', 21), GPIO('A', 22), + GPIO('C', 1), GPIO('C', 2), GPIO('C', 3), GPIO('C', 4), +}; + +#define AFEC1_START 15 // The first 15 pins are on afec0 + +static inline struct gpio_adc +pin_to_gpio_adc(uint8_t pin) +{ + int chan; + for (chan=0; ; chan++) { + if (chan >= ARRAY_SIZE(afec_pins)) + shutdown("Not a valid ADC pin"); + if (afec_pins[chan] == pin) { + break; + } + } + return (struct gpio_adc){ .chan=chan }; +} + +static inline Afec * +gpio_adc_to_afec(struct gpio_adc g) +{ + return (g.chan >= AFEC1_START ? AFEC1 : AFEC0); +} + +static inline uint32_t +gpio_adc_to_afec_chan(struct gpio_adc g) +{ + return (g.chan >= AFEC1_START ? g.chan - AFEC1_START : g.chan); +} + +#define ADC_FREQ_MAX 6000000UL +DECL_CONSTANT(ADC_MAX, 4095); + +static int +init_afec(Afec* afec) { + + // Enable PMC + enable_pclock(afec == AFEC0 ? ID_AFEC0 : ID_AFEC1); + + // If busy, return busy + if ((afec->AFE_ISR & AFE_ISR_DRDY) == AFE_ISR_DRDY) { + return -1; + } + + // Reset + afec->AFE_CR = AFE_CR_SWRST; + + // Configure afec + afec->AFE_MR = AFE_MR_ANACH_ALLOWED | \ + AFE_MR_PRESCAL (SystemCoreClock / (2 * ADC_FREQ_MAX) -1) | \ + AFE_MR_SETTLING_AST3 | \ + AFE_MR_TRACKTIM(2) | \ + AFE_MR_TRANSFER(1) | \ + AFE_MR_STARTUP_SUT64; + afec->AFE_EMR = AFE_EMR_TAG | \ + AFE_EMR_RES_NO_AVERAGE | \ + AFE_EMR_STM; + afec->AFE_ACR = AFE_ACR_IBCTL(1); + + // Disable interrupts + afec->AFE_IDR = 0xDF00803F; + + // Disable SW triggering + uint32_t mr = afec->AFE_MR; + + mr &= ~(AFE_MR_TRGSEL_Msk | AFE_MR_TRGEN | AFE_MR_FREERUN_ON); + mr |= AFE_MR_TRGEN_DIS; + afec->AFE_MR = mr; + + return 0; +} + +void +gpio_afec_init(void) { + + while(init_afec(AFEC0) != 0) { + (void)(AFEC0->AFE_LCDR & AFE_LCDR_LDATA_Msk); + } + while(init_afec(AFEC1) != 0) { + (void)(AFEC1->AFE_LCDR & AFE_LCDR_LDATA_Msk); + } + +} +DECL_INIT(gpio_afec_init); + +struct gpio_adc +gpio_adc_setup(uint8_t pin) +{ + struct gpio_adc adc_pin = pin_to_gpio_adc(pin); + Afec *afec = gpio_adc_to_afec(adc_pin); + uint32_t afec_chan = gpio_adc_to_afec_chan(adc_pin); + + //config channel + uint32_t reg = afec->AFE_DIFFR; + reg &= ~(1u << afec_chan); + afec->AFE_DIFFR = reg; + reg = afec->AFE_CGR; + reg &= ~(0x03u << (2 * afec_chan)); + reg |= 1 << (2 * afec_chan); + afec->AFE_CGR = reg; + + // Configure channel + // afec_ch_get_config_defaults(&ch_cfg); + // afec_ch_set_config(afec, afec_chan, &ch_cfg); + // Remove default internal offset from channel + // See Atmel Appnote AT03078 Section 1.5 + afec->AFE_CSELR = afec_chan; + afec->AFE_COCR = (0x800 & AFE_COCR_AOFF_Msk); + + // Enable and calibrate Channel + afec->AFE_CHER = 1 << afec_chan; + + reg = afec->AFE_CHSR; + afec->AFE_CDOR = reg; + afec->AFE_CR = AFE_CR_AUTOCAL; + + return adc_pin; +} + +enum { AFE_DUMMY=0xff }; +uint8_t active_channel = AFE_DUMMY; + +// Try to sample a value. Returns zero if sample ready, otherwise +// returns the number of clock ticks the caller should wait before +// retrying this function. +uint32_t +gpio_adc_sample(struct gpio_adc g) +{ + Afec *afec = gpio_adc_to_afec(g); + uint32_t afec_chan = gpio_adc_to_afec_chan(g); + if (active_channel == g.chan) { + if ((afec->AFE_ISR & AFE_ISR_DRDY) + && (afec->AFE_ISR & (1 << afec_chan))) { + // Sample now ready + return 0; + } else { + // Busy + goto need_delay; + } + } else if (active_channel != AFE_DUMMY) { + goto need_delay; + } + + afec->AFE_CHDR = 0x803F; // Disable all channels + afec->AFE_CHER = 1 << afec_chan; + + active_channel = g.chan; + + for (uint32_t chan = 0; chan < 16; ++chan) + { + if ((afec->AFE_ISR & (1 << chan)) != 0) + { + afec->AFE_CSELR = chan; + (void)(afec->AFE_CDR); + } + } + afec->AFE_CR = AFE_CR_START; + +need_delay: + return ADC_FREQ_MAX * 10000ULL / CONFIG_CLOCK_FREQ; // about 400 mcu clock cycles or 40 afec cycles +} + +// Read a value; use only after gpio_adc_sample() returns zero +uint16_t +gpio_adc_read(struct gpio_adc g) +{ + Afec *afec = gpio_adc_to_afec(g); + uint32_t afec_chan = gpio_adc_to_afec_chan(g); + active_channel = AFE_DUMMY; + afec->AFE_CSELR = afec_chan; + return afec->AFE_CDR; +} + +// Cancel a sample that may have been started with gpio_adc_sample() +void +gpio_adc_cancel_sample(struct gpio_adc g) +{ + if (active_channel == g.chan) { + active_channel = AFE_DUMMY; + } +} diff --git a/src/atsam/sam4s_sysinit.c b/src/atsam/sam4s_sysinit.c new file mode 100644 index 00000000..41a2dcfe --- /dev/null +++ b/src/atsam/sam4s_sysinit.c @@ -0,0 +1,65 @@ +// This code is from lib/sam4e/gcc/system_sam4e.c - it is unclear why +// it is not defined in the Atmel sam4s cmsis code. + +#include "internal.h" + +/* Clock Settings (120MHz) */ +#define SYS_BOARD_OSCOUNT (CKGR_MOR_MOSCXTST(0x8U)) +#define SYS_BOARD_PLLAR (CKGR_PLLAR_ONE \ + | CKGR_PLLAR_MULA(0x13U) \ + | CKGR_PLLAR_PLLACOUNT(0x3fU) \ + | CKGR_PLLAR_DIVA(0x1U)) +#define SYS_BOARD_MCKR (PMC_MCKR_PRES_CLK_2 | PMC_MCKR_CSS_PLLA_CLK) + +#define SYS_CKGR_MOR_KEY_VALUE CKGR_MOR_KEY(0x37) /* Key to unlock MOR register */ + +uint32_t SystemCoreClock = CHIP_FREQ_MAINCK_RC_4MHZ; + +void SystemInit( void ) +{ + /* Set FWS according to SYS_BOARD_MCKR configuration */ + EFC0->EEFC_FMR = EEFC_FMR_FWS(5); + + /* Initialize main oscillator */ + if ( !(PMC->CKGR_MOR & CKGR_MOR_MOSCSEL) ) + { + PMC->CKGR_MOR = SYS_CKGR_MOR_KEY_VALUE | SYS_BOARD_OSCOUNT | CKGR_MOR_MOSCRCEN | CKGR_MOR_MOSCXTEN; + + while ( !(PMC->PMC_SR & PMC_SR_MOSCXTS) ) + { + } + } + + /* Switch to 3-20MHz Xtal oscillator */ + PMC->CKGR_MOR = SYS_CKGR_MOR_KEY_VALUE | SYS_BOARD_OSCOUNT | CKGR_MOR_MOSCRCEN | CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCSEL; + + while ( !(PMC->PMC_SR & PMC_SR_MOSCSELS) ) + { + } + + PMC->PMC_MCKR = (PMC->PMC_MCKR & ~(uint32_t)PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK; + + while ( !(PMC->PMC_SR & PMC_SR_MCKRDY) ) + { + } + + /* Initialize PLLA */ + PMC->CKGR_PLLAR = SYS_BOARD_PLLAR; + while ( !(PMC->PMC_SR & PMC_SR_LOCKA) ) + { + } + + /* Switch to main clock */ + PMC->PMC_MCKR = (SYS_BOARD_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK; + while ( !(PMC->PMC_SR & PMC_SR_MCKRDY) ) + { + } + + /* Switch to PLLA */ + PMC->PMC_MCKR = SYS_BOARD_MCKR; + while ( !(PMC->PMC_SR & PMC_SR_MCKRDY) ) + { + } + + SystemCoreClock = CHIP_FREQ_CPU_MAX; +} diff --git a/src/atsam/sam4s_timer.c b/src/atsam/sam4s_timer.c new file mode 100644 index 00000000..a051f08c --- /dev/null +++ b/src/atsam/sam4s_timer.c @@ -0,0 +1,118 @@ +// SAM4s 16bit timer interrupt scheduling +// +// Copyright (C) 2016-2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "board/io.h" // readl +#include "board/irq.h" // irq_disable +#include "board/misc.h" // timer_read_time +#include "board/timer_irq.h" // timer_dispatch_many +#include "command.h" // DECL_SHUTDOWN +#include "internal.h" // TC0 +#include "sched.h" // DECL_INIT + + +/**************************************************************** + * Low level timer code + ****************************************************************/ + +// Get the 16bit timer value +static uint32_t +timer_get(void) +{ + return TC0->TC_CHANNEL[0].TC_CV; +} + +// Set the next irq time +static void +timer_set(uint32_t value) +{ + TC0->TC_CHANNEL[0].TC_RA = value; +} + +// Activate timer dispatch as soon as possible +void +timer_kick(void) +{ + timer_set(timer_read_time() + 50); +} + + +/**************************************************************** + * 16bit hardware timer to 32bit conversion + ****************************************************************/ + +// High bits of timer (top 17 bits) +static uint32_t timer_high; + +// Return the current time (in absolute clock ticks). +uint32_t __always_inline +timer_read_time(void) +{ + uint32_t th = readl(&timer_high); + uint32_t cur = timer_get(); + // Combine timer_high (high 17 bits) and current time (low 16 + // bits) using method that handles rollovers correctly. + return (th ^ cur) + (th & 0x8000); +} + +// Update timer_high every 0x8000 clock ticks +static uint_fast8_t +timer_event(struct timer *t) +{ + timer_high += 0x8000; + t->waketime = timer_high + 0x8000; + return SF_RESCHEDULE; +} +static struct timer wrap_timer = { + .func = timer_event, + .waketime = 0x8000, +}; + +void +timer_reset(void) +{ + sched_add_timer(&wrap_timer); +} +DECL_SHUTDOWN(timer_reset); + + +/**************************************************************** + * Timer init + ****************************************************************/ + +void +timer_init(void) +{ + TcChannel *tc = &TC0->TC_CHANNEL[0]; + // Reset the timer + tc->TC_CCR = TC_CCR_CLKDIS; + tc->TC_IDR = 0xFFFFFFFF; + // Enable it + enable_pclock(ID_TC0); + tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK2; + tc->TC_IER = TC_IER_CPAS; + NVIC_SetPriority(TC0_IRQn, 2); + NVIC_EnableIRQ(TC0_IRQn); + timer_kick(); + timer_reset(); + tc->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; +} +DECL_INIT(timer_init); + + +/**************************************************************** + * Main timer dispatch irq handler + ****************************************************************/ + +// IRQ handler +void __visible __aligned(16) // aligning helps stabilize perf benchmarks +TC0_Handler(void) +{ + irq_disable(); + uint32_t next = timer_dispatch_many(); + timer_set(next); + TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending + irq_enable(); +} diff --git a/src/atsam/serial.c b/src/atsam/serial.c new file mode 100644 index 00000000..c87539ad --- /dev/null +++ b/src/atsam/serial.c @@ -0,0 +1,77 @@ +// sam3/sam4 serial port +// +// Copyright (C) 2016-2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_SERIAL_BAUD +#include "board/serial_irq.h" // serial_rx_data +#include "internal.h" // gpio_peripheral +#include "sched.h" // DECL_INIT + +// Serial port pins +#if CONFIG_MACH_SAM3X8E +#define Serial_IRQ_Handler UART_Handler +static Uart * const Port = UART; +static const uint32_t Pmc_id = ID_UART, Irq_id = UART_IRQn; +static const uint32_t rx_pin = GPIO('A', 8); +static const uint32_t tx_pin = GPIO('A', 9); +#elif CONFIG_MACH_SAM4S8C +#define Serial_IRQ_Handler UART1_Handler +static Uart * const Port = UART1; +static const uint32_t Pmc_id = ID_UART1, Irq_id = UART1_IRQn; +static const uint32_t rx_pin = GPIO('B', 2); +static const uint32_t tx_pin = GPIO('B', 3); +#elif CONFIG_MACH_SAM4E8E +#define Serial_IRQ_Handler UART0_Handler +static Uart * const Port = UART0; +static const uint32_t Pmc_id = ID_UART0, Irq_id = UART0_IRQn; +static const uint32_t rx_pin = GPIO('A', 9); +static const uint32_t tx_pin = GPIO('A', 10); +#endif + +void +serial_init(void) +{ + gpio_peripheral(rx_pin, 'A', 1); + gpio_peripheral(tx_pin, 'A', 0); + + // Reset uart + enable_pclock(Pmc_id); + Port->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS; + Port->UART_CR = (UART_CR_RSTRX | UART_CR_RSTTX + | UART_CR_RXDIS | UART_CR_TXDIS); + Port->UART_IDR = 0xFFFFFFFF; + + // Enable uart + Port->UART_MR = (US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO + | UART_MR_CHMODE_NORMAL); + Port->UART_BRGR = SystemCoreClock / (16 * CONFIG_SERIAL_BAUD); + Port->UART_IER = UART_IER_RXRDY; + NVIC_EnableIRQ(Irq_id); + NVIC_SetPriority(Irq_id, 0); + Port->UART_CR = UART_CR_RXEN | UART_CR_TXEN; +} +DECL_INIT(serial_init); + +void __visible +Serial_IRQ_Handler(void) +{ + uint32_t status = Port->UART_SR; + if (status & UART_SR_RXRDY) + serial_rx_byte(Port->UART_RHR); + if (status & UART_SR_TXRDY) { + uint8_t data; + int ret = serial_get_tx_byte(&data); + if (ret) + Port->UART_IDR = UART_IDR_TXRDY; + else + Port->UART_THR = data; + } +} + +void +serial_enable_tx_irq(void) +{ + Port->UART_IER = UART_IDR_TXRDY; +} diff --git a/src/atsam/spi.c b/src/atsam/spi.c new file mode 100644 index 00000000..5f79a02c --- /dev/null +++ b/src/atsam/spi.c @@ -0,0 +1,285 @@ +// SPI transmissions on sam3 +// +// Copyright (C) 2018 Petri Honkala +// Copyright (C) 2018 Florian Heilmann +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "command.h" // shutdown +#include "gpio.h" // spi_setup +#include "internal.h" // gpio_peripheral +#include "sched.h" // sched_shutdown + + +/**************************************************************** + * 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' }, + { USART0, ID_USART0, GPIO('A', 10), GPIO('A', 11), GPIO('A', 17), 'A', 'B'}, + { USART1, ID_USART1, GPIO('A', 12), GPIO('A', 13), GPIO('A', 16), 'A', 'A'}, + { USART2, ID_USART2, GPIO('B', 21), GPIO('B', 20), GPIO('B', 24), 'A', 'A'}, +#elif CONFIG_MACH_SAM4S8C + { SPI, ID_SPI, GPIO('A', 12), GPIO('A', 13), GPIO('A', 14), 'A', 'A' }, + { USART0, ID_USART0, GPIO('A', 5), GPIO('A', 6), GPIO('A', 2), 'A', 'B' }, + { USART1, ID_USART1, GPIO('A', 21), GPIO('A', 22), GPIO('A', 23), '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'}, + { SPI, ID_SPI, GPIO('A', 12), GPIO('A', 13), GPIO('A', 14), 'A', 'A' }, +#endif +}; + +static int +is_spihw(void *dev) +{ +#if CONFIG_MACH_SAM3X8E + return dev == SPI0; +#else + return dev == SPI; +#endif +} + +static void +init_pins(uint32_t bus) +{ + 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); +} + + +/**************************************************************** + * 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 */ + pSpi->SPI_CR = SPI_CR_SPIDIS; + /* Execute a software reset of the SPI twice */ + pSpi->SPI_CR = SPI_CR_SWRST; + pSpi->SPI_CR = SPI_CR_SWRST; + + 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 */ + pSpi->SPI_CSR[0] = 0; + pSpi->SPI_CSR[1] = 0; + pSpi->SPI_CSR[2] = 0; + pSpi->SPI_CSR[3] = 0; + + /* Set basic channel config */ + pSpi->SPI_CSR[CHANNEL] = 0; + /* Enable SPI */ + pSpi->SPI_CR = SPI_CR_SPIEN; +} + +static struct spi_config +spihw_setup(uint32_t bus, uint8_t mode, uint32_t rate) +{ + // Make sure bus is enabled + spihw_init(bus); + + 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){ .spidev = spi_bus[bus].dev, .cfg = config }; +} + +static void +spihw_prepare(struct spi_config config) +{ + Spi *pSpi = config.spidev; + pSpi->SPI_CSR[CHANNEL] = config.cfg; +} + +static void +spihw_transfer(struct spi_config config, uint8_t receive_data + , uint8_t len, uint8_t *data) +{ + Spi *pSpi = config.spidev; + if (receive_data) { + while (len--) { + pSpi->SPI_TDR = *data; + // wait for receive register + while (!(pSpi->SPI_SR & SPI_SR_RDRF)) + ; + // get data + *data++ = pSpi->SPI_RDR; + } + } else { + while (len--) { + pSpi->SPI_TDR = *data++; + // wait for receive register + while (!(pSpi->SPI_SR & SPI_SR_RDRF)) + ; + // read data (to clear RDRF) + pSpi->SPI_RDR; + } + } +} + + +/**************************************************************** + * 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); +} diff --git a/src/atsam/timer.c b/src/atsam/timer.c new file mode 100644 index 00000000..b2d600a5 --- /dev/null +++ b/src/atsam/timer.c @@ -0,0 +1,65 @@ +// SAM3/SAM4 timer interrupt scheduling +// +// Copyright (C) 2016-2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "board/irq.h" // irq_disable +#include "board/misc.h" // timer_read_time +#include "board/timer_irq.h" // timer_dispatch_many +#include "command.h" // DECL_SHUTDOWN +#include "internal.h" // TC0 +#include "sched.h" // DECL_INIT + +// Set the next irq time +static void +timer_set(uint32_t value) +{ + TC0->TC_CHANNEL[0].TC_RA = value; +} + +// Return the current time (in absolute clock ticks). +uint32_t +timer_read_time(void) +{ + return TC0->TC_CHANNEL[0].TC_CV; +} + +// Activate timer dispatch as soon as possible +void +timer_kick(void) +{ + timer_set(timer_read_time() + 50); + TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending +} + +void +timer_init(void) +{ + TcChannel *tc = &TC0->TC_CHANNEL[0]; + // Reset the timer + tc->TC_CCR = TC_CCR_CLKDIS; + tc->TC_IDR = 0xFFFFFFFF; + // Enable it + enable_pclock(ID_TC0); + tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1; + tc->TC_IER = TC_IER_CPAS; + NVIC_SetPriority(TC0_IRQn, 2); + NVIC_EnableIRQ(TC0_IRQn); + timer_kick(); + tc->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; +} +DECL_INIT(timer_init); + +// IRQ handler +void __visible __aligned(16) // aligning helps stabilize perf benchmarks +TC0_Handler(void) +{ + irq_disable(); + uint32_t status = TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending + if (likely(status & TC_SR_CPAS)) { + uint32_t next = timer_dispatch_many(); + timer_set(next); + } + irq_enable(); +} diff --git a/src/atsam/usb_cdc_ep.h b/src/atsam/usb_cdc_ep.h new file mode 100644 index 00000000..bcf1d3e3 --- /dev/null +++ b/src/atsam/usb_cdc_ep.h @@ -0,0 +1,10 @@ +#ifndef __SAM3_USB_CDC_EP_H +#define __SAM3_USB_CDC_EP_H + +enum { + USB_CDC_EP_ACM = 3, + USB_CDC_EP_BULK_OUT = 1, + USB_CDC_EP_BULK_IN = 2, +}; + +#endif // usb_cdc_ep.h diff --git a/src/atsamd/Kconfig b/src/atsamd/Kconfig new file mode 100644 index 00000000..02e21b7b --- /dev/null +++ b/src/atsamd/Kconfig @@ -0,0 +1,57 @@ +# Kconfig settings for Atmel SAMD processors + +if MACH_ATSAMD + +config ATSAMD_SELECT + bool + default y + select HAVE_GPIO + select HAVE_GPIO_ADC + select HAVE_GPIO_I2C + select HAVE_GPIO_SPI + select HAVE_GPIO_HARD_PWM + select HAVE_GPIO_BITBANGING + +config BOARD_DIRECTORY + string + default "atsamd" + +choice + prompt "Processor model" + config MACH_SAMD21A + bool "SAMD21G18A (Arduino Zero)" +endchoice + +config CLOCK_FREQ + int + default 48000000 + +choice + prompt "Bootloader offset" + config FLASH_START_0000 + bool "No bootloader" + config FLASH_START_2000 + bool "8KiB bootloader (Arduino Zero)" + config FLASH_START_4000 + bool "16KiB bootloader (Arduino M0)" +endchoice + +config FLASH_START + hex + default 0x4000 if FLASH_START_4000 + default 0x2000 if FLASH_START_2000 + default 0x0000 + +config USBSERIAL + bool "Use USB for communication (instead of serial)" + default y +config SERIAL + depends on !USBSERIAL + bool + default y +config SERIAL_BAUD + depends on SERIAL + int "Baud rate for serial port" + default 250000 + +endif diff --git a/src/atsamd/Makefile b/src/atsamd/Makefile new file mode 100644 index 00000000..ca8b2e99 --- /dev/null +++ b/src/atsamd/Makefile @@ -0,0 +1,54 @@ +# Additional atsamd build rules + +# Setup the toolchain +CROSS_PREFIX=arm-none-eabi- + +dirs-y += src/atsamd src/generic +dirs-$(CONFIG_MACH_SAMD21A) += lib/samd21/samd21a/gcc/gcc/ + +CFLAGS-$(CONFIG_MACH_SAMD21A) += -mcpu=cortex-m0plus +CFLAGS-$(CONFIG_MACH_SAMD21A) += -Ilib/samd21/samd21a/include -D__SAMD21G18A__ +CFLAGS += $(CFLAGS-y) -mthumb -Ilib/cmsis-core + +eflags-$(CONFIG_MACH_SAMD21A) += -T $(OUT)samd21a.ld +CFLAGS_klipper.elf += $(eflags-y) --specs=nano.specs --specs=nosys.specs + +# Add source files +src-y += atsamd/main.c atsamd/timer.c atsamd/clock.c atsamd/gpio.c +src-y += generic/crc16_ccitt.c generic/alloc.c +src-y += generic/armcm_irq.c generic/timer_irq.c +src-$(CONFIG_USBSERIAL) += atsamd/usbserial.c generic/usb_cdc.c +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_GPIO_HARD_PWM) += atsamd/hard_pwm.c +src-$(CONFIG_MACH_SAMD21A) += ../lib/samd21/samd21a/gcc/gcc/startup_samd21.c + +# Support bootloader offset address +target-$(CONFIG_MACH_SAMD21A) := $(OUT)samd21a.ld $(target-y) + +$(OUT)samd21a.ld: lib/samd21/samd21a/gcc/gcc/samd21g18a_flash.ld $(OUT)board-link + @echo " Preprocessing $@" + $(Q)$(CPP) -P -MD -MT $@ -DFLASH_START=$(CONFIG_FLASH_START) $< -o $@ + +# Build the additional hex and bin output files +target-y += $(OUT)klipper.bin $(OUT)klipper.elf.hex + +$(OUT)klipper.bin: $(OUT)klipper.elf + @echo " Creating hex file $@" + $(Q)$(OBJCOPY) -O binary $< $@ + +$(OUT)klipper.elf.hex: $(OUT)klipper.elf + @echo " Creating hex file $@" + $(Q)$(OBJCOPY) -j .text -j .relocate -O ihex $< $@ + +# Flash rules +lib/bossac/bin/bossac: + @echo " Building bossac" + $(Q)make -C lib/bossac bin/bossac + +flash: $(OUT)klipper.bin lib/bossac/bin/bossac + @echo " Flashing $^ to $(FLASH_DEVICE) via bossac" + $(Q)if [ -z $(FLASH_DEVICE) ]; then echo "Please specify FLASH_DEVICE"; exit 1; fi + $(Q)lib/bossac/bin/bossac -U -p "$(FLASH_DEVICE)" -a --offset=0x2000 -w $(OUT)klipper.bin -v -b -R diff --git a/src/atsamd/adc.c b/src/atsamd/adc.c new file mode 100644 index 00000000..38836592 --- /dev/null +++ b/src/atsamd/adc.c @@ -0,0 +1,121 @@ +// Analog to Digital Converter support +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "command.h" // shutdown +#include "gpio.h" // gpio_adc_read +#include "internal.h" // GPIO +#include "samd21.h" // ADC +#include "sched.h" // sched_shutdown + +static const uint8_t adc_pins[] = { + GPIO('A', 2), GPIO('A', 3), GPIO('B', 8), GPIO('B', 9), GPIO('A', 4), + GPIO('A', 5), GPIO('A', 6), GPIO('A', 7), GPIO('B', 0), GPIO('B', 1), + GPIO('B', 2), GPIO('B', 3), GPIO('B', 4), GPIO('B', 5), GPIO('B', 6), + GPIO('B', 7), GPIO('A', 8), GPIO('A', 9), GPIO('A', 10), GPIO('A', 11) +}; + +DECL_CONSTANT(ADC_MAX, 4095); + +static void +adc_init(void) +{ + static uint8_t have_run_init; + if (have_run_init) + return; + have_run_init = 1; + + // Enable adc clock + enable_pclock(ADC_GCLK_ID, PM_APBCMASK_ADC); + + // Load calibraiton info + uint32_t v = *((uint32_t*)ADC_FUSES_BIASCAL_ADDR); + uint32_t bias = (v & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos; + v = *((uint32_t*)ADC_FUSES_LINEARITY_0_ADDR); + uint32_t li0 = (v & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos; + v = *((uint32_t*)ADC_FUSES_LINEARITY_1_ADDR); + uint32_t li5 = (v & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos; + uint32_t lin = li0 | (li5 << 5); + ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(lin); + + // Setup and enable adc + ADC->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1; + ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV128; + ADC->SAMPCTRL.reg = 63; + ADC->CTRLA.reg = ADC_CTRLA_ENABLE; +} + +struct gpio_adc +gpio_adc_setup(uint8_t pin) +{ + // Find pin in adc_pins table + uint8_t chan; + for (chan=0; ; chan++) { + if (chan >= ARRAY_SIZE(adc_pins)) + shutdown("Not a valid ADC pin"); + if (adc_pins[chan] == pin) + break; + } + + // Enable ADC + adc_init(); + + // Set pin in ADC mode + gpio_peripheral(pin, 'B', 0); + + return (struct gpio_adc){ chan }; +} + +enum { ADC_DUMMY=0xff }; +static uint8_t last_analog_read = ADC_DUMMY; + +// Try to sample a value. Returns zero if sample ready, otherwise +// returns the number of clock ticks the caller should wait before +// retrying this function. +uint32_t +gpio_adc_sample(struct gpio_adc g) +{ + if (last_analog_read == g.chan) { + if (ADC->INTFLAG.reg & ADC_INTFLAG_RESRDY) + // Sample now ready + return 0; + // ADC is still busy + goto need_delay; + } + if (last_analog_read != ADC_DUMMY) + // Sample on another channel in progress + goto need_delay; + last_analog_read = g.chan; + + // Set the channel to sample + ADC->INPUTCTRL.reg = (ADC_INPUTCTRL_MUXPOS(g.chan) + | ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_GAIN_DIV2); + + // Start the sample + ADC->SWTRIG.reg = ADC_SWTRIG_START; + + // Schedule next attempt after sample is likely to be complete +need_delay: + return 42 * 128 + 200; // 42 == 1 + (63+1)/2 + 1 + 12/2 + 1.5 +} + +// Read a value; use only after gpio_adc_sample() returns zero +uint16_t +gpio_adc_read(struct gpio_adc g) +{ + last_analog_read = ADC_DUMMY; + return ADC->RESULT.reg; +} + +// Cancel a sample that may have been started with gpio_adc_sample() +void +gpio_adc_cancel_sample(struct gpio_adc g) +{ + if (last_analog_read == g.chan) { + ADC->SWTRIG.reg = ADC_SWTRIG_FLUSH; + ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; + last_analog_read = ADC_DUMMY; + } +} diff --git a/src/atsamd/clock.c b/src/atsamd/clock.c new file mode 100644 index 00000000..ef0cd9bc --- /dev/null +++ b/src/atsamd/clock.c @@ -0,0 +1,77 @@ +// Code to setup peripheral clocks on the SAMD21 +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_CLOCK_FREQ +#include "compiler.h" // DIV_ROUND_CLOSEST +#include "internal.h" // enable_pclock +#include "samd21.h" // GCLK + +// The "generic clock generators" that are configured +#define CLKGEN_MAIN 0 +#define CLKGEN_32K 1 +#define CLKGEN_ULP32K 2 + +// Enable a peripheral clock and power to that peripheral +void +enable_pclock(uint32_t clock_id, uint32_t pmask) +{ + GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_ID(clock_id) + | GCLK_CLKCTRL_GEN(CLKGEN_MAIN) | GCLK_CLKCTRL_CLKEN); + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) + ; + PM->APBCMASK.reg |= pmask; +} + +#define FREQ_XOSC32K 32768 + +void +SystemInit(void) +{ + // Setup flash to work with 48Mhz clock + NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_RWS_HALF; + + // Enable external 32Khz crystal + uint32_t val = (SYSCTRL_XOSC32K_STARTUP(6) + | SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K); + SYSCTRL->XOSC32K.reg = val; + SYSCTRL->XOSC32K.reg = val | SYSCTRL_XOSC32K_ENABLE; + while (!(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_XOSC32KRDY)) + ; + + // Reset GCLK + GCLK->CTRL.reg = GCLK_CTRL_SWRST; + while (GCLK->CTRL.reg & GCLK_CTRL_SWRST) + ; + + // Route external 32Khz clock to DFLL48M (via CLKGEN_32K) + GCLK->GENDIV.reg = GCLK_GENDIV_ID(CLKGEN_32K); + GCLK->GENCTRL.reg = (GCLK_GENCTRL_ID(CLKGEN_32K) + | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_GENEN); + GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_ID(SYSCTRL_GCLK_ID_DFLL48) + | GCLK_CLKCTRL_GEN(CLKGEN_32K) | GCLK_CLKCTRL_CLKEN); + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) + ; + + // Configure DFLL48M clock + SYSCTRL->DFLLCTRL.reg = 0; + uint32_t mul = DIV_ROUND_CLOSEST(CONFIG_CLOCK_FREQ, FREQ_XOSC32K); + SYSCTRL->DFLLMUL.reg = (SYSCTRL_DFLLMUL_CSTEP(31) + | SYSCTRL_DFLLMUL_FSTEP(511) + | SYSCTRL_DFLLMUL_MUL(mul)); + SYSCTRL->DFLLCTRL.reg = (SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_WAITLOCK + | SYSCTRL_DFLLCTRL_QLDIS + | SYSCTRL_DFLLCTRL_ENABLE); + uint32_t ready = (SYSCTRL_PCLKSR_DFLLRDY | SYSCTRL_PCLKSR_DFLLLCKC + | SYSCTRL_PCLKSR_DFLLLCKF); + while ((SYSCTRL->PCLKSR.reg & ready) != ready) + ; + + // Switch main clock to DFLL48M clock + GCLK->GENDIV.reg = GCLK_GENDIV_ID(CLKGEN_MAIN); + GCLK->GENCTRL.reg = (GCLK_GENCTRL_ID(CLKGEN_MAIN) + | GCLK_GENCTRL_SRC_DFLL48M + | GCLK_GENCTRL_IDC | GCLK_GENCTRL_GENEN); +} diff --git a/src/atsamd/gpio.c b/src/atsamd/gpio.c new file mode 100644 index 00000000..045dc003 --- /dev/null +++ b/src/atsamd/gpio.c @@ -0,0 +1,141 @@ +// samd21 gpio functions +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include // ffs +#include "board/irq.h" // irq_save +#include "command.h" // shutdown +#include "gpio.h" // gpio_out_setup +#include "internal.h" // gpio_peripheral +#include "samd21.h" // PORT +#include "sched.h" // sched_shutdown + + +/**************************************************************** + * Pin multiplexing + ****************************************************************/ + +void +gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up) +{ + uint32_t bank = GPIO2PORT(gpio), bit = gpio % 32; + PortGroup *pg = &PORT->Group[bank]; + if (ptype) { + volatile uint8_t *pmux = &pg->PMUX[bit/2].reg; + uint8_t shift = (bit & 1) ? 4 : 0, mask = ~(0xf << shift); + *pmux = (*pmux & mask) | ((ptype - 'A') << shift); + } + if (pull_up) { + if (pull_up > 0) + pg->OUTSET.reg = (1<OUTCLR.reg = (1<PINCFG[bit].reg = ((ptype ? PORT_PINCFG_PMUXEN : 0) + | (pull_up ? PORT_PINCFG_PULLEN : 0)); +} + + +/**************************************************************** + * General Purpose Input Output (GPIO) pins + ****************************************************************/ + +#define NUM_PORT 2 + +struct gpio_out +gpio_out_setup(uint8_t pin, uint8_t val) +{ + if (GPIO2PORT(pin) >= NUM_PORT) + goto fail; + PortGroup *pg = &PORT->Group[GPIO2PORT(pin)]; + struct gpio_out g = { .regs=pg, .bit=GPIO2BIT(pin) }; + gpio_out_reset(g, val); + return g; +fail: + shutdown("Not an output pin"); +} + +static void +set_pincfg(PortGroup *pg, uint32_t bit, uint8_t cfg) +{ + pg->PINCFG[ffs(bit)-1].reg = cfg; +} + +void +gpio_out_reset(struct gpio_out g, uint8_t val) +{ + PortGroup *pg = g.regs; + irqstatus_t flag = irq_save(); + if (val) + pg->OUTSET.reg = g.bit; + else + pg->OUTCLR.reg = g.bit; + pg->DIRSET.reg = g.bit; + set_pincfg(pg, g.bit, 0); + irq_restore(flag); +} + +void +gpio_out_toggle_noirq(struct gpio_out g) +{ + PortGroup *pg = g.regs; + pg->OUTTGL.reg = g.bit; +} + +void +gpio_out_toggle(struct gpio_out g) +{ + gpio_out_toggle_noirq(g); +} + +void +gpio_out_write(struct gpio_out g, uint8_t val) +{ + PortGroup *pg = g.regs; + if (val) + pg->OUTSET.reg = g.bit; + else + pg->OUTCLR.reg = g.bit; +} + + +struct gpio_in +gpio_in_setup(uint8_t pin, int8_t pull_up) +{ + if (GPIO2PORT(pin) >= NUM_PORT) + goto fail; + PortGroup *pg = &PORT->Group[GPIO2PORT(pin)]; + struct gpio_in g = { .regs=pg, .bit=GPIO2BIT(pin) }; + gpio_in_reset(g, pull_up); + return g; +fail: + shutdown("Not an input pin"); +} + +void +gpio_in_reset(struct gpio_in g, int8_t pull_up) +{ + PortGroup *pg = g.regs; + irqstatus_t flag = irq_save(); + uint32_t cfg = PORT_PINCFG_INEN; + if (pull_up) { + cfg |= PORT_PINCFG_PULLEN; + if (pull_up > 0) + pg->OUTSET.reg = g.bit; + else + pg->OUTCLR.reg = g.bit; + } + set_pincfg(pg, g.bit, cfg); + pg->DIRCLR.reg = g.bit; + irq_restore(flag); +} + +uint8_t +gpio_in_read(struct gpio_in g) +{ + PortGroup *pg = g.regs; + return !!(pg->IN.reg & g.bit); +} diff --git a/src/atsamd/gpio.h b/src/atsamd/gpio.h new file mode 100644 index 00000000..5bf44454 --- /dev/null +++ b/src/atsamd/gpio.h @@ -0,0 +1,55 @@ +#ifndef __SAM3X8E_GPIO_H +#define __SAM3X8E_GPIO_H + +#include + +struct gpio_out { + void *regs; + uint32_t bit; +}; +struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val); +void gpio_out_reset(struct gpio_out g, uint8_t val); +void gpio_out_toggle_noirq(struct gpio_out g); +void gpio_out_toggle(struct gpio_out g); +void gpio_out_write(struct gpio_out g, uint8_t val); + +struct gpio_in { + void *regs; + uint32_t bit; +}; +struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up); +void gpio_in_reset(struct gpio_in g, int8_t pull_up); +uint8_t gpio_in_read(struct gpio_in g); + +struct gpio_pwm { + void *reg; +}; +struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val); +void gpio_pwm_write(struct gpio_pwm g, uint8_t val); + +struct gpio_adc { + uint32_t chan; +}; +struct gpio_adc gpio_adc_setup(uint8_t pin); +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 ctrla, baud; +}; +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); + +struct i2c_config { + uint8_t addr; +}; + +struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); +void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); + +#endif // gpio.h diff --git a/src/atsamd/hard_pwm.c b/src/atsamd/hard_pwm.c new file mode 100644 index 00000000..7deecfb9 --- /dev/null +++ b/src/atsamd/hard_pwm.c @@ -0,0 +1,102 @@ +// Hardware PWM support on samd21 +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "command.h" // shutdown +#include "gpio.h" // gpio_pwm_write +#include "internal.h" // GPIO +#include "samd21.h" // TCC0 +#include "sched.h" // sched_shutdown + +struct gpio_pwm_info { + uint32_t gpio; + Tcc *tcc; + uint32_t clock_id, power_id, channel; + char ptype; +}; + +static const struct gpio_pwm_info pwm_regs[] = { + { GPIO('A', 4), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 0, 'E' }, + { GPIO('A', 5), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 1, 'E' }, + { GPIO('A', 6), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 0, 'E' }, + { GPIO('A', 7), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 1, 'E' }, + { GPIO('A', 8), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 0, 'E' }, + { GPIO('A', 9), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 1, 'E' }, + { GPIO('A', 10), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 0, 'E' }, + { GPIO('A', 11), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 1, 'E' }, + { GPIO('A', 12), TCC2, TCC2_GCLK_ID, PM_APBCMASK_TCC2, 0, 'E' }, + { GPIO('A', 13), TCC2, TCC2_GCLK_ID, PM_APBCMASK_TCC2, 1, 'E' }, + { GPIO('A', 16), TCC2, TCC2_GCLK_ID, PM_APBCMASK_TCC2, 0, 'E' }, + { GPIO('A', 17), TCC2, TCC2_GCLK_ID, PM_APBCMASK_TCC2, 1, 'E' }, + { GPIO('A', 18), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 2, 'F' }, + { GPIO('A', 19), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 3, 'F' }, + { GPIO('A', 24), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 2, 'F' }, + { GPIO('A', 25), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 3, 'F' }, + { GPIO('A', 30), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 0, 'E' }, + { GPIO('A', 31), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 1, 'E' }, + { GPIO('B', 30), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 0, 'E' }, + { GPIO('B', 31), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 1, 'E' }, +}; + +#define MAX_PWM 255 + +DECL_CONSTANT(PWM_MAX, MAX_PWM); + +struct gpio_pwm +gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) +{ + // Find pin in pwm_regs table + const struct gpio_pwm_info *p = pwm_regs; + for (; ; p++) { + if (p >= &pwm_regs[ARRAY_SIZE(pwm_regs)]) + shutdown("Not a valid PWM pin"); + if (p->gpio == pin) + break; + } + + // Enable timer clock + enable_pclock(p->clock_id, p->power_id); + + // Map cycle_time to pwm clock divisor + uint32_t cs; + switch (cycle_time) { + case 0 ... (1+2) * MAX_PWM / 2 - 1: cs = 0; break; + case (1+2) * MAX_PWM / 2 ... (2+4) * MAX_PWM / 2 - 1: cs = 1; break; + case (2+4) * MAX_PWM / 2 ... (4+8) * MAX_PWM / 2 - 1: cs = 2; break; + case (4+8) * MAX_PWM / 2 ... (8+16) * MAX_PWM / 2 - 1: cs = 3; break; + case (8+16) * MAX_PWM / 2 ... (16+64) * MAX_PWM / 2 - 1: cs = 4; break; + case (16+64) * MAX_PWM / 2 ... (64+256) * MAX_PWM / 2 - 1: cs = 5; break; + case (64+256) * MAX_PWM / 2 ... (256+1024) * MAX_PWM / 2 - 1: cs = 6; break; + default: cs = 7; break; + } + uint32_t ctrla = TCC_CTRLA_ENABLE | TCC_CTRLA_PRESCALER(cs); + + // Enable timer + Tcc *tcc = p->tcc; + uint32_t old_ctrla = tcc->CTRLA.reg; + if (old_ctrla != ctrla) { + if (old_ctrla & TCC_CTRLA_ENABLE) + shutdown("PWM already programmed at different speed"); + tcc->CTRLA.reg = ctrla & ~TCC_CTRLA_ENABLE; + tcc->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; + tcc->PER.reg = MAX_PWM; + tcc->CTRLA.reg = ctrla; + } + + // Set initial value + struct gpio_pwm g = (struct gpio_pwm) { (void*)&tcc->CCB[p->channel].reg }; + gpio_pwm_write(g, val); + + // Route output to pin + gpio_peripheral(pin, p->ptype, 0); + + return g; +} + +void +gpio_pwm_write(struct gpio_pwm g, uint8_t val) +{ + *(volatile uint32_t*)g.reg = val; +} diff --git a/src/atsamd/i2c.c b/src/atsamd/i2c.c new file mode 100644 index 00000000..801c0e32 --- /dev/null +++ b/src/atsamd/i2c.c @@ -0,0 +1,114 @@ +// i2c support on samd21 +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_CLOCK_FREQ +#include "internal.h" // enable_pclock +#include "command.h" // shutdown +#include "gpio.h" // i2c_setup +#include "samd21.h" // SERCOM3 +#include "sched.h" // sched_shutdown + +#define TIME_RISE 125ULL // 125 nanoseconds +#define I2C_FREQ 100000 + +static void +i2c_init(void) +{ + static int have_run_init; + if (have_run_init) + return; + have_run_init = 1; + + // Setup clock + enable_pclock(SERCOM3_GCLK_ID_CORE, PM_APBCMASK_SERCOM3); + + // Configure SDA, SCL pins + gpio_peripheral(GPIO('A', 22), 'C', 0); + gpio_peripheral(GPIO('A', 23), 'C', 0); + + // Configure i2c + SercomI2cm *si = &SERCOM3->I2CM; + si->CTRLA.reg = 0; + uint32_t areg = (SERCOM_I2CM_CTRLA_LOWTOUTEN + | SERCOM_I2CM_CTRLA_INACTOUT(3) + | SERCOM_I2CM_STATUS_SEXTTOUT + | SERCOM_I2CM_STATUS_MEXTTOUT + | SERCOM_I2CM_CTRLA_MODE_I2C_MASTER); + si->CTRLA.reg = areg; + uint32_t baud = (CONFIG_CLOCK_FREQ / I2C_FREQ + - 10 - CONFIG_CLOCK_FREQ*TIME_RISE/1000000000) / 2; + si->BAUD.reg = baud; + si->CTRLA.reg = areg | SERCOM_I2CM_CTRLA_ENABLE; + while (si->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_ENABLE) + ; + + // Go into idle mode + si->STATUS.reg = SERCOM_I2CM_STATUS_BUSSTATE(1); + while (si->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_SYSOP) + ; +} + +struct i2c_config +i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) +{ + if (bus) + shutdown("Unsupported i2c bus"); + i2c_init(); + return (struct i2c_config){ .addr=addr<<1 }; +} + +static void +i2c_wait(SercomI2cm *si) +{ + for (;;) { + uint32_t intflag = si->INTFLAG.reg; + if (!(intflag & SERCOM_I2CM_INTFLAG_MB)) { + if (si->STATUS.reg & SERCOM_I2CM_STATUS_BUSERR) + shutdown("i2c buserror"); + continue; + } + if (intflag & SERCOM_I2CM_INTFLAG_ERROR) + shutdown("i2c error"); + break; + } +} + +static void +i2c_start(SercomI2cm *si, uint8_t addr) +{ + si->ADDR.reg = addr; + i2c_wait(si); +} + +static void +i2c_send_byte(SercomI2cm *si, uint8_t b) +{ + si->DATA.reg = b; + i2c_wait(si); +} + +static void +i2c_stop(SercomI2cm *si) +{ + si->CTRLB.reg = SERCOM_I2CM_CTRLB_CMD(3); +} + +void +i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) +{ + SercomI2cm *si = &SERCOM3->I2CM; + i2c_start(si, config.addr); + while (write_len--) + i2c_send_byte(si, *write++); + i2c_stop(si); +} + +void +i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read) +{ + shutdown("i2c_read not supported on samd21"); +} diff --git a/src/atsamd/internal.h b/src/atsamd/internal.h new file mode 100644 index 00000000..7043007f --- /dev/null +++ b/src/atsamd/internal.h @@ -0,0 +1,14 @@ +#ifndef __SAMD21_INTERNAL_H +#define __SAMD21_INTERNAL_H +// Local definitions for samd21 code + +#include // uint32_t + +#define GPIO(PORT, NUM) (((PORT)-'A') * 32 + (NUM)) +#define GPIO2PORT(PIN) ((PIN) / 32) +#define GPIO2BIT(PIN) (1<<((PIN) % 32)) + +void enable_pclock(uint32_t clock_id, uint32_t pmask); +void gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up); + +#endif // internal.h diff --git a/src/atsamd/main.c b/src/atsamd/main.c new file mode 100644 index 00000000..4687fc34 --- /dev/null +++ b/src/atsamd/main.c @@ -0,0 +1,52 @@ +// Main starting point for SAMD21 boards +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "command.h" // DECL_CONSTANT +#include "samd21.h" // SystemInit +#include "sched.h" // sched_main + +DECL_CONSTANT(MCU, "samd21g"); + + +/**************************************************************** + * watchdog handler + ****************************************************************/ + +void +watchdog_reset(void) +{ + WDT->CLEAR.reg = 0xa5; +} +DECL_TASK(watchdog_reset); + +void +watchdog_init(void) +{ + WDT->CONFIG.reg = WDT_CONFIG_PER_16K; // 500ms timeout + WDT->CTRL.reg = WDT_CTRL_ENABLE; +} +DECL_INIT(watchdog_init); + + +/**************************************************************** + * misc functions + ****************************************************************/ + +void +command_reset(uint32_t *args) +{ + NVIC_SystemReset(); +} +DECL_COMMAND_FLAGS(command_reset, HF_IN_SHUTDOWN, "reset"); + +// Main entry point +int +main(void) +{ + SystemInit(); + sched_main(); + return 0; +} diff --git a/src/atsamd/serial.c b/src/atsamd/serial.c new file mode 100644 index 00000000..895ffb27 --- /dev/null +++ b/src/atsamd/serial.c @@ -0,0 +1,61 @@ +// samd21 serial port +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_SERIAL_BAUD +#include "board/serial_irq.h" // serial_rx_data +#include "internal.h" // enable_pclock +#include "samd21.h" // SERCOM0 +#include "sched.h" // DECL_INIT + +void +serial_init(void) +{ + // Enable serial clock + enable_pclock(SERCOM0_GCLK_ID_CORE, PM_APBCMASK_SERCOM0); + // Enable pins + gpio_peripheral(GPIO('A', 10), 'C', 0); + gpio_peripheral(GPIO('A', 11), 'C', 0); + // Configure serial + SercomUsart *su = &SERCOM0->USART; + su->CTRLA.reg = 0; + uint32_t areg = (SERCOM_USART_CTRLA_MODE_USART_INT_CLK + | SERCOM_USART_CTRLA_DORD + | SERCOM_USART_CTRLA_SAMPR(1) + | SERCOM_USART_CTRLA_TXPO(1) + | SERCOM_USART_CTRLA_RXPO(3)); + su->CTRLA.reg = areg; + su->CTRLB.reg = SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN; + uint32_t baud8 = CONFIG_CLOCK_FREQ / (2 * CONFIG_SERIAL_BAUD); + su->BAUD.reg = (SERCOM_USART_BAUD_FRAC_BAUD(baud8 / 8) + | SERCOM_USART_BAUD_FRAC_FP(baud8 % 8)); + NVIC_SetPriority(SERCOM0_IRQn, 0); + NVIC_EnableIRQ(SERCOM0_IRQn); + su->INTENSET.reg = SERCOM_USART_INTENSET_RXC; + su->CTRLA.reg = areg | SERCOM_USART_CTRLA_ENABLE; +} +DECL_INIT(serial_init); + +void __visible +SERCOM0_Handler(void) +{ + uint32_t status = SERCOM0->USART.INTFLAG.reg; + if (status & SERCOM_USART_INTFLAG_RXC) + serial_rx_byte(SERCOM0->USART.DATA.reg); + if (status & SERCOM_USART_INTFLAG_DRE) { + uint8_t data; + int ret = serial_get_tx_byte(&data); + if (ret) + SERCOM0->USART.INTENCLR.reg = SERCOM_USART_INTENSET_DRE; + else + SERCOM0->USART.DATA.reg = data; + } +} + +void +serial_enable_tx_irq(void) +{ + SERCOM0->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; +} diff --git a/src/atsamd/spi.c b/src/atsamd/spi.c new file mode 100644 index 00000000..12c7d680 --- /dev/null +++ b/src/atsamd/spi.c @@ -0,0 +1,92 @@ +// spi support on samd21 +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_CLOCK_FREQ +#include "internal.h" // enable_pclock +#include "command.h" // shutdown +#include "gpio.h" // spi_setup +#include "samd21.h" // SERCOM4 +#include "sched.h" // sched_shutdown + +static void +spi_init(uint32_t ctrla, uint32_t baud) +{ + static int have_run_init; + if (have_run_init) + return; + have_run_init = 1; + + // Setup clock + enable_pclock(SERCOM4_GCLK_ID_CORE, PM_APBCMASK_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; + ss->BAUD.reg = baud; + ss->CTRLA.reg = ctrla; +} + +struct spi_config +spi_setup(uint32_t bus, uint8_t mode, uint32_t rate) +{ + if (bus) + shutdown("Invalid spi bus"); + + uint32_t ctrla = (SERCOM_SPI_CTRLA_MODE_SPI_MASTER + | (mode << SERCOM_SPI_CTRLA_CPHA_Pos) + | SERCOM_SPI_CTRLA_DIPO(0) + | SERCOM_SPI_CTRLA_DOPO(1) + | SERCOM_SPI_CTRLA_ENABLE); + uint32_t baud = CONFIG_CLOCK_FREQ / (2 * rate) - 1; + spi_init(ctrla, baud); + return (struct spi_config){ .ctrla = ctrla, .baud = baud }; +} + +void +spi_prepare(struct spi_config config) +{ + uint32_t ctrla = config.ctrla, baud = config.baud; + SercomSpi *ss = &SERCOM4->SPI; + if (ctrla == ss->CTRLA.reg && baud == ss->BAUD.reg) + return; + ss->CTRLA.reg = ctrla & ~SERCOM_SPI_CTRLA_ENABLE; + ss->CTRLA.reg = ctrla & ~SERCOM_SPI_CTRLA_ENABLE; + ss->BAUD.reg = baud; + ss->CTRLA.reg = ctrla; +} + +void +spi_transfer(struct spi_config config, uint8_t receive_data + , uint8_t len, uint8_t *data) +{ + SercomSpi *ss = &SERCOM4->SPI; + if (receive_data) { + while (len--) { + ss->DATA.reg = *data; + // wait for receive register + while (!(ss->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC)) + ; + // get data + *data++ = ss->DATA.reg; + } + } else { + while (len--) { + ss->DATA.reg = *data++; + // wait for receive register + while (!(ss->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC)) + ; + // read data (to clear RXC) + ss->DATA.reg; + } + } +} diff --git a/src/atsamd/timer.c b/src/atsamd/timer.c new file mode 100644 index 00000000..882b4c10 --- /dev/null +++ b/src/atsamd/timer.c @@ -0,0 +1,66 @@ +// SAMD21 timer interrupt scheduling +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "board/irq.h" // irq_disable +#include "board/misc.h" // timer_read_time +#include "board/timer_irq.h" // timer_dispatch_many +#include "internal.h" // enable_pclock +#include "samd21.h" // TC4 +#include "sched.h" // DECL_INIT + +// Set the next irq time +static void +timer_set(uint32_t value) +{ + TC4->COUNT32.CC[0].reg = value; + TC4->COUNT32.INTFLAG.reg = TC_INTFLAG_MC0; +} + +// Return the current time (in absolute clock ticks). +uint32_t +timer_read_time(void) +{ + return TC4->COUNT32.COUNT.reg; +} + +// Activate timer dispatch as soon as possible +void +timer_kick(void) +{ + timer_set(timer_read_time() + 50); +} + +void +timer_init(void) +{ + // Supply power and clock to the timer + enable_pclock(TC3_GCLK_ID, PM_APBCMASK_TC3); + enable_pclock(TC4_GCLK_ID, PM_APBCMASK_TC4); + + // Configure the timer + TcCount32 *tc = &TC4->COUNT32; + irqstatus_t flag = irq_save(); + tc->CTRLA.reg = 0; + tc->CTRLA.reg = TC_CTRLA_MODE_COUNT32; + NVIC_SetPriority(TC4_IRQn, 2); + NVIC_EnableIRQ(TC4_IRQn); + tc->INTENSET.reg = TC_INTENSET_MC0; + tc->COUNT.reg = 0; + timer_kick(); + tc->CTRLA.reg = TC_CTRLA_MODE_COUNT32 | TC_CTRLA_ENABLE; + irq_restore(flag); +} +DECL_INIT(timer_init); + +// IRQ handler +void __visible __aligned(16) // aligning helps stabilize perf benchmarks +TC4_Handler(void) +{ + irq_disable(); + uint32_t next = timer_dispatch_many(); + timer_set(next); + irq_enable(); +} diff --git a/src/atsamd/usbserial.c b/src/atsamd/usbserial.c new file mode 100644 index 00000000..0864a4c5 --- /dev/null +++ b/src/atsamd/usbserial.c @@ -0,0 +1,246 @@ +// Hardware interface to USB on samd21 +// +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include // memcpy +#include "autoconf.h" // CONFIG_FLASH_START +#include "board/io.h" // readl +#include "board/irq.h" // irq_disable +#include "board/usb_cdc.h" // usb_notify_ep0 +#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN +#include "internal.h" // enable_pclock +#include "samd21.h" // USB +#include "sched.h" // DECL_INIT + + +/**************************************************************** + * USB transfer memory + ****************************************************************/ + +static uint8_t __aligned(4) ep0out[USB_CDC_EP0_SIZE]; +static uint8_t __aligned(4) ep0in[USB_CDC_EP0_SIZE]; +static uint8_t __aligned(4) acmin[USB_CDC_EP_ACM_SIZE]; +static uint8_t __aligned(4) bulkout[USB_CDC_EP_BULK_OUT_SIZE]; +static uint8_t __aligned(4) bulkin[USB_CDC_EP_BULK_IN_SIZE]; + +static UsbDeviceDescriptor usb_desc[USB_CDC_EP_BULK_IN + 1] = { + [0] = { { + { + .ADDR.reg = (uint32_t)ep0out, + .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(ep0out) >> 4), + }, { + .ADDR.reg = (uint32_t)ep0in, + .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(ep0in) >> 4), + }, + } }, + [USB_CDC_EP_ACM] = { { + { + }, { + .ADDR.reg = (uint32_t)acmin, + .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(acmin) >> 4), + }, + } }, + [USB_CDC_EP_BULK_OUT] = { { + { + .ADDR.reg = (uint32_t)bulkout, + .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(bulkout) >> 4), + }, { + }, + } }, + [USB_CDC_EP_BULK_IN] = { { + { + }, { + .ADDR.reg = (uint32_t)bulkin, + .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(bulkin) >> 4), + }, + } }, +}; + + +/**************************************************************** + * Interface + ****************************************************************/ + +#define EP0 USB->DEVICE.DeviceEndpoint[0] +#define EP_ACM USB->DEVICE.DeviceEndpoint[USB_CDC_EP_ACM] +#define EP_BULKOUT USB->DEVICE.DeviceEndpoint[USB_CDC_EP_BULK_OUT] +#define EP_BULKIN USB->DEVICE.DeviceEndpoint[USB_CDC_EP_BULK_IN] + +static int_fast8_t +usb_write_packet(uint32_t ep, uint32_t bank, const void *data, uint_fast8_t len) +{ + // Check if there is room for this packet + UsbDeviceEndpoint *ude = &USB->DEVICE.DeviceEndpoint[ep]; + uint8_t sts = ude->EPSTATUS.reg; + uint8_t bkrdy = (bank ? USB_DEVICE_EPSTATUS_BK1RDY + : USB_DEVICE_EPSTATUS_BK0RDY); + if (sts & bkrdy) + return -1; + // Copy the packet to the given buffer + UsbDeviceDescBank *uddb = &usb_desc[ep].DeviceDescBank[bank]; + memcpy((void*)uddb->ADDR.reg, data, len); + // Inform the USB hardware of the available packet + uint32_t pcksize = uddb->PCKSIZE.reg; + uint32_t c = pcksize & ~USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk; + uddb->PCKSIZE.reg = c | USB_DEVICE_PCKSIZE_BYTE_COUNT(len); + ude->EPSTATUSSET.reg = bkrdy; + return len; +} + +static int_fast8_t +usb_read_packet(uint32_t ep, uint32_t bank, void *data, uint_fast8_t max_len) +{ + // Check if there is a packet ready + UsbDeviceEndpoint *ude = &USB->DEVICE.DeviceEndpoint[ep]; + uint8_t sts = ude->EPSTATUS.reg; + uint8_t bkrdy = (bank ? USB_DEVICE_EPSTATUS_BK1RDY + : USB_DEVICE_EPSTATUS_BK0RDY); + if (!(sts & bkrdy)) + return -1; + // Copy the packet to the given buffer + UsbDeviceDescBank *uddb = &usb_desc[ep].DeviceDescBank[bank]; + uint32_t pcksize = uddb->PCKSIZE.reg; + uint32_t c = pcksize & USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk; + if (c > max_len) + c = max_len; + memcpy(data, (void*)uddb->ADDR.reg, c); + // Notify the USB hardware that the space is now available + ude->EPSTATUSCLR.reg = bkrdy; + return c; +} + +int_fast8_t +usb_read_bulk_out(void *data, uint_fast8_t max_len) +{ + return usb_read_packet(USB_CDC_EP_BULK_OUT, 0, data, max_len); +} + +int_fast8_t +usb_send_bulk_in(void *data, uint_fast8_t len) +{ + return usb_write_packet(USB_CDC_EP_BULK_IN, 1, data, len); +} + +int_fast8_t +usb_read_ep0(void *data, uint_fast8_t max_len) +{ + return usb_read_packet(0, 0, data, max_len); +} + +int_fast8_t +usb_read_ep0_setup(void *data, uint_fast8_t max_len) +{ + return usb_read_ep0(data, max_len); +} + +int_fast8_t +usb_send_ep0(const void *data, uint_fast8_t len) +{ + return usb_write_packet(0, 1, data, len); +} + +void +usb_stall_ep0(void) +{ + EP0.EPSTATUSSET.reg = USB_DEVICE_EPSTATUS_STALLRQ(3); +} + +static uint8_t set_address; + +void +usb_set_address(uint_fast8_t addr) +{ + writeb(&set_address, addr | USB_DEVICE_DADD_ADDEN); + usb_send_ep0(NULL, 0); +} + +void +usb_set_configure(void) +{ + EP_ACM.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE1(4); + + EP_BULKOUT.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(3); + EP_BULKOUT.EPINTENSET.reg = ( + USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1); + + EP_BULKIN.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE1(3); + EP_BULKIN.EPINTENSET.reg = ( + USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1); +} + +void +usb_request_bootloader(void) +{ + if (CONFIG_FLASH_START) { + // Arduino Zero bootloader hack + irq_disable(); + writel((void*)0x20007FFC, 0x07738135); + NVIC_SystemReset(); + } +} + +void +usbserial_init(void) +{ + // configure usb clock + enable_pclock(USB_GCLK_ID, 0); + // configure USBD+ and USBD- pins + gpio_peripheral(GPIO('A', 24), 'G', 0); + gpio_peripheral(GPIO('A', 25), 'G', 0); + uint16_t trim = (readl((void*)USB_FUSES_TRIM_ADDR) + & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; + uint16_t transp = (readl((void*)USB_FUSES_TRANSP_ADDR) + & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; + uint16_t transn = (readl((void*)USB_FUSES_TRANSN_ADDR) + & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; + USB->DEVICE.PADCAL.reg = (USB_PADCAL_TRIM(trim) | USB_PADCAL_TRANSP(transp) + | USB_PADCAL_TRANSN(transn)); + // Enable USB in device mode + USB->DEVICE.CTRLA.reg = USB_CTRLA_ENABLE; + USB->DEVICE.DESCADD.reg = (uint32_t)usb_desc; + EP0.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(1) | USB_DEVICE_EPCFG_EPTYPE1(1); + EP_ACM.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE1(4); + USB->DEVICE.CTRLB.reg = 0; + // enable irqs + USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_EORST; + NVIC_SetPriority(USB_IRQn, 1); + NVIC_EnableIRQ(USB_IRQn); +} +DECL_INIT(usbserial_init); + +void __visible +USB_Handler(void) +{ + uint8_t s = USB->DEVICE.INTFLAG.reg; + if (s & USB_DEVICE_INTFLAG_EORST) { + USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST; + // Enable endpoint 0 irqs + EP0.EPINTENSET.reg = ( + USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 + | USB_DEVICE_EPINTENSET_RXSTP); + } + + uint16_t ep = USB->DEVICE.EPINTSMRY.reg; + if (ep & (1<<0)) { + uint8_t sts = EP0.EPINTFLAG.reg; + EP0.EPINTFLAG.reg = sts; + if (set_address && sts & USB_DEVICE_EPINTFLAG_TRCPT1) { + // Apply address after last "in" message transmitted + USB->DEVICE.DADD.reg = set_address; + set_address = 0; + } + usb_notify_ep0(); + } + if (ep & (1< /dev/null 2>&1 || true diff --git a/src/sam3/adc.c b/src/sam3/adc.c deleted file mode 100644 index 425d60a9..00000000 --- a/src/sam3/adc.c +++ /dev/null @@ -1,96 +0,0 @@ -// Analog to digital support -// -// Copyright (C) 2016-2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "autoconf.h" // CONFIG_CLOCK_FREQ -#include "board/irq.h" // irq_save -#include "command.h" // shutdown -#include "compiler.h" // ARRAY_SIZE -#include "gpio.h" // gpio_adc_setup -#include "internal.h" // GPIO -#include "sched.h" // sched_shutdown - -static const uint8_t adc_pins[] = { -#if CONFIG_MACH_SAM3X8E - GPIO('A', 2), GPIO('A', 3), GPIO('A', 4), GPIO('A', 6), - GPIO('A', 22), GPIO('A', 23), GPIO('A', 24), GPIO('A', 16), - GPIO('B', 12), GPIO('B', 13), GPIO('B', 17), GPIO('B', 18), - GPIO('B', 19), GPIO('B', 20) -#elif CONFIG_MACH_SAM4S8C - GPIO('A', 17), GPIO('A', 18), GPIO('A', 19), GPIO('A', 20), - GPIO('B', 0), GPIO('B', 1), GPIO('B', 2), GPIO('B', 3), - GPIO('A', 21), GPIO('A', 22), GPIO('C', 13), GPIO('C', 15), - GPIO('C', 12), GPIO('C', 29), GPIO('C', 30) -#endif -}; - -#define ADC_FREQ_MAX 20000000 -DECL_CONSTANT(ADC_MAX, 4095); - -struct gpio_adc -gpio_adc_setup(uint8_t pin) -{ - // Find pin in adc_pins table - int chan; - for (chan=0; ; chan++) { - if (chan >= ARRAY_SIZE(adc_pins)) - shutdown("Not a valid ADC pin"); - if (adc_pins[chan] == pin) - break; - } - - if (!is_enabled_pclock(ID_ADC)) { - // Setup ADC - enable_pclock(ID_ADC); - uint32_t prescal = SystemCoreClock / (2 * ADC_FREQ_MAX) - 1; - ADC->ADC_MR = (ADC_MR_PRESCAL(prescal) - | ADC_MR_STARTUP_SUT768 - | ADC_MR_TRANSFER(1)); - } - return (struct gpio_adc){ .chan = 1 << chan }; -} - -// Try to sample a value. Returns zero if sample ready, otherwise -// returns the number of clock ticks the caller should wait before -// retrying this function. -uint32_t -gpio_adc_sample(struct gpio_adc g) -{ - uint32_t chsr = ADC->ADC_CHSR & 0xffff; - if (!chsr) { - // Start sample - ADC->ADC_CHER = g.chan; - ADC->ADC_CR = ADC_CR_START; - goto need_delay; - } - if (chsr != g.chan) - // Sampling in progress on another channel - goto need_delay; - if (!(ADC->ADC_ISR & ADC_ISR_DRDY)) - // Conversion still in progress - goto need_delay; - // Conversion ready - return 0; -need_delay: - return ADC_FREQ_MAX * 1000ULL / CONFIG_CLOCK_FREQ; -} - -// Read a value; use only after gpio_adc_sample() returns zero -uint16_t -gpio_adc_read(struct gpio_adc g) -{ - ADC->ADC_CHDR = g.chan; - return ADC->ADC_LCDR; -} - -// Cancel a sample that may have been started with gpio_adc_sample() -void -gpio_adc_cancel_sample(struct gpio_adc g) -{ - irqstatus_t flag = irq_save(); - if ((ADC->ADC_CHSR & 0xffff) == g.chan) - gpio_adc_read(g); - irq_restore(flag); -} diff --git a/src/sam3/gpio.c b/src/sam3/gpio.c deleted file mode 100644 index 7e6a1dab..00000000 --- a/src/sam3/gpio.c +++ /dev/null @@ -1,142 +0,0 @@ -// GPIO functions on sam3/sam4 -// -// Copyright (C) 2016-2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "board/irq.h" // irq_save -#include "command.h" // shutdown -#include "compiler.h" // ARRAY_SIZE -#include "gpio.h" // gpio_out_setup -#include "internal.h" // gpio_peripheral -#include "sched.h" // sched_shutdown - -static Pio * const digital_regs[] = { -#if CONFIG_MACH_SAM3X8E - PIOA, PIOB, PIOC, PIOD -#elif CONFIG_MACH_SAM4S8C - PIOA, PIOB, PIOC -#elif CONFIG_MACH_SAM4E8E - PIOA, PIOB, PIOC, PIOD, PIOE -#endif -}; - - -/**************************************************************** - * Pin multiplexing - ****************************************************************/ - -void -gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up) -{ - uint32_t bank = GPIO2PORT(gpio), bit = GPIO2BIT(gpio), pt = ptype - 'A'; - Pio *regs = digital_regs[bank]; - -#if CONFIG_MACH_SAM3X8E - regs->PIO_ABSR = (regs->PIO_ABSR & ~bit) | (pt & 0x01 ? bit : 0); -#else - regs->PIO_ABCDSR[0] = (regs->PIO_ABCDSR[0] & ~bit) | (pt & 0x01 ? bit : 0); - regs->PIO_ABCDSR[1] = (regs->PIO_ABCDSR[1] & ~bit) | (pt & 0x02 ? bit : 0); -#endif - - if (pull_up > 0) - regs->PIO_PUER = bit; - else - regs->PIO_PUDR = bit; - regs->PIO_PDR = bit; -} - - -/**************************************************************** - * General Purpose Input Output (GPIO) pins - ****************************************************************/ - -struct gpio_out -gpio_out_setup(uint8_t pin, uint8_t val) -{ - if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs)) - goto fail; - Pio *regs = digital_regs[GPIO2PORT(pin)]; - struct gpio_out g = { .regs=regs, .bit=GPIO2BIT(pin) }; - gpio_out_reset(g, val); - return g; -fail: - shutdown("Not an output pin"); -} - -void -gpio_out_reset(struct gpio_out g, uint8_t val) -{ - Pio *regs = g.regs; - irqstatus_t flag = irq_save(); - if (val) - regs->PIO_SODR = g.bit; - else - regs->PIO_CODR = g.bit; - regs->PIO_OER = g.bit; - regs->PIO_OWER = g.bit; - regs->PIO_PER = g.bit; - regs->PIO_PUDR = g.bit; - irq_restore(flag); -} - -void -gpio_out_toggle_noirq(struct gpio_out g) -{ - Pio *regs = g.regs; - regs->PIO_ODSR ^= g.bit; -} - -void -gpio_out_toggle(struct gpio_out g) -{ - irqstatus_t flag = irq_save(); - gpio_out_toggle_noirq(g); - irq_restore(flag); -} - -void -gpio_out_write(struct gpio_out g, uint8_t val) -{ - Pio *regs = g.regs; - if (val) - regs->PIO_SODR = g.bit; - else - regs->PIO_CODR = g.bit; -} - - -struct gpio_in -gpio_in_setup(uint8_t pin, int8_t pull_up) -{ - if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs)) - goto fail; - uint32_t port = GPIO2PORT(pin); - enable_pclock(ID_PIOA + port); - struct gpio_in g = { .regs=digital_regs[port], .bit=GPIO2BIT(pin) }; - gpio_in_reset(g, pull_up); - return g; -fail: - shutdown("Not an input pin"); -} - -void -gpio_in_reset(struct gpio_in g, int8_t pull_up) -{ - Pio *regs = g.regs; - irqstatus_t flag = irq_save(); - if (pull_up) - regs->PIO_PUER = g.bit; - else - regs->PIO_PUDR = g.bit; - regs->PIO_ODR = g.bit; - regs->PIO_PER = g.bit; - irq_restore(flag); -} - -uint8_t -gpio_in_read(struct gpio_in g) -{ - Pio *regs = g.regs; - return !!(regs->PIO_PDSR & g.bit); -} diff --git a/src/sam3/gpio.h b/src/sam3/gpio.h deleted file mode 100644 index e106e094..00000000 --- a/src/sam3/gpio.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef __SAM3_GPIO_H -#define __SAM3_GPIO_H - -#include // uint32_t - -struct gpio_out { - void *regs; - uint32_t bit; -}; -struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val); -void gpio_out_reset(struct gpio_out g, uint8_t val); -void gpio_out_toggle_noirq(struct gpio_out g); -void gpio_out_toggle(struct gpio_out g); -void gpio_out_write(struct gpio_out g, uint8_t val); - -struct gpio_in { - void *regs; - uint32_t bit; -}; -struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up); -void gpio_in_reset(struct gpio_in g, int8_t pull_up); -uint8_t gpio_in_read(struct gpio_in g); - -struct gpio_adc { - uint32_t chan; -}; -struct gpio_adc gpio_adc_setup(uint8_t pin); -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 *spidev; - uint32_t cfg; -}; -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); - -struct i2c_config { - void *twi; - uint8_t addr; -}; - -struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); -void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); -void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg - , uint8_t read_len, uint8_t *read); - -#endif // gpio.h diff --git a/src/sam3/i2c.c b/src/sam3/i2c.c deleted file mode 100644 index 910471fd..00000000 --- a/src/sam3/i2c.c +++ /dev/null @@ -1,175 +0,0 @@ -// SAM4 I2C Port -// -// Copyright (C) 2018 Florian Heilmann -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "board/misc.h" // timer_from_us -#include "command.h" // shutdown -#include "gpio.h" // i2c_setup -#include "internal.h" // gpio_peripheral -#include "sched.h" // sched_shutdown - -// I2C pin definitions -#if CONFIG_MACH_SAM3X8E -#define TWI0_SCL_GPIO GPIO('A', 18) -#define TWI0_SDA_GPIO GPIO('A', 17) -#define TWI1_SCL_GPIO GPIO('B', 13) -#define TWI1_SDA_GPIO GPIO('B', 12) -#elif CONFIG_MACH_SAM4S8C || CONFIG_MACH_SAM4E8E -#define TWI0_SCL_GPIO GPIO('A', 4) -#define TWI0_SDA_GPIO GPIO('A', 3) -#define TWI1_SCL_GPIO GPIO('B', 5) -#define TWI1_SDA_GPIO GPIO('B', 4) -#endif - -static void -i2c_init(Twi *p_twi, uint32_t rate) -{ - enable_pclock(p_twi == TWI0 ? ID_TWI0 : ID_TWI1); - if (p_twi == TWI0) { - gpio_peripheral(TWI0_SCL_GPIO, 'A', 0); - gpio_peripheral(TWI0_SDA_GPIO, 'A', 0); - } else { - gpio_peripheral(TWI1_SCL_GPIO, 'A', 0); - gpio_peripheral(TWI1_SDA_GPIO, 'A', 0); - } - p_twi->TWI_IDR = 0xFFFFFFFF; - (void)p_twi->TWI_SR; - p_twi->TWI_CR = TWI_CR_SWRST; - (void)p_twi->TWI_RHR; - p_twi->TWI_CR = TWI_CR_MSDIS; - p_twi->TWI_CR = TWI_CR_SVDIS; - p_twi->TWI_CR = TWI_CR_MSEN; - - uint32_t cldiv = 0; - uint32_t chdiv = 0; - uint32_t ckdiv = 0; - - cldiv = SystemCoreClock / ((rate > 384000 ? 384000 : rate) * 4) - 4; - - while((cldiv > 255) && (ckdiv < 7)) { - ckdiv++; - cldiv /= 2; - } - - if (rate > 348000) { - chdiv = SystemCoreClock / ((2 * rate - 384000) * 4) - 4; - while((chdiv > 255) && (ckdiv < 7)) { - ckdiv++; - chdiv /= 2; - } - } else { - chdiv = cldiv; - } - p_twi->TWI_CWGR = TWI_CWGR_CLDIV(cldiv) | \ - TWI_CWGR_CHDIV(chdiv) | \ - TWI_CWGR_CKDIV(ckdiv); -} - -static uint32_t -addr_to_u32(uint8_t addr_len, uint8_t *addr) -{ - uint32_t address = addr[0]; - if (addr_len > 1) { - address <<= 8; - address |= addr[1]; - } - if (addr_len > 2) { - address <<= 8; - address |= addr[2]; - } - if (addr_len > 3) { - shutdown("Addresses larger than 3 bytes are not supported"); - } - return address; -} - -struct i2c_config -i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) -{ - if ((bus > 1) | (rate > 400000)) - shutdown("Invalid i2c_setup parameters!"); - Twi *p_twi = (bus == 0) ? TWI0 : TWI1; - i2c_init(p_twi, rate); - return (struct i2c_config){ .twi=p_twi, .addr=addr}; -} - -void -i2c_write(struct i2c_config config, - uint8_t write_len, uint8_t *write) -{ - Twi *p_twi = config.twi; - uint32_t status; - uint32_t bytes_to_send = write_len; - p_twi->TWI_MMR = TWI_MMR_DADR(config.addr); - for(;;) { - status = p_twi->TWI_SR; - if (status & TWI_SR_NACK) - shutdown("I2C NACK error encountered!"); - if (!(status & TWI_SR_TXRDY)) - continue; - if (!bytes_to_send) - break; - p_twi->TWI_THR = *write++; - bytes_to_send--; - } - p_twi->TWI_CR = TWI_CR_STOP; - while(!(p_twi->TWI_SR& TWI_SR_TXCOMP)) { - } -} - -static void -i2c_wait(Twi* p_twi, uint32_t bit, uint32_t timeout) -{ - for (;;) { - uint32_t flags = p_twi->TWI_SR; - if (flags & bit) - break; - if (!timer_is_before(timer_read_time(), timeout)) - shutdown("I2C timeout occured"); - } -} - -void -i2c_read(struct i2c_config config, - uint8_t reg_len, uint8_t *reg, - uint8_t read_len, uint8_t *read) -{ - Twi *p_twi = config.twi; - uint32_t status; - uint32_t bytes_to_send=read_len; - uint8_t stop = 0; - p_twi->TWI_MMR = 0; - p_twi->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_DADR(config.addr) | - ((reg_len << TWI_MMR_IADRSZ_Pos) & - TWI_MMR_IADRSZ_Msk); - p_twi->TWI_IADR = 0; - p_twi->TWI_IADR = addr_to_u32(reg_len, reg); - if (bytes_to_send == 1) { - p_twi->TWI_CR = TWI_CR_START | TWI_CR_STOP; - stop = 1; - } else { - p_twi->TWI_CR = TWI_CR_START; - stop = 0; - } - while (bytes_to_send > 0) { - status = p_twi->TWI_SR; - if (status & TWI_SR_NACK) { - shutdown("I2C NACK error encountered!"); - } - if (bytes_to_send == 1 && !stop) { - p_twi->TWI_CR = TWI_CR_STOP; - stop = 1; - } - i2c_wait(p_twi, TWI_SR_RXRDY, timer_read_time() + timer_from_us(5000)); - if (!(status & TWI_SR_RXRDY)) { - continue; - } - *read++ = p_twi->TWI_RHR; - bytes_to_send--; - } - while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) {} - (void)p_twi->TWI_SR; -} diff --git a/src/sam3/internal.h b/src/sam3/internal.h deleted file mode 100644 index dd94a609..00000000 --- a/src/sam3/internal.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __SAM3_INTERNAL_H -#define __SAM3_INTERNAL_H -// Local definitions for sam3/sam4 code - -#include // uint32_t -#include "autoconf.h" // CONFIG_MACH_SAM3X8E - -#if CONFIG_MACH_SAM3X8E -#include "sam3x8e.h" -#elif CONFIG_MACH_SAM4S8C -#include "sam4s.h" -#elif CONFIG_MACH_SAM4E8E -#include "sam4e.h" -#endif - -#define GPIO(PORT, NUM) (((PORT)-'A') * 32 + (NUM)) -#define GPIO2PORT(PIN) ((PIN) / 32) -#define GPIO2BIT(PIN) (1<<((PIN) % 32)) - -void gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up); -int is_enabled_pclock(uint32_t id); -void enable_pclock(uint32_t id); - -#endif // internal.h diff --git a/src/sam3/main.c b/src/sam3/main.c deleted file mode 100644 index 1b9375db..00000000 --- a/src/sam3/main.c +++ /dev/null @@ -1,77 +0,0 @@ -// Main starting point for SAM3/SAM4 boards -// -// Copyright (C) 2016-2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "board/irq.h" // irq_disable -#include "command.h" // DECL_CONSTANT -#include "internal.h" // WDT -#include "sched.h" // sched_main - -DECL_CONSTANT(MCU, CONFIG_MCU); - - -/**************************************************************** - * watchdog handler - ****************************************************************/ - -void -watchdog_reset(void) -{ - WDT->WDT_CR = 0xA5000001; -} -DECL_TASK(watchdog_reset); - -void -watchdog_init(void) -{ - uint32_t timeout = 500 * 32768 / 128 / 1000; // 500ms timeout - WDT->WDT_MR = WDT_MR_WDRSTEN | WDT_MR_WDV(timeout) | WDT_MR_WDD(timeout); -} -DECL_INIT(watchdog_init); - - -/**************************************************************** - * misc functions - ****************************************************************/ - -// Check if a peripheral clock has been enabled -int -is_enabled_pclock(uint32_t id) -{ - if (id < 32) - return !!(PMC->PMC_PCSR0 & (1 << id)); - else - return !!(PMC->PMC_PCSR1 & (1 << (id - 32))); -} - -// Enable a peripheral clock -void -enable_pclock(uint32_t id) -{ - if (id < 32) - PMC->PMC_PCER0 = 1 << id; - else - PMC->PMC_PCER1 = 1 << (id - 32); -} - -void -command_reset(uint32_t *args) -{ - irq_disable(); - RSTC->RSTC_CR = ((0xA5 << RSTC_CR_KEY_Pos) | RSTC_CR_PROCRST - | RSTC_CR_PERRST); - for (;;) - ; -} -DECL_COMMAND_FLAGS(command_reset, HF_IN_SHUTDOWN, "reset"); - -// Main entry point -int -main(void) -{ - SystemInit(); - sched_main(); - return 0; -} diff --git a/src/sam3/sam4_cache.c b/src/sam3/sam4_cache.c deleted file mode 100644 index 75459660..00000000 --- a/src/sam3/sam4_cache.c +++ /dev/null @@ -1,16 +0,0 @@ -// SAM4 cache enable -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "sam4e.h" // CMCC -#include "sched.h" // DECL_INIT - -void -sam4_cache_init(void) -{ - if (!(CMCC->CMCC_SR & CMCC_SR_CSTS)) - CMCC->CMCC_CTRL = CMCC_CTRL_CEN; -} -DECL_INIT(sam4_cache_init); diff --git a/src/sam3/sam4_usb.c b/src/sam3/sam4_usb.c deleted file mode 100644 index ecf8e544..00000000 --- a/src/sam3/sam4_usb.c +++ /dev/null @@ -1,238 +0,0 @@ -// Hardware interface to SAM4 USB Device Port -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include // NULL -#include "board/irq.h" // irq_disable -#include "board/usb_cdc.h" // usb_notify_ep0 -#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN -#include "internal.h" // UDP -#include "sched.h" // DECL_INIT - -#define CSR_EP0 (UDP_CSR_EPTYPE_CTRL | UDP_CSR_EPEDS) -#define CSR_ACM (UDP_CSR_EPTYPE_INT_IN | UDP_CSR_EPEDS) -#define CSR_BULK_OUT (UDP_CSR_EPTYPE_BULK_OUT | UDP_CSR_EPEDS) -#define CSR_BULK_IN (UDP_CSR_EPTYPE_BULK_IN | UDP_CSR_EPEDS) - -static void -usb_write_packet(uint32_t ep, const uint8_t *data, uint32_t len) -{ - while (len--) - UDP->UDP_FDR[ep] = *data++; -} - -static uint32_t -usb_read_packet(uint32_t ep, uint32_t csr, uint8_t *data, uint32_t max_len) -{ - uint32_t pk_len = (csr & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos; - uint32_t len = pk_len < max_len ? pk_len : max_len, orig_len = len; - while (len--) - *data++ = UDP->UDP_FDR[ep]; - return orig_len; -} - -int_fast8_t -usb_read_bulk_out(void *data, uint_fast8_t max_len) -{ - static uint32_t next_bk = UDP_CSR_RX_DATA_BK0; - const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT - | UDP_CSR_TXCOMP); - uint32_t csr = UDP->UDP_CSR[USB_CDC_EP_BULK_OUT]; - uint32_t bk = csr & (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); - if (!bk) { - // Not ready to receive data - if (csr & other_irqs) - UDP->UDP_CSR[USB_CDC_EP_BULK_OUT] = ( - CSR_BULK_OUT | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); - UDP->UDP_IER = 1<UDP_CSR[USB_CDC_EP_BULK_OUT] = CSR_BULK_OUT | next_bk; - return len; -} - -int_fast8_t -usb_send_bulk_in(void *data, uint_fast8_t len) -{ - const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT - | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); - uint32_t csr = UDP->UDP_CSR[USB_CDC_EP_BULK_IN]; - if (csr & UDP_CSR_TXPKTRDY) { - // Not ready to send - if (csr & other_irqs) - UDP->UDP_CSR[USB_CDC_EP_BULK_IN] = CSR_BULK_IN | UDP_CSR_TXCOMP; - UDP->UDP_IER = 1<UDP_CSR[USB_CDC_EP_BULK_IN] = CSR_BULK_IN | UDP_CSR_TXPKTRDY; - return len; -} - -int_fast8_t -usb_read_ep0(void *data, uint_fast8_t max_len) -{ - const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT - | UDP_CSR_TXCOMP | UDP_CSR_RX_DATA_BK1); - uint32_t csr = UDP->UDP_CSR[0]; - if (csr & other_irqs) - return -2; - if (!(csr & UDP_CSR_RX_DATA_BK0)) { - // Not ready to receive data - UDP->UDP_IER = 1<<0; - return -1; - } - uint32_t len = usb_read_packet(0, csr, data, max_len); - if (UDP->UDP_CSR[0] & other_irqs) - return -2; - UDP->UDP_CSR[0] = CSR_EP0 | other_irqs; - return len; -} - -int_fast8_t -usb_read_ep0_setup(void *data, uint_fast8_t max_len) -{ - const uint32_t other_irqs = (UDP_CSR_STALLSENT | UDP_CSR_TXCOMP - | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); - uint32_t csr = UDP->UDP_CSR[0]; - if (!(csr & UDP_CSR_RXSETUP)) { - // No data ready to read - if (csr & other_irqs) - UDP->UDP_CSR[0] = CSR_EP0 | UDP_CSR_RXSETUP; - UDP->UDP_IER = 1<<0; - return -1; - } - uint32_t len = usb_read_packet(0, csr, data, max_len); - uint32_t dir = *(uint8_t*)data & 0x80 ? UDP_CSR_DIR : 0; - UDP->UDP_CSR[0] = CSR_EP0 | dir; - return len; -} - -int_fast8_t -usb_send_ep0(const void *data, uint_fast8_t len) -{ - const uint32_t other_irqs = (UDP_CSR_RXSETUP | UDP_CSR_STALLSENT - | UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1); - uint32_t csr = UDP->UDP_CSR[0]; - if (csr & other_irqs) - return -2; - if (csr & UDP_CSR_TXPKTRDY) { - // Not ready to send - UDP->UDP_IER = 1<<0; - return -1; - } - usb_write_packet(0, data, len); - uint32_t dir = csr & UDP_CSR_DIR; - UDP->UDP_CSR[0] = CSR_EP0 | dir | UDP_CSR_TXPKTRDY | other_irqs; - return len; -} - -void -usb_stall_ep0(void) -{ - UDP->UDP_CSR[0] = CSR_EP0 | UDP_CSR_FORCESTALL; - UDP->UDP_IER = 1<<0; -} - -static uint32_t set_address; - -void -usb_set_address(uint_fast8_t addr) -{ - set_address = addr | UDP_FADDR_FEN; - usb_send_ep0(NULL, 0); - UDP->UDP_IER = 1<<0; -} - -void -usb_set_configure(void) -{ - UDP->UDP_CSR[USB_CDC_EP_ACM] = CSR_ACM; - UDP->UDP_CSR[USB_CDC_EP_BULK_OUT] = CSR_BULK_OUT; - UDP->UDP_CSR[USB_CDC_EP_BULK_IN] = CSR_BULK_IN; - UDP->UDP_GLB_STAT |= UDP_GLB_STAT_CONFG; -} - -#if CONFIG_MACH_SAM4S8C -#define EFC_HW EFC0 -#elif CONFIG_MACH_SAM4E8E -#define EFC_HW EFC -#endif - -void noinline __aligned(16) // align for predictable flash code access -usb_request_bootloader(void) -{ - irq_disable(); - // Request boot from ROM (instead of boot from flash) - while ((EFC_HW->EEFC_FSR & EEFC_FSR_FRDY) == 0) - ; - EFC_HW->EEFC_FCR = (EEFC_FCR_FCMD_CGPB | EEFC_FCR_FARG(1) - | EEFC_FCR_FKEY_PASSWD); - while ((EFC_HW->EEFC_FSR & EEFC_FSR_FRDY) == 0) - ; - // Reboot - RSTC->RSTC_CR = RSTC_CR_KEY(0xA5) | RSTC_CR_PROCRST | RSTC_CR_PERRST; - for (;;) - ; -} - -void -usbserial_init(void) -{ - // Enable clocks - enable_pclock(ID_UDP); - PMC->PMC_USB = PMC_USB_USBDIV(5 - 1); // PLLA=240Mhz; divide by 5 for 48Mhz - PMC->PMC_SCER = PMC_SCER_UDP; - - // Enable USB pullup - UDP->UDP_TXVC = UDP_TXVC_PUON | UDP_TXVC_TXVDIS; - - // Enable interrupts - UDP->UDP_ICR = 0xffffffff; - NVIC_SetPriority(UDP_IRQn, 1); - NVIC_EnableIRQ(UDP_IRQn); -} -DECL_INIT(usbserial_init); - -// Configure endpoint 0 after usb reset completes -static void -handle_end_reset(void) -{ - UDP->UDP_ICR = UDP_ISR_ENDBUSRES; - - UDP->UDP_CSR[0] = CSR_EP0; - UDP->UDP_IER = 1<<0; - - UDP->UDP_TXVC = UDP_TXVC_PUON; -} - -void __visible -UDP_Handler(void) -{ - uint32_t s = UDP->UDP_ISR; - UDP->UDP_IDR = s; - if (s & UDP_ISR_ENDBUSRES) - handle_end_reset(); - if (s & UDP_ISR_RXRSM) - UDP->UDP_ICR = UDP_ISR_RXRSM; - if (s & (1<<0)) { - usb_notify_ep0(); - - if (set_address && UDP->UDP_CSR[0] & UDP_CSR_TXCOMP) { - // Ack from set_address command sent - now update address - UDP->UDP_FADDR = set_address; - UDP->UDP_GLB_STAT |= UDP_GLB_STAT_FADDEN; - set_address = 0; - } - } - if (s & (1< -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "autoconf.h" // CONFIG_CLOCK_FREQ -#include "command.h" // shutdown -#include "gpio.h" // gpio_adc_setup -#include "internal.h" // GPIO -#include "sam4e.h" // AFEC0 -#include "sched.h" // sched_shutdown - -static const uint8_t afec_pins[] = { - //remove first channel, since it offsets the channel number: GPIO('A', 8), - GPIO('A', 17), GPIO('A', 18), GPIO('A', 19), - GPIO('A', 20), GPIO('B', 0), GPIO('B', 1), GPIO('C', 13), - GPIO('C', 15), GPIO('C', 12), GPIO('C', 29), GPIO('C', 30), - GPIO('C', 31), GPIO('C', 26), GPIO('C', 27), GPIO('C',0), - // AFEC1 - GPIO('B', 2), GPIO('B', 3), GPIO('A', 21), GPIO('A', 22), - GPIO('C', 1), GPIO('C', 2), GPIO('C', 3), GPIO('C', 4), -}; - -#define AFEC1_START 15 // The first 15 pins are on afec0 - -static inline struct gpio_adc -pin_to_gpio_adc(uint8_t pin) -{ - int chan; - for (chan=0; ; chan++) { - if (chan >= ARRAY_SIZE(afec_pins)) - shutdown("Not a valid ADC pin"); - if (afec_pins[chan] == pin) { - break; - } - } - return (struct gpio_adc){ .chan=chan }; -} - -static inline Afec * -gpio_adc_to_afec(struct gpio_adc g) -{ - return (g.chan >= AFEC1_START ? AFEC1 : AFEC0); -} - -static inline uint32_t -gpio_adc_to_afec_chan(struct gpio_adc g) -{ - return (g.chan >= AFEC1_START ? g.chan - AFEC1_START : g.chan); -} - -#define ADC_FREQ_MAX 6000000UL -DECL_CONSTANT(ADC_MAX, 4095); - -static int -init_afec(Afec* afec) { - - // Enable PMC - enable_pclock(afec == AFEC0 ? ID_AFEC0 : ID_AFEC1); - - // If busy, return busy - if ((afec->AFE_ISR & AFE_ISR_DRDY) == AFE_ISR_DRDY) { - return -1; - } - - // Reset - afec->AFE_CR = AFE_CR_SWRST; - - // Configure afec - afec->AFE_MR = AFE_MR_ANACH_ALLOWED | \ - AFE_MR_PRESCAL (SystemCoreClock / (2 * ADC_FREQ_MAX) -1) | \ - AFE_MR_SETTLING_AST3 | \ - AFE_MR_TRACKTIM(2) | \ - AFE_MR_TRANSFER(1) | \ - AFE_MR_STARTUP_SUT64; - afec->AFE_EMR = AFE_EMR_TAG | \ - AFE_EMR_RES_NO_AVERAGE | \ - AFE_EMR_STM; - afec->AFE_ACR = AFE_ACR_IBCTL(1); - - // Disable interrupts - afec->AFE_IDR = 0xDF00803F; - - // Disable SW triggering - uint32_t mr = afec->AFE_MR; - - mr &= ~(AFE_MR_TRGSEL_Msk | AFE_MR_TRGEN | AFE_MR_FREERUN_ON); - mr |= AFE_MR_TRGEN_DIS; - afec->AFE_MR = mr; - - return 0; -} - -void -gpio_afec_init(void) { - - while(init_afec(AFEC0) != 0) { - (void)(AFEC0->AFE_LCDR & AFE_LCDR_LDATA_Msk); - } - while(init_afec(AFEC1) != 0) { - (void)(AFEC1->AFE_LCDR & AFE_LCDR_LDATA_Msk); - } - -} -DECL_INIT(gpio_afec_init); - -struct gpio_adc -gpio_adc_setup(uint8_t pin) -{ - struct gpio_adc adc_pin = pin_to_gpio_adc(pin); - Afec *afec = gpio_adc_to_afec(adc_pin); - uint32_t afec_chan = gpio_adc_to_afec_chan(adc_pin); - - //config channel - uint32_t reg = afec->AFE_DIFFR; - reg &= ~(1u << afec_chan); - afec->AFE_DIFFR = reg; - reg = afec->AFE_CGR; - reg &= ~(0x03u << (2 * afec_chan)); - reg |= 1 << (2 * afec_chan); - afec->AFE_CGR = reg; - - // Configure channel - // afec_ch_get_config_defaults(&ch_cfg); - // afec_ch_set_config(afec, afec_chan, &ch_cfg); - // Remove default internal offset from channel - // See Atmel Appnote AT03078 Section 1.5 - afec->AFE_CSELR = afec_chan; - afec->AFE_COCR = (0x800 & AFE_COCR_AOFF_Msk); - - // Enable and calibrate Channel - afec->AFE_CHER = 1 << afec_chan; - - reg = afec->AFE_CHSR; - afec->AFE_CDOR = reg; - afec->AFE_CR = AFE_CR_AUTOCAL; - - return adc_pin; -} - -enum { AFE_DUMMY=0xff }; -uint8_t active_channel = AFE_DUMMY; - -// Try to sample a value. Returns zero if sample ready, otherwise -// returns the number of clock ticks the caller should wait before -// retrying this function. -uint32_t -gpio_adc_sample(struct gpio_adc g) -{ - Afec *afec = gpio_adc_to_afec(g); - uint32_t afec_chan = gpio_adc_to_afec_chan(g); - if (active_channel == g.chan) { - if ((afec->AFE_ISR & AFE_ISR_DRDY) - && (afec->AFE_ISR & (1 << afec_chan))) { - // Sample now ready - return 0; - } else { - // Busy - goto need_delay; - } - } else if (active_channel != AFE_DUMMY) { - goto need_delay; - } - - afec->AFE_CHDR = 0x803F; // Disable all channels - afec->AFE_CHER = 1 << afec_chan; - - active_channel = g.chan; - - for (uint32_t chan = 0; chan < 16; ++chan) - { - if ((afec->AFE_ISR & (1 << chan)) != 0) - { - afec->AFE_CSELR = chan; - (void)(afec->AFE_CDR); - } - } - afec->AFE_CR = AFE_CR_START; - -need_delay: - return ADC_FREQ_MAX * 10000ULL / CONFIG_CLOCK_FREQ; // about 400 mcu clock cycles or 40 afec cycles -} - -// Read a value; use only after gpio_adc_sample() returns zero -uint16_t -gpio_adc_read(struct gpio_adc g) -{ - Afec *afec = gpio_adc_to_afec(g); - uint32_t afec_chan = gpio_adc_to_afec_chan(g); - active_channel = AFE_DUMMY; - afec->AFE_CSELR = afec_chan; - return afec->AFE_CDR; -} - -// Cancel a sample that may have been started with gpio_adc_sample() -void -gpio_adc_cancel_sample(struct gpio_adc g) -{ - if (active_channel == g.chan) { - active_channel = AFE_DUMMY; - } -} diff --git a/src/sam3/sam4s_sysinit.c b/src/sam3/sam4s_sysinit.c deleted file mode 100644 index 41a2dcfe..00000000 --- a/src/sam3/sam4s_sysinit.c +++ /dev/null @@ -1,65 +0,0 @@ -// This code is from lib/sam4e/gcc/system_sam4e.c - it is unclear why -// it is not defined in the Atmel sam4s cmsis code. - -#include "internal.h" - -/* Clock Settings (120MHz) */ -#define SYS_BOARD_OSCOUNT (CKGR_MOR_MOSCXTST(0x8U)) -#define SYS_BOARD_PLLAR (CKGR_PLLAR_ONE \ - | CKGR_PLLAR_MULA(0x13U) \ - | CKGR_PLLAR_PLLACOUNT(0x3fU) \ - | CKGR_PLLAR_DIVA(0x1U)) -#define SYS_BOARD_MCKR (PMC_MCKR_PRES_CLK_2 | PMC_MCKR_CSS_PLLA_CLK) - -#define SYS_CKGR_MOR_KEY_VALUE CKGR_MOR_KEY(0x37) /* Key to unlock MOR register */ - -uint32_t SystemCoreClock = CHIP_FREQ_MAINCK_RC_4MHZ; - -void SystemInit( void ) -{ - /* Set FWS according to SYS_BOARD_MCKR configuration */ - EFC0->EEFC_FMR = EEFC_FMR_FWS(5); - - /* Initialize main oscillator */ - if ( !(PMC->CKGR_MOR & CKGR_MOR_MOSCSEL) ) - { - PMC->CKGR_MOR = SYS_CKGR_MOR_KEY_VALUE | SYS_BOARD_OSCOUNT | CKGR_MOR_MOSCRCEN | CKGR_MOR_MOSCXTEN; - - while ( !(PMC->PMC_SR & PMC_SR_MOSCXTS) ) - { - } - } - - /* Switch to 3-20MHz Xtal oscillator */ - PMC->CKGR_MOR = SYS_CKGR_MOR_KEY_VALUE | SYS_BOARD_OSCOUNT | CKGR_MOR_MOSCRCEN | CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCSEL; - - while ( !(PMC->PMC_SR & PMC_SR_MOSCSELS) ) - { - } - - PMC->PMC_MCKR = (PMC->PMC_MCKR & ~(uint32_t)PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK; - - while ( !(PMC->PMC_SR & PMC_SR_MCKRDY) ) - { - } - - /* Initialize PLLA */ - PMC->CKGR_PLLAR = SYS_BOARD_PLLAR; - while ( !(PMC->PMC_SR & PMC_SR_LOCKA) ) - { - } - - /* Switch to main clock */ - PMC->PMC_MCKR = (SYS_BOARD_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK; - while ( !(PMC->PMC_SR & PMC_SR_MCKRDY) ) - { - } - - /* Switch to PLLA */ - PMC->PMC_MCKR = SYS_BOARD_MCKR; - while ( !(PMC->PMC_SR & PMC_SR_MCKRDY) ) - { - } - - SystemCoreClock = CHIP_FREQ_CPU_MAX; -} diff --git a/src/sam3/sam4s_timer.c b/src/sam3/sam4s_timer.c deleted file mode 100644 index a051f08c..00000000 --- a/src/sam3/sam4s_timer.c +++ /dev/null @@ -1,118 +0,0 @@ -// SAM4s 16bit timer interrupt scheduling -// -// Copyright (C) 2016-2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "board/io.h" // readl -#include "board/irq.h" // irq_disable -#include "board/misc.h" // timer_read_time -#include "board/timer_irq.h" // timer_dispatch_many -#include "command.h" // DECL_SHUTDOWN -#include "internal.h" // TC0 -#include "sched.h" // DECL_INIT - - -/**************************************************************** - * Low level timer code - ****************************************************************/ - -// Get the 16bit timer value -static uint32_t -timer_get(void) -{ - return TC0->TC_CHANNEL[0].TC_CV; -} - -// Set the next irq time -static void -timer_set(uint32_t value) -{ - TC0->TC_CHANNEL[0].TC_RA = value; -} - -// Activate timer dispatch as soon as possible -void -timer_kick(void) -{ - timer_set(timer_read_time() + 50); -} - - -/**************************************************************** - * 16bit hardware timer to 32bit conversion - ****************************************************************/ - -// High bits of timer (top 17 bits) -static uint32_t timer_high; - -// Return the current time (in absolute clock ticks). -uint32_t __always_inline -timer_read_time(void) -{ - uint32_t th = readl(&timer_high); - uint32_t cur = timer_get(); - // Combine timer_high (high 17 bits) and current time (low 16 - // bits) using method that handles rollovers correctly. - return (th ^ cur) + (th & 0x8000); -} - -// Update timer_high every 0x8000 clock ticks -static uint_fast8_t -timer_event(struct timer *t) -{ - timer_high += 0x8000; - t->waketime = timer_high + 0x8000; - return SF_RESCHEDULE; -} -static struct timer wrap_timer = { - .func = timer_event, - .waketime = 0x8000, -}; - -void -timer_reset(void) -{ - sched_add_timer(&wrap_timer); -} -DECL_SHUTDOWN(timer_reset); - - -/**************************************************************** - * Timer init - ****************************************************************/ - -void -timer_init(void) -{ - TcChannel *tc = &TC0->TC_CHANNEL[0]; - // Reset the timer - tc->TC_CCR = TC_CCR_CLKDIS; - tc->TC_IDR = 0xFFFFFFFF; - // Enable it - enable_pclock(ID_TC0); - tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK2; - tc->TC_IER = TC_IER_CPAS; - NVIC_SetPriority(TC0_IRQn, 2); - NVIC_EnableIRQ(TC0_IRQn); - timer_kick(); - timer_reset(); - tc->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; -} -DECL_INIT(timer_init); - - -/**************************************************************** - * Main timer dispatch irq handler - ****************************************************************/ - -// IRQ handler -void __visible __aligned(16) // aligning helps stabilize perf benchmarks -TC0_Handler(void) -{ - irq_disable(); - uint32_t next = timer_dispatch_many(); - timer_set(next); - TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending - irq_enable(); -} diff --git a/src/sam3/serial.c b/src/sam3/serial.c deleted file mode 100644 index c87539ad..00000000 --- a/src/sam3/serial.c +++ /dev/null @@ -1,77 +0,0 @@ -// sam3/sam4 serial port -// -// Copyright (C) 2016-2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "autoconf.h" // CONFIG_SERIAL_BAUD -#include "board/serial_irq.h" // serial_rx_data -#include "internal.h" // gpio_peripheral -#include "sched.h" // DECL_INIT - -// Serial port pins -#if CONFIG_MACH_SAM3X8E -#define Serial_IRQ_Handler UART_Handler -static Uart * const Port = UART; -static const uint32_t Pmc_id = ID_UART, Irq_id = UART_IRQn; -static const uint32_t rx_pin = GPIO('A', 8); -static const uint32_t tx_pin = GPIO('A', 9); -#elif CONFIG_MACH_SAM4S8C -#define Serial_IRQ_Handler UART1_Handler -static Uart * const Port = UART1; -static const uint32_t Pmc_id = ID_UART1, Irq_id = UART1_IRQn; -static const uint32_t rx_pin = GPIO('B', 2); -static const uint32_t tx_pin = GPIO('B', 3); -#elif CONFIG_MACH_SAM4E8E -#define Serial_IRQ_Handler UART0_Handler -static Uart * const Port = UART0; -static const uint32_t Pmc_id = ID_UART0, Irq_id = UART0_IRQn; -static const uint32_t rx_pin = GPIO('A', 9); -static const uint32_t tx_pin = GPIO('A', 10); -#endif - -void -serial_init(void) -{ - gpio_peripheral(rx_pin, 'A', 1); - gpio_peripheral(tx_pin, 'A', 0); - - // Reset uart - enable_pclock(Pmc_id); - Port->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS; - Port->UART_CR = (UART_CR_RSTRX | UART_CR_RSTTX - | UART_CR_RXDIS | UART_CR_TXDIS); - Port->UART_IDR = 0xFFFFFFFF; - - // Enable uart - Port->UART_MR = (US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO - | UART_MR_CHMODE_NORMAL); - Port->UART_BRGR = SystemCoreClock / (16 * CONFIG_SERIAL_BAUD); - Port->UART_IER = UART_IER_RXRDY; - NVIC_EnableIRQ(Irq_id); - NVIC_SetPriority(Irq_id, 0); - Port->UART_CR = UART_CR_RXEN | UART_CR_TXEN; -} -DECL_INIT(serial_init); - -void __visible -Serial_IRQ_Handler(void) -{ - uint32_t status = Port->UART_SR; - if (status & UART_SR_RXRDY) - serial_rx_byte(Port->UART_RHR); - if (status & UART_SR_TXRDY) { - uint8_t data; - int ret = serial_get_tx_byte(&data); - if (ret) - Port->UART_IDR = UART_IDR_TXRDY; - else - Port->UART_THR = data; - } -} - -void -serial_enable_tx_irq(void) -{ - Port->UART_IER = UART_IDR_TXRDY; -} diff --git a/src/sam3/spi.c b/src/sam3/spi.c deleted file mode 100644 index 5f79a02c..00000000 --- a/src/sam3/spi.c +++ /dev/null @@ -1,285 +0,0 @@ -// SPI transmissions on sam3 -// -// Copyright (C) 2018 Petri Honkala -// Copyright (C) 2018 Florian Heilmann -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "command.h" // shutdown -#include "gpio.h" // spi_setup -#include "internal.h" // gpio_peripheral -#include "sched.h" // sched_shutdown - - -/**************************************************************** - * 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' }, - { USART0, ID_USART0, GPIO('A', 10), GPIO('A', 11), GPIO('A', 17), 'A', 'B'}, - { USART1, ID_USART1, GPIO('A', 12), GPIO('A', 13), GPIO('A', 16), 'A', 'A'}, - { USART2, ID_USART2, GPIO('B', 21), GPIO('B', 20), GPIO('B', 24), 'A', 'A'}, -#elif CONFIG_MACH_SAM4S8C - { SPI, ID_SPI, GPIO('A', 12), GPIO('A', 13), GPIO('A', 14), 'A', 'A' }, - { USART0, ID_USART0, GPIO('A', 5), GPIO('A', 6), GPIO('A', 2), 'A', 'B' }, - { USART1, ID_USART1, GPIO('A', 21), GPIO('A', 22), GPIO('A', 23), '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'}, - { SPI, ID_SPI, GPIO('A', 12), GPIO('A', 13), GPIO('A', 14), 'A', 'A' }, -#endif -}; - -static int -is_spihw(void *dev) -{ -#if CONFIG_MACH_SAM3X8E - return dev == SPI0; -#else - return dev == SPI; -#endif -} - -static void -init_pins(uint32_t bus) -{ - 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); -} - - -/**************************************************************** - * 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 */ - pSpi->SPI_CR = SPI_CR_SPIDIS; - /* Execute a software reset of the SPI twice */ - pSpi->SPI_CR = SPI_CR_SWRST; - pSpi->SPI_CR = SPI_CR_SWRST; - - 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 */ - pSpi->SPI_CSR[0] = 0; - pSpi->SPI_CSR[1] = 0; - pSpi->SPI_CSR[2] = 0; - pSpi->SPI_CSR[3] = 0; - - /* Set basic channel config */ - pSpi->SPI_CSR[CHANNEL] = 0; - /* Enable SPI */ - pSpi->SPI_CR = SPI_CR_SPIEN; -} - -static struct spi_config -spihw_setup(uint32_t bus, uint8_t mode, uint32_t rate) -{ - // Make sure bus is enabled - spihw_init(bus); - - 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){ .spidev = spi_bus[bus].dev, .cfg = config }; -} - -static void -spihw_prepare(struct spi_config config) -{ - Spi *pSpi = config.spidev; - pSpi->SPI_CSR[CHANNEL] = config.cfg; -} - -static void -spihw_transfer(struct spi_config config, uint8_t receive_data - , uint8_t len, uint8_t *data) -{ - Spi *pSpi = config.spidev; - if (receive_data) { - while (len--) { - pSpi->SPI_TDR = *data; - // wait for receive register - while (!(pSpi->SPI_SR & SPI_SR_RDRF)) - ; - // get data - *data++ = pSpi->SPI_RDR; - } - } else { - while (len--) { - pSpi->SPI_TDR = *data++; - // wait for receive register - while (!(pSpi->SPI_SR & SPI_SR_RDRF)) - ; - // read data (to clear RDRF) - pSpi->SPI_RDR; - } - } -} - - -/**************************************************************** - * 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); -} diff --git a/src/sam3/timer.c b/src/sam3/timer.c deleted file mode 100644 index b2d600a5..00000000 --- a/src/sam3/timer.c +++ /dev/null @@ -1,65 +0,0 @@ -// SAM3/SAM4 timer interrupt scheduling -// -// Copyright (C) 2016-2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "board/irq.h" // irq_disable -#include "board/misc.h" // timer_read_time -#include "board/timer_irq.h" // timer_dispatch_many -#include "command.h" // DECL_SHUTDOWN -#include "internal.h" // TC0 -#include "sched.h" // DECL_INIT - -// Set the next irq time -static void -timer_set(uint32_t value) -{ - TC0->TC_CHANNEL[0].TC_RA = value; -} - -// Return the current time (in absolute clock ticks). -uint32_t -timer_read_time(void) -{ - return TC0->TC_CHANNEL[0].TC_CV; -} - -// Activate timer dispatch as soon as possible -void -timer_kick(void) -{ - timer_set(timer_read_time() + 50); - TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending -} - -void -timer_init(void) -{ - TcChannel *tc = &TC0->TC_CHANNEL[0]; - // Reset the timer - tc->TC_CCR = TC_CCR_CLKDIS; - tc->TC_IDR = 0xFFFFFFFF; - // Enable it - enable_pclock(ID_TC0); - tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1; - tc->TC_IER = TC_IER_CPAS; - NVIC_SetPriority(TC0_IRQn, 2); - NVIC_EnableIRQ(TC0_IRQn); - timer_kick(); - tc->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; -} -DECL_INIT(timer_init); - -// IRQ handler -void __visible __aligned(16) // aligning helps stabilize perf benchmarks -TC0_Handler(void) -{ - irq_disable(); - uint32_t status = TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending - if (likely(status & TC_SR_CPAS)) { - uint32_t next = timer_dispatch_many(); - timer_set(next); - } - irq_enable(); -} diff --git a/src/sam3/usb_cdc_ep.h b/src/sam3/usb_cdc_ep.h deleted file mode 100644 index bcf1d3e3..00000000 --- a/src/sam3/usb_cdc_ep.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __SAM3_USB_CDC_EP_H -#define __SAM3_USB_CDC_EP_H - -enum { - USB_CDC_EP_ACM = 3, - USB_CDC_EP_BULK_OUT = 1, - USB_CDC_EP_BULK_IN = 2, -}; - -#endif // usb_cdc_ep.h diff --git a/src/samd21/Kconfig b/src/samd21/Kconfig deleted file mode 100644 index 18bd7d81..00000000 --- a/src/samd21/Kconfig +++ /dev/null @@ -1,51 +0,0 @@ -# Kconfig settings for SAMD21 processors - -if MACH_SAMD21 - -config SAMD_SELECT - bool - default y - select HAVE_GPIO - select HAVE_GPIO_ADC - select HAVE_GPIO_I2C - select HAVE_GPIO_SPI - select HAVE_GPIO_HARD_PWM - select HAVE_GPIO_BITBANGING - -config BOARD_DIRECTORY - string - default "samd21" - -config CLOCK_FREQ - int - default 48000000 - -choice - prompt "Bootloader offset" - config FLASH_START_0000 - bool "No bootloader" - config FLASH_START_2000 - bool "8KiB bootloader (Arduino Zero)" - config FLASH_START_4000 - bool "16KiB bootloader (Arduino M0)" -endchoice - -config FLASH_START - hex - default 0x4000 if FLASH_START_4000 - default 0x2000 if FLASH_START_2000 - default 0x0000 - -config USBSERIAL - bool "Use USB for communication (instead of serial)" - default y -config SERIAL - depends on !USBSERIAL - bool - default y -config SERIAL_BAUD - depends on SERIAL - int "Baud rate for serial port" - default 250000 - -endif diff --git a/src/samd21/Makefile b/src/samd21/Makefile deleted file mode 100644 index 24ef1e49..00000000 --- a/src/samd21/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# Additional samd21 build rules - -# Setup the toolchain -CROSS_PREFIX=arm-none-eabi- - -dirs-y += src/samd21 src/generic -dirs-y += lib/samd21/samd21a/gcc/gcc/ - -CFLAGS += -mthumb -mcpu=cortex-m0plus -CFLAGS += -Ilib/cmsis-core -Ilib/samd21/samd21a/include -CFLAGS += -D__SAMD21G18A__ - -CFLAGS_klipper.elf += -T $(OUT)samd21.ld -CFLAGS_klipper.elf += --specs=nano.specs --specs=nosys.specs - -# Add source files -src-y += samd21/main.c samd21/timer.c samd21/clock.c samd21/gpio.c -src-y += generic/crc16_ccitt.c generic/alloc.c -src-y += generic/armcm_irq.c generic/timer_irq.c -src-y += ../lib/samd21/samd21a/gcc/gcc/startup_samd21.c -src-$(CONFIG_USBSERIAL) += samd21/usbserial.c generic/usb_cdc.c -src-$(CONFIG_SERIAL) += samd21/serial.c generic/serial_irq.c -src-$(CONFIG_HAVE_GPIO_ADC) += samd21/adc.c -src-$(CONFIG_HAVE_GPIO_I2C) += samd21/i2c.c -src-$(CONFIG_HAVE_GPIO_SPI) += samd21/spi.c -src-$(CONFIG_HAVE_GPIO_HARD_PWM) += samd21/hard_pwm.c - -# Support bootloader offset address -target-y := $(OUT)samd21.ld $(target-y) - -$(OUT)samd21.ld: lib/samd21/samd21a/gcc/gcc/samd21g18a_flash.ld $(OUT)board-link - @echo " Preprocessing $@" - $(Q)$(CPP) -P -MD -MT $@ -DFLASH_START=$(CONFIG_FLASH_START) $< -o $@ - -# Build the additional hex and bin output files -target-y += $(OUT)klipper.bin $(OUT)klipper.elf.hex - -$(OUT)klipper.bin: $(OUT)klipper.elf - @echo " Creating hex file $@" - $(Q)$(OBJCOPY) -O binary $< $@ - -$(OUT)klipper.elf.hex: $(OUT)klipper.elf - @echo " Creating hex file $@" - $(Q)$(OBJCOPY) -j .text -j .relocate -O ihex $< $@ - -# Flash rules -lib/bossac/bin/bossac: - @echo " Building bossac" - $(Q)make -C lib/bossac bin/bossac - -flash: $(OUT)klipper.bin lib/bossac/bin/bossac - @echo " Flashing $^ to $(FLASH_DEVICE) via bossac" - $(Q)if [ -z $(FLASH_DEVICE) ]; then echo "Please specify FLASH_DEVICE"; exit 1; fi - $(Q)lib/bossac/bin/bossac -U -p "$(FLASH_DEVICE)" -a --offset=0x2000 -w $(OUT)klipper.bin -v -b -R diff --git a/src/samd21/adc.c b/src/samd21/adc.c deleted file mode 100644 index 38836592..00000000 --- a/src/samd21/adc.c +++ /dev/null @@ -1,121 +0,0 @@ -// Analog to Digital Converter support -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "command.h" // shutdown -#include "gpio.h" // gpio_adc_read -#include "internal.h" // GPIO -#include "samd21.h" // ADC -#include "sched.h" // sched_shutdown - -static const uint8_t adc_pins[] = { - GPIO('A', 2), GPIO('A', 3), GPIO('B', 8), GPIO('B', 9), GPIO('A', 4), - GPIO('A', 5), GPIO('A', 6), GPIO('A', 7), GPIO('B', 0), GPIO('B', 1), - GPIO('B', 2), GPIO('B', 3), GPIO('B', 4), GPIO('B', 5), GPIO('B', 6), - GPIO('B', 7), GPIO('A', 8), GPIO('A', 9), GPIO('A', 10), GPIO('A', 11) -}; - -DECL_CONSTANT(ADC_MAX, 4095); - -static void -adc_init(void) -{ - static uint8_t have_run_init; - if (have_run_init) - return; - have_run_init = 1; - - // Enable adc clock - enable_pclock(ADC_GCLK_ID, PM_APBCMASK_ADC); - - // Load calibraiton info - uint32_t v = *((uint32_t*)ADC_FUSES_BIASCAL_ADDR); - uint32_t bias = (v & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos; - v = *((uint32_t*)ADC_FUSES_LINEARITY_0_ADDR); - uint32_t li0 = (v & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos; - v = *((uint32_t*)ADC_FUSES_LINEARITY_1_ADDR); - uint32_t li5 = (v & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos; - uint32_t lin = li0 | (li5 << 5); - ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(lin); - - // Setup and enable adc - ADC->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1; - ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV128; - ADC->SAMPCTRL.reg = 63; - ADC->CTRLA.reg = ADC_CTRLA_ENABLE; -} - -struct gpio_adc -gpio_adc_setup(uint8_t pin) -{ - // Find pin in adc_pins table - uint8_t chan; - for (chan=0; ; chan++) { - if (chan >= ARRAY_SIZE(adc_pins)) - shutdown("Not a valid ADC pin"); - if (adc_pins[chan] == pin) - break; - } - - // Enable ADC - adc_init(); - - // Set pin in ADC mode - gpio_peripheral(pin, 'B', 0); - - return (struct gpio_adc){ chan }; -} - -enum { ADC_DUMMY=0xff }; -static uint8_t last_analog_read = ADC_DUMMY; - -// Try to sample a value. Returns zero if sample ready, otherwise -// returns the number of clock ticks the caller should wait before -// retrying this function. -uint32_t -gpio_adc_sample(struct gpio_adc g) -{ - if (last_analog_read == g.chan) { - if (ADC->INTFLAG.reg & ADC_INTFLAG_RESRDY) - // Sample now ready - return 0; - // ADC is still busy - goto need_delay; - } - if (last_analog_read != ADC_DUMMY) - // Sample on another channel in progress - goto need_delay; - last_analog_read = g.chan; - - // Set the channel to sample - ADC->INPUTCTRL.reg = (ADC_INPUTCTRL_MUXPOS(g.chan) - | ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_GAIN_DIV2); - - // Start the sample - ADC->SWTRIG.reg = ADC_SWTRIG_START; - - // Schedule next attempt after sample is likely to be complete -need_delay: - return 42 * 128 + 200; // 42 == 1 + (63+1)/2 + 1 + 12/2 + 1.5 -} - -// Read a value; use only after gpio_adc_sample() returns zero -uint16_t -gpio_adc_read(struct gpio_adc g) -{ - last_analog_read = ADC_DUMMY; - return ADC->RESULT.reg; -} - -// Cancel a sample that may have been started with gpio_adc_sample() -void -gpio_adc_cancel_sample(struct gpio_adc g) -{ - if (last_analog_read == g.chan) { - ADC->SWTRIG.reg = ADC_SWTRIG_FLUSH; - ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; - last_analog_read = ADC_DUMMY; - } -} diff --git a/src/samd21/clock.c b/src/samd21/clock.c deleted file mode 100644 index ef0cd9bc..00000000 --- a/src/samd21/clock.c +++ /dev/null @@ -1,77 +0,0 @@ -// Code to setup peripheral clocks on the SAMD21 -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "autoconf.h" // CONFIG_CLOCK_FREQ -#include "compiler.h" // DIV_ROUND_CLOSEST -#include "internal.h" // enable_pclock -#include "samd21.h" // GCLK - -// The "generic clock generators" that are configured -#define CLKGEN_MAIN 0 -#define CLKGEN_32K 1 -#define CLKGEN_ULP32K 2 - -// Enable a peripheral clock and power to that peripheral -void -enable_pclock(uint32_t clock_id, uint32_t pmask) -{ - GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_ID(clock_id) - | GCLK_CLKCTRL_GEN(CLKGEN_MAIN) | GCLK_CLKCTRL_CLKEN); - while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) - ; - PM->APBCMASK.reg |= pmask; -} - -#define FREQ_XOSC32K 32768 - -void -SystemInit(void) -{ - // Setup flash to work with 48Mhz clock - NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_RWS_HALF; - - // Enable external 32Khz crystal - uint32_t val = (SYSCTRL_XOSC32K_STARTUP(6) - | SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K); - SYSCTRL->XOSC32K.reg = val; - SYSCTRL->XOSC32K.reg = val | SYSCTRL_XOSC32K_ENABLE; - while (!(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_XOSC32KRDY)) - ; - - // Reset GCLK - GCLK->CTRL.reg = GCLK_CTRL_SWRST; - while (GCLK->CTRL.reg & GCLK_CTRL_SWRST) - ; - - // Route external 32Khz clock to DFLL48M (via CLKGEN_32K) - GCLK->GENDIV.reg = GCLK_GENDIV_ID(CLKGEN_32K); - GCLK->GENCTRL.reg = (GCLK_GENCTRL_ID(CLKGEN_32K) - | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_GENEN); - GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_ID(SYSCTRL_GCLK_ID_DFLL48) - | GCLK_CLKCTRL_GEN(CLKGEN_32K) | GCLK_CLKCTRL_CLKEN); - while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) - ; - - // Configure DFLL48M clock - SYSCTRL->DFLLCTRL.reg = 0; - uint32_t mul = DIV_ROUND_CLOSEST(CONFIG_CLOCK_FREQ, FREQ_XOSC32K); - SYSCTRL->DFLLMUL.reg = (SYSCTRL_DFLLMUL_CSTEP(31) - | SYSCTRL_DFLLMUL_FSTEP(511) - | SYSCTRL_DFLLMUL_MUL(mul)); - SYSCTRL->DFLLCTRL.reg = (SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_WAITLOCK - | SYSCTRL_DFLLCTRL_QLDIS - | SYSCTRL_DFLLCTRL_ENABLE); - uint32_t ready = (SYSCTRL_PCLKSR_DFLLRDY | SYSCTRL_PCLKSR_DFLLLCKC - | SYSCTRL_PCLKSR_DFLLLCKF); - while ((SYSCTRL->PCLKSR.reg & ready) != ready) - ; - - // Switch main clock to DFLL48M clock - GCLK->GENDIV.reg = GCLK_GENDIV_ID(CLKGEN_MAIN); - GCLK->GENCTRL.reg = (GCLK_GENCTRL_ID(CLKGEN_MAIN) - | GCLK_GENCTRL_SRC_DFLL48M - | GCLK_GENCTRL_IDC | GCLK_GENCTRL_GENEN); -} diff --git a/src/samd21/gpio.c b/src/samd21/gpio.c deleted file mode 100644 index 045dc003..00000000 --- a/src/samd21/gpio.c +++ /dev/null @@ -1,141 +0,0 @@ -// samd21 gpio functions -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include // ffs -#include "board/irq.h" // irq_save -#include "command.h" // shutdown -#include "gpio.h" // gpio_out_setup -#include "internal.h" // gpio_peripheral -#include "samd21.h" // PORT -#include "sched.h" // sched_shutdown - - -/**************************************************************** - * Pin multiplexing - ****************************************************************/ - -void -gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up) -{ - uint32_t bank = GPIO2PORT(gpio), bit = gpio % 32; - PortGroup *pg = &PORT->Group[bank]; - if (ptype) { - volatile uint8_t *pmux = &pg->PMUX[bit/2].reg; - uint8_t shift = (bit & 1) ? 4 : 0, mask = ~(0xf << shift); - *pmux = (*pmux & mask) | ((ptype - 'A') << shift); - } - if (pull_up) { - if (pull_up > 0) - pg->OUTSET.reg = (1<OUTCLR.reg = (1<PINCFG[bit].reg = ((ptype ? PORT_PINCFG_PMUXEN : 0) - | (pull_up ? PORT_PINCFG_PULLEN : 0)); -} - - -/**************************************************************** - * General Purpose Input Output (GPIO) pins - ****************************************************************/ - -#define NUM_PORT 2 - -struct gpio_out -gpio_out_setup(uint8_t pin, uint8_t val) -{ - if (GPIO2PORT(pin) >= NUM_PORT) - goto fail; - PortGroup *pg = &PORT->Group[GPIO2PORT(pin)]; - struct gpio_out g = { .regs=pg, .bit=GPIO2BIT(pin) }; - gpio_out_reset(g, val); - return g; -fail: - shutdown("Not an output pin"); -} - -static void -set_pincfg(PortGroup *pg, uint32_t bit, uint8_t cfg) -{ - pg->PINCFG[ffs(bit)-1].reg = cfg; -} - -void -gpio_out_reset(struct gpio_out g, uint8_t val) -{ - PortGroup *pg = g.regs; - irqstatus_t flag = irq_save(); - if (val) - pg->OUTSET.reg = g.bit; - else - pg->OUTCLR.reg = g.bit; - pg->DIRSET.reg = g.bit; - set_pincfg(pg, g.bit, 0); - irq_restore(flag); -} - -void -gpio_out_toggle_noirq(struct gpio_out g) -{ - PortGroup *pg = g.regs; - pg->OUTTGL.reg = g.bit; -} - -void -gpio_out_toggle(struct gpio_out g) -{ - gpio_out_toggle_noirq(g); -} - -void -gpio_out_write(struct gpio_out g, uint8_t val) -{ - PortGroup *pg = g.regs; - if (val) - pg->OUTSET.reg = g.bit; - else - pg->OUTCLR.reg = g.bit; -} - - -struct gpio_in -gpio_in_setup(uint8_t pin, int8_t pull_up) -{ - if (GPIO2PORT(pin) >= NUM_PORT) - goto fail; - PortGroup *pg = &PORT->Group[GPIO2PORT(pin)]; - struct gpio_in g = { .regs=pg, .bit=GPIO2BIT(pin) }; - gpio_in_reset(g, pull_up); - return g; -fail: - shutdown("Not an input pin"); -} - -void -gpio_in_reset(struct gpio_in g, int8_t pull_up) -{ - PortGroup *pg = g.regs; - irqstatus_t flag = irq_save(); - uint32_t cfg = PORT_PINCFG_INEN; - if (pull_up) { - cfg |= PORT_PINCFG_PULLEN; - if (pull_up > 0) - pg->OUTSET.reg = g.bit; - else - pg->OUTCLR.reg = g.bit; - } - set_pincfg(pg, g.bit, cfg); - pg->DIRCLR.reg = g.bit; - irq_restore(flag); -} - -uint8_t -gpio_in_read(struct gpio_in g) -{ - PortGroup *pg = g.regs; - return !!(pg->IN.reg & g.bit); -} diff --git a/src/samd21/gpio.h b/src/samd21/gpio.h deleted file mode 100644 index 5bf44454..00000000 --- a/src/samd21/gpio.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef __SAM3X8E_GPIO_H -#define __SAM3X8E_GPIO_H - -#include - -struct gpio_out { - void *regs; - uint32_t bit; -}; -struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val); -void gpio_out_reset(struct gpio_out g, uint8_t val); -void gpio_out_toggle_noirq(struct gpio_out g); -void gpio_out_toggle(struct gpio_out g); -void gpio_out_write(struct gpio_out g, uint8_t val); - -struct gpio_in { - void *regs; - uint32_t bit; -}; -struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up); -void gpio_in_reset(struct gpio_in g, int8_t pull_up); -uint8_t gpio_in_read(struct gpio_in g); - -struct gpio_pwm { - void *reg; -}; -struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val); -void gpio_pwm_write(struct gpio_pwm g, uint8_t val); - -struct gpio_adc { - uint32_t chan; -}; -struct gpio_adc gpio_adc_setup(uint8_t pin); -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 ctrla, baud; -}; -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); - -struct i2c_config { - uint8_t addr; -}; - -struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); -void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); -void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg - , uint8_t read_len, uint8_t *read); - -#endif // gpio.h diff --git a/src/samd21/hard_pwm.c b/src/samd21/hard_pwm.c deleted file mode 100644 index 7deecfb9..00000000 --- a/src/samd21/hard_pwm.c +++ /dev/null @@ -1,102 +0,0 @@ -// Hardware PWM support on samd21 -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "command.h" // shutdown -#include "gpio.h" // gpio_pwm_write -#include "internal.h" // GPIO -#include "samd21.h" // TCC0 -#include "sched.h" // sched_shutdown - -struct gpio_pwm_info { - uint32_t gpio; - Tcc *tcc; - uint32_t clock_id, power_id, channel; - char ptype; -}; - -static const struct gpio_pwm_info pwm_regs[] = { - { GPIO('A', 4), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 0, 'E' }, - { GPIO('A', 5), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 1, 'E' }, - { GPIO('A', 6), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 0, 'E' }, - { GPIO('A', 7), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 1, 'E' }, - { GPIO('A', 8), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 0, 'E' }, - { GPIO('A', 9), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 1, 'E' }, - { GPIO('A', 10), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 0, 'E' }, - { GPIO('A', 11), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 1, 'E' }, - { GPIO('A', 12), TCC2, TCC2_GCLK_ID, PM_APBCMASK_TCC2, 0, 'E' }, - { GPIO('A', 13), TCC2, TCC2_GCLK_ID, PM_APBCMASK_TCC2, 1, 'E' }, - { GPIO('A', 16), TCC2, TCC2_GCLK_ID, PM_APBCMASK_TCC2, 0, 'E' }, - { GPIO('A', 17), TCC2, TCC2_GCLK_ID, PM_APBCMASK_TCC2, 1, 'E' }, - { GPIO('A', 18), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 2, 'F' }, - { GPIO('A', 19), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 3, 'F' }, - { GPIO('A', 24), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 2, 'F' }, - { GPIO('A', 25), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 3, 'F' }, - { GPIO('A', 30), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 0, 'E' }, - { GPIO('A', 31), TCC1, TCC1_GCLK_ID, PM_APBCMASK_TCC1, 1, 'E' }, - { GPIO('B', 30), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 0, 'E' }, - { GPIO('B', 31), TCC0, TCC0_GCLK_ID, PM_APBCMASK_TCC0, 1, 'E' }, -}; - -#define MAX_PWM 255 - -DECL_CONSTANT(PWM_MAX, MAX_PWM); - -struct gpio_pwm -gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) -{ - // Find pin in pwm_regs table - const struct gpio_pwm_info *p = pwm_regs; - for (; ; p++) { - if (p >= &pwm_regs[ARRAY_SIZE(pwm_regs)]) - shutdown("Not a valid PWM pin"); - if (p->gpio == pin) - break; - } - - // Enable timer clock - enable_pclock(p->clock_id, p->power_id); - - // Map cycle_time to pwm clock divisor - uint32_t cs; - switch (cycle_time) { - case 0 ... (1+2) * MAX_PWM / 2 - 1: cs = 0; break; - case (1+2) * MAX_PWM / 2 ... (2+4) * MAX_PWM / 2 - 1: cs = 1; break; - case (2+4) * MAX_PWM / 2 ... (4+8) * MAX_PWM / 2 - 1: cs = 2; break; - case (4+8) * MAX_PWM / 2 ... (8+16) * MAX_PWM / 2 - 1: cs = 3; break; - case (8+16) * MAX_PWM / 2 ... (16+64) * MAX_PWM / 2 - 1: cs = 4; break; - case (16+64) * MAX_PWM / 2 ... (64+256) * MAX_PWM / 2 - 1: cs = 5; break; - case (64+256) * MAX_PWM / 2 ... (256+1024) * MAX_PWM / 2 - 1: cs = 6; break; - default: cs = 7; break; - } - uint32_t ctrla = TCC_CTRLA_ENABLE | TCC_CTRLA_PRESCALER(cs); - - // Enable timer - Tcc *tcc = p->tcc; - uint32_t old_ctrla = tcc->CTRLA.reg; - if (old_ctrla != ctrla) { - if (old_ctrla & TCC_CTRLA_ENABLE) - shutdown("PWM already programmed at different speed"); - tcc->CTRLA.reg = ctrla & ~TCC_CTRLA_ENABLE; - tcc->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; - tcc->PER.reg = MAX_PWM; - tcc->CTRLA.reg = ctrla; - } - - // Set initial value - struct gpio_pwm g = (struct gpio_pwm) { (void*)&tcc->CCB[p->channel].reg }; - gpio_pwm_write(g, val); - - // Route output to pin - gpio_peripheral(pin, p->ptype, 0); - - return g; -} - -void -gpio_pwm_write(struct gpio_pwm g, uint8_t val) -{ - *(volatile uint32_t*)g.reg = val; -} diff --git a/src/samd21/i2c.c b/src/samd21/i2c.c deleted file mode 100644 index 801c0e32..00000000 --- a/src/samd21/i2c.c +++ /dev/null @@ -1,114 +0,0 @@ -// i2c support on samd21 -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "autoconf.h" // CONFIG_CLOCK_FREQ -#include "internal.h" // enable_pclock -#include "command.h" // shutdown -#include "gpio.h" // i2c_setup -#include "samd21.h" // SERCOM3 -#include "sched.h" // sched_shutdown - -#define TIME_RISE 125ULL // 125 nanoseconds -#define I2C_FREQ 100000 - -static void -i2c_init(void) -{ - static int have_run_init; - if (have_run_init) - return; - have_run_init = 1; - - // Setup clock - enable_pclock(SERCOM3_GCLK_ID_CORE, PM_APBCMASK_SERCOM3); - - // Configure SDA, SCL pins - gpio_peripheral(GPIO('A', 22), 'C', 0); - gpio_peripheral(GPIO('A', 23), 'C', 0); - - // Configure i2c - SercomI2cm *si = &SERCOM3->I2CM; - si->CTRLA.reg = 0; - uint32_t areg = (SERCOM_I2CM_CTRLA_LOWTOUTEN - | SERCOM_I2CM_CTRLA_INACTOUT(3) - | SERCOM_I2CM_STATUS_SEXTTOUT - | SERCOM_I2CM_STATUS_MEXTTOUT - | SERCOM_I2CM_CTRLA_MODE_I2C_MASTER); - si->CTRLA.reg = areg; - uint32_t baud = (CONFIG_CLOCK_FREQ / I2C_FREQ - - 10 - CONFIG_CLOCK_FREQ*TIME_RISE/1000000000) / 2; - si->BAUD.reg = baud; - si->CTRLA.reg = areg | SERCOM_I2CM_CTRLA_ENABLE; - while (si->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_ENABLE) - ; - - // Go into idle mode - si->STATUS.reg = SERCOM_I2CM_STATUS_BUSSTATE(1); - while (si->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_SYSOP) - ; -} - -struct i2c_config -i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) -{ - if (bus) - shutdown("Unsupported i2c bus"); - i2c_init(); - return (struct i2c_config){ .addr=addr<<1 }; -} - -static void -i2c_wait(SercomI2cm *si) -{ - for (;;) { - uint32_t intflag = si->INTFLAG.reg; - if (!(intflag & SERCOM_I2CM_INTFLAG_MB)) { - if (si->STATUS.reg & SERCOM_I2CM_STATUS_BUSERR) - shutdown("i2c buserror"); - continue; - } - if (intflag & SERCOM_I2CM_INTFLAG_ERROR) - shutdown("i2c error"); - break; - } -} - -static void -i2c_start(SercomI2cm *si, uint8_t addr) -{ - si->ADDR.reg = addr; - i2c_wait(si); -} - -static void -i2c_send_byte(SercomI2cm *si, uint8_t b) -{ - si->DATA.reg = b; - i2c_wait(si); -} - -static void -i2c_stop(SercomI2cm *si) -{ - si->CTRLB.reg = SERCOM_I2CM_CTRLB_CMD(3); -} - -void -i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) -{ - SercomI2cm *si = &SERCOM3->I2CM; - i2c_start(si, config.addr); - while (write_len--) - i2c_send_byte(si, *write++); - i2c_stop(si); -} - -void -i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg - , uint8_t read_len, uint8_t *read) -{ - shutdown("i2c_read not supported on samd21"); -} diff --git a/src/samd21/internal.h b/src/samd21/internal.h deleted file mode 100644 index 7043007f..00000000 --- a/src/samd21/internal.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __SAMD21_INTERNAL_H -#define __SAMD21_INTERNAL_H -// Local definitions for samd21 code - -#include // uint32_t - -#define GPIO(PORT, NUM) (((PORT)-'A') * 32 + (NUM)) -#define GPIO2PORT(PIN) ((PIN) / 32) -#define GPIO2BIT(PIN) (1<<((PIN) % 32)) - -void enable_pclock(uint32_t clock_id, uint32_t pmask); -void gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up); - -#endif // internal.h diff --git a/src/samd21/main.c b/src/samd21/main.c deleted file mode 100644 index 4687fc34..00000000 --- a/src/samd21/main.c +++ /dev/null @@ -1,52 +0,0 @@ -// Main starting point for SAMD21 boards -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "command.h" // DECL_CONSTANT -#include "samd21.h" // SystemInit -#include "sched.h" // sched_main - -DECL_CONSTANT(MCU, "samd21g"); - - -/**************************************************************** - * watchdog handler - ****************************************************************/ - -void -watchdog_reset(void) -{ - WDT->CLEAR.reg = 0xa5; -} -DECL_TASK(watchdog_reset); - -void -watchdog_init(void) -{ - WDT->CONFIG.reg = WDT_CONFIG_PER_16K; // 500ms timeout - WDT->CTRL.reg = WDT_CTRL_ENABLE; -} -DECL_INIT(watchdog_init); - - -/**************************************************************** - * misc functions - ****************************************************************/ - -void -command_reset(uint32_t *args) -{ - NVIC_SystemReset(); -} -DECL_COMMAND_FLAGS(command_reset, HF_IN_SHUTDOWN, "reset"); - -// Main entry point -int -main(void) -{ - SystemInit(); - sched_main(); - return 0; -} diff --git a/src/samd21/serial.c b/src/samd21/serial.c deleted file mode 100644 index 895ffb27..00000000 --- a/src/samd21/serial.c +++ /dev/null @@ -1,61 +0,0 @@ -// samd21 serial port -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "autoconf.h" // CONFIG_SERIAL_BAUD -#include "board/serial_irq.h" // serial_rx_data -#include "internal.h" // enable_pclock -#include "samd21.h" // SERCOM0 -#include "sched.h" // DECL_INIT - -void -serial_init(void) -{ - // Enable serial clock - enable_pclock(SERCOM0_GCLK_ID_CORE, PM_APBCMASK_SERCOM0); - // Enable pins - gpio_peripheral(GPIO('A', 10), 'C', 0); - gpio_peripheral(GPIO('A', 11), 'C', 0); - // Configure serial - SercomUsart *su = &SERCOM0->USART; - su->CTRLA.reg = 0; - uint32_t areg = (SERCOM_USART_CTRLA_MODE_USART_INT_CLK - | SERCOM_USART_CTRLA_DORD - | SERCOM_USART_CTRLA_SAMPR(1) - | SERCOM_USART_CTRLA_TXPO(1) - | SERCOM_USART_CTRLA_RXPO(3)); - su->CTRLA.reg = areg; - su->CTRLB.reg = SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN; - uint32_t baud8 = CONFIG_CLOCK_FREQ / (2 * CONFIG_SERIAL_BAUD); - su->BAUD.reg = (SERCOM_USART_BAUD_FRAC_BAUD(baud8 / 8) - | SERCOM_USART_BAUD_FRAC_FP(baud8 % 8)); - NVIC_SetPriority(SERCOM0_IRQn, 0); - NVIC_EnableIRQ(SERCOM0_IRQn); - su->INTENSET.reg = SERCOM_USART_INTENSET_RXC; - su->CTRLA.reg = areg | SERCOM_USART_CTRLA_ENABLE; -} -DECL_INIT(serial_init); - -void __visible -SERCOM0_Handler(void) -{ - uint32_t status = SERCOM0->USART.INTFLAG.reg; - if (status & SERCOM_USART_INTFLAG_RXC) - serial_rx_byte(SERCOM0->USART.DATA.reg); - if (status & SERCOM_USART_INTFLAG_DRE) { - uint8_t data; - int ret = serial_get_tx_byte(&data); - if (ret) - SERCOM0->USART.INTENCLR.reg = SERCOM_USART_INTENSET_DRE; - else - SERCOM0->USART.DATA.reg = data; - } -} - -void -serial_enable_tx_irq(void) -{ - SERCOM0->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; -} diff --git a/src/samd21/spi.c b/src/samd21/spi.c deleted file mode 100644 index 12c7d680..00000000 --- a/src/samd21/spi.c +++ /dev/null @@ -1,92 +0,0 @@ -// spi support on samd21 -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "autoconf.h" // CONFIG_CLOCK_FREQ -#include "internal.h" // enable_pclock -#include "command.h" // shutdown -#include "gpio.h" // spi_setup -#include "samd21.h" // SERCOM4 -#include "sched.h" // sched_shutdown - -static void -spi_init(uint32_t ctrla, uint32_t baud) -{ - static int have_run_init; - if (have_run_init) - return; - have_run_init = 1; - - // Setup clock - enable_pclock(SERCOM4_GCLK_ID_CORE, PM_APBCMASK_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; - ss->BAUD.reg = baud; - ss->CTRLA.reg = ctrla; -} - -struct spi_config -spi_setup(uint32_t bus, uint8_t mode, uint32_t rate) -{ - if (bus) - shutdown("Invalid spi bus"); - - uint32_t ctrla = (SERCOM_SPI_CTRLA_MODE_SPI_MASTER - | (mode << SERCOM_SPI_CTRLA_CPHA_Pos) - | SERCOM_SPI_CTRLA_DIPO(0) - | SERCOM_SPI_CTRLA_DOPO(1) - | SERCOM_SPI_CTRLA_ENABLE); - uint32_t baud = CONFIG_CLOCK_FREQ / (2 * rate) - 1; - spi_init(ctrla, baud); - return (struct spi_config){ .ctrla = ctrla, .baud = baud }; -} - -void -spi_prepare(struct spi_config config) -{ - uint32_t ctrla = config.ctrla, baud = config.baud; - SercomSpi *ss = &SERCOM4->SPI; - if (ctrla == ss->CTRLA.reg && baud == ss->BAUD.reg) - return; - ss->CTRLA.reg = ctrla & ~SERCOM_SPI_CTRLA_ENABLE; - ss->CTRLA.reg = ctrla & ~SERCOM_SPI_CTRLA_ENABLE; - ss->BAUD.reg = baud; - ss->CTRLA.reg = ctrla; -} - -void -spi_transfer(struct spi_config config, uint8_t receive_data - , uint8_t len, uint8_t *data) -{ - SercomSpi *ss = &SERCOM4->SPI; - if (receive_data) { - while (len--) { - ss->DATA.reg = *data; - // wait for receive register - while (!(ss->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC)) - ; - // get data - *data++ = ss->DATA.reg; - } - } else { - while (len--) { - ss->DATA.reg = *data++; - // wait for receive register - while (!(ss->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC)) - ; - // read data (to clear RXC) - ss->DATA.reg; - } - } -} diff --git a/src/samd21/timer.c b/src/samd21/timer.c deleted file mode 100644 index 882b4c10..00000000 --- a/src/samd21/timer.c +++ /dev/null @@ -1,66 +0,0 @@ -// SAMD21 timer interrupt scheduling -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include "board/irq.h" // irq_disable -#include "board/misc.h" // timer_read_time -#include "board/timer_irq.h" // timer_dispatch_many -#include "internal.h" // enable_pclock -#include "samd21.h" // TC4 -#include "sched.h" // DECL_INIT - -// Set the next irq time -static void -timer_set(uint32_t value) -{ - TC4->COUNT32.CC[0].reg = value; - TC4->COUNT32.INTFLAG.reg = TC_INTFLAG_MC0; -} - -// Return the current time (in absolute clock ticks). -uint32_t -timer_read_time(void) -{ - return TC4->COUNT32.COUNT.reg; -} - -// Activate timer dispatch as soon as possible -void -timer_kick(void) -{ - timer_set(timer_read_time() + 50); -} - -void -timer_init(void) -{ - // Supply power and clock to the timer - enable_pclock(TC3_GCLK_ID, PM_APBCMASK_TC3); - enable_pclock(TC4_GCLK_ID, PM_APBCMASK_TC4); - - // Configure the timer - TcCount32 *tc = &TC4->COUNT32; - irqstatus_t flag = irq_save(); - tc->CTRLA.reg = 0; - tc->CTRLA.reg = TC_CTRLA_MODE_COUNT32; - NVIC_SetPriority(TC4_IRQn, 2); - NVIC_EnableIRQ(TC4_IRQn); - tc->INTENSET.reg = TC_INTENSET_MC0; - tc->COUNT.reg = 0; - timer_kick(); - tc->CTRLA.reg = TC_CTRLA_MODE_COUNT32 | TC_CTRLA_ENABLE; - irq_restore(flag); -} -DECL_INIT(timer_init); - -// IRQ handler -void __visible __aligned(16) // aligning helps stabilize perf benchmarks -TC4_Handler(void) -{ - irq_disable(); - uint32_t next = timer_dispatch_many(); - timer_set(next); - irq_enable(); -} diff --git a/src/samd21/usbserial.c b/src/samd21/usbserial.c deleted file mode 100644 index 0864a4c5..00000000 --- a/src/samd21/usbserial.c +++ /dev/null @@ -1,246 +0,0 @@ -// Hardware interface to USB on samd21 -// -// Copyright (C) 2018 Kevin O'Connor -// -// This file may be distributed under the terms of the GNU GPLv3 license. - -#include // memcpy -#include "autoconf.h" // CONFIG_FLASH_START -#include "board/io.h" // readl -#include "board/irq.h" // irq_disable -#include "board/usb_cdc.h" // usb_notify_ep0 -#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN -#include "internal.h" // enable_pclock -#include "samd21.h" // USB -#include "sched.h" // DECL_INIT - - -/**************************************************************** - * USB transfer memory - ****************************************************************/ - -static uint8_t __aligned(4) ep0out[USB_CDC_EP0_SIZE]; -static uint8_t __aligned(4) ep0in[USB_CDC_EP0_SIZE]; -static uint8_t __aligned(4) acmin[USB_CDC_EP_ACM_SIZE]; -static uint8_t __aligned(4) bulkout[USB_CDC_EP_BULK_OUT_SIZE]; -static uint8_t __aligned(4) bulkin[USB_CDC_EP_BULK_IN_SIZE]; - -static UsbDeviceDescriptor usb_desc[USB_CDC_EP_BULK_IN + 1] = { - [0] = { { - { - .ADDR.reg = (uint32_t)ep0out, - .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(ep0out) >> 4), - }, { - .ADDR.reg = (uint32_t)ep0in, - .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(ep0in) >> 4), - }, - } }, - [USB_CDC_EP_ACM] = { { - { - }, { - .ADDR.reg = (uint32_t)acmin, - .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(acmin) >> 4), - }, - } }, - [USB_CDC_EP_BULK_OUT] = { { - { - .ADDR.reg = (uint32_t)bulkout, - .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(bulkout) >> 4), - }, { - }, - } }, - [USB_CDC_EP_BULK_IN] = { { - { - }, { - .ADDR.reg = (uint32_t)bulkin, - .PCKSIZE.reg = USB_DEVICE_PCKSIZE_SIZE(sizeof(bulkin) >> 4), - }, - } }, -}; - - -/**************************************************************** - * Interface - ****************************************************************/ - -#define EP0 USB->DEVICE.DeviceEndpoint[0] -#define EP_ACM USB->DEVICE.DeviceEndpoint[USB_CDC_EP_ACM] -#define EP_BULKOUT USB->DEVICE.DeviceEndpoint[USB_CDC_EP_BULK_OUT] -#define EP_BULKIN USB->DEVICE.DeviceEndpoint[USB_CDC_EP_BULK_IN] - -static int_fast8_t -usb_write_packet(uint32_t ep, uint32_t bank, const void *data, uint_fast8_t len) -{ - // Check if there is room for this packet - UsbDeviceEndpoint *ude = &USB->DEVICE.DeviceEndpoint[ep]; - uint8_t sts = ude->EPSTATUS.reg; - uint8_t bkrdy = (bank ? USB_DEVICE_EPSTATUS_BK1RDY - : USB_DEVICE_EPSTATUS_BK0RDY); - if (sts & bkrdy) - return -1; - // Copy the packet to the given buffer - UsbDeviceDescBank *uddb = &usb_desc[ep].DeviceDescBank[bank]; - memcpy((void*)uddb->ADDR.reg, data, len); - // Inform the USB hardware of the available packet - uint32_t pcksize = uddb->PCKSIZE.reg; - uint32_t c = pcksize & ~USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk; - uddb->PCKSIZE.reg = c | USB_DEVICE_PCKSIZE_BYTE_COUNT(len); - ude->EPSTATUSSET.reg = bkrdy; - return len; -} - -static int_fast8_t -usb_read_packet(uint32_t ep, uint32_t bank, void *data, uint_fast8_t max_len) -{ - // Check if there is a packet ready - UsbDeviceEndpoint *ude = &USB->DEVICE.DeviceEndpoint[ep]; - uint8_t sts = ude->EPSTATUS.reg; - uint8_t bkrdy = (bank ? USB_DEVICE_EPSTATUS_BK1RDY - : USB_DEVICE_EPSTATUS_BK0RDY); - if (!(sts & bkrdy)) - return -1; - // Copy the packet to the given buffer - UsbDeviceDescBank *uddb = &usb_desc[ep].DeviceDescBank[bank]; - uint32_t pcksize = uddb->PCKSIZE.reg; - uint32_t c = pcksize & USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk; - if (c > max_len) - c = max_len; - memcpy(data, (void*)uddb->ADDR.reg, c); - // Notify the USB hardware that the space is now available - ude->EPSTATUSCLR.reg = bkrdy; - return c; -} - -int_fast8_t -usb_read_bulk_out(void *data, uint_fast8_t max_len) -{ - return usb_read_packet(USB_CDC_EP_BULK_OUT, 0, data, max_len); -} - -int_fast8_t -usb_send_bulk_in(void *data, uint_fast8_t len) -{ - return usb_write_packet(USB_CDC_EP_BULK_IN, 1, data, len); -} - -int_fast8_t -usb_read_ep0(void *data, uint_fast8_t max_len) -{ - return usb_read_packet(0, 0, data, max_len); -} - -int_fast8_t -usb_read_ep0_setup(void *data, uint_fast8_t max_len) -{ - return usb_read_ep0(data, max_len); -} - -int_fast8_t -usb_send_ep0(const void *data, uint_fast8_t len) -{ - return usb_write_packet(0, 1, data, len); -} - -void -usb_stall_ep0(void) -{ - EP0.EPSTATUSSET.reg = USB_DEVICE_EPSTATUS_STALLRQ(3); -} - -static uint8_t set_address; - -void -usb_set_address(uint_fast8_t addr) -{ - writeb(&set_address, addr | USB_DEVICE_DADD_ADDEN); - usb_send_ep0(NULL, 0); -} - -void -usb_set_configure(void) -{ - EP_ACM.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE1(4); - - EP_BULKOUT.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(3); - EP_BULKOUT.EPINTENSET.reg = ( - USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1); - - EP_BULKIN.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE1(3); - EP_BULKIN.EPINTENSET.reg = ( - USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1); -} - -void -usb_request_bootloader(void) -{ - if (CONFIG_FLASH_START) { - // Arduino Zero bootloader hack - irq_disable(); - writel((void*)0x20007FFC, 0x07738135); - NVIC_SystemReset(); - } -} - -void -usbserial_init(void) -{ - // configure usb clock - enable_pclock(USB_GCLK_ID, 0); - // configure USBD+ and USBD- pins - gpio_peripheral(GPIO('A', 24), 'G', 0); - gpio_peripheral(GPIO('A', 25), 'G', 0); - uint16_t trim = (readl((void*)USB_FUSES_TRIM_ADDR) - & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; - uint16_t transp = (readl((void*)USB_FUSES_TRANSP_ADDR) - & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; - uint16_t transn = (readl((void*)USB_FUSES_TRANSN_ADDR) - & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; - USB->DEVICE.PADCAL.reg = (USB_PADCAL_TRIM(trim) | USB_PADCAL_TRANSP(transp) - | USB_PADCAL_TRANSN(transn)); - // Enable USB in device mode - USB->DEVICE.CTRLA.reg = USB_CTRLA_ENABLE; - USB->DEVICE.DESCADD.reg = (uint32_t)usb_desc; - EP0.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(1) | USB_DEVICE_EPCFG_EPTYPE1(1); - EP_ACM.EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE1(4); - USB->DEVICE.CTRLB.reg = 0; - // enable irqs - USB->DEVICE.INTENSET.reg = USB_DEVICE_INTENSET_EORST; - NVIC_SetPriority(USB_IRQn, 1); - NVIC_EnableIRQ(USB_IRQn); -} -DECL_INIT(usbserial_init); - -void __visible -USB_Handler(void) -{ - uint8_t s = USB->DEVICE.INTFLAG.reg; - if (s & USB_DEVICE_INTFLAG_EORST) { - USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_EORST; - // Enable endpoint 0 irqs - EP0.EPINTENSET.reg = ( - USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1 - | USB_DEVICE_EPINTENSET_RXSTP); - } - - uint16_t ep = USB->DEVICE.EPINTSMRY.reg; - if (ep & (1<<0)) { - uint8_t sts = EP0.EPINTFLAG.reg; - EP0.EPINTFLAG.reg = sts; - if (set_address && sts & USB_DEVICE_EPINTFLAG_TRCPT1) { - // Apply address after last "in" message transmitted - USB->DEVICE.DADD.reg = set_address; - set_address = 0; - } - usb_notify_ep0(); - } - if (ep & (1<