src: Rename source folders for atsam and atsamd architectures

Signed-off-by: Florian Heilmann <Florian.Heilmann@gmx.net>
This commit is contained in:
Florian Heilmann
2019-01-13 02:14:50 +01:00
committed by KevinOConnor
parent 432e6c490a
commit 6256599a6d
37 changed files with 84 additions and 77 deletions

53
src/atsam/Kconfig Normal file
View File

@@ -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

58
src/atsam/Makefile Normal file
View File

@@ -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

96
src/atsam/adc.c Normal file
View File

@@ -0,0 +1,96 @@
// Analog to digital support
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// 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);
}

142
src/atsam/gpio.c Normal file
View File

@@ -0,0 +1,142 @@
// GPIO functions on sam3/sam4
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// 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);
}

51
src/atsam/gpio.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef __SAM3_GPIO_H
#define __SAM3_GPIO_H
#include <stdint.h> // 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

175
src/atsam/i2c.c Normal file
View File

@@ -0,0 +1,175 @@
// SAM4 I2C Port
//
// Copyright (C) 2018 Florian Heilmann <Florian.Heilmann@gmx.net>
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// 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;
}

24
src/atsam/internal.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef __SAM3_INTERNAL_H
#define __SAM3_INTERNAL_H
// Local definitions for sam3/sam4 code
#include <stdint.h> // 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

77
src/atsam/main.c Normal file
View File

@@ -0,0 +1,77 @@
// Main starting point for SAM3/SAM4 boards
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// 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;
}

16
src/atsam/sam4_cache.c Normal file
View File

@@ -0,0 +1,16 @@
// SAM4 cache enable
//
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// 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);

238
src/atsam/sam4_usb.c Normal file
View File

@@ -0,0 +1,238 @@
// Hardware interface to SAM4 USB Device Port
//
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <string.h> // 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<<USB_CDC_EP_BULK_OUT;
return -1;
}
uint32_t len = usb_read_packet(USB_CDC_EP_BULK_OUT, csr, data, max_len);
if (bk == (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1))
bk = next_bk;
next_bk = bk ^ (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1);
UDP->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<<USB_CDC_EP_BULK_IN;
return -1;
}
usb_write_packet(USB_CDC_EP_BULK_IN, data, len);
UDP->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<<USB_CDC_EP_BULK_OUT))
usb_notify_bulk_out();
if (s & (1<<USB_CDC_EP_BULK_IN))
usb_notify_bulk_in();
}

203
src/atsam/sam4e_afec.c Normal file
View File

@@ -0,0 +1,203 @@
// SAM4e8e Analog Front-End Converter (AFEC) support
//
// Copyright (C) 2018 Florian Heilmann <Florian.Heilmann@gmx.net>
//
// 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;
}
}

65
src/atsam/sam4s_sysinit.c Normal file
View File

@@ -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;
}

118
src/atsam/sam4s_timer.c Normal file
View File

@@ -0,0 +1,118 @@
// SAM4s 16bit timer interrupt scheduling
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// 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();
}

77
src/atsam/serial.c Normal file
View File

@@ -0,0 +1,77 @@
// sam3/sam4 serial port
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// 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;
}

285
src/atsam/spi.c Normal file
View File

@@ -0,0 +1,285 @@
// SPI transmissions on sam3
//
// Copyright (C) 2018 Petri Honkala <cruwaller@gmail.com>
// Copyright (C) 2018 Florian Heilmann <Florian.Heilmann@gmx.net>
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// 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);
}

65
src/atsam/timer.c Normal file
View File

@@ -0,0 +1,65 @@
// SAM3/SAM4 timer interrupt scheduling
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// 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();
}

10
src/atsam/usb_cdc_ep.h Normal file
View File

@@ -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