src: Rename source folders for atsam and atsamd architectures
Signed-off-by: Florian Heilmann <Florian.Heilmann@gmx.net>
This commit is contained in:
committed by
KevinOConnor
parent
432e6c490a
commit
6256599a6d
53
src/atsam/Kconfig
Normal file
53
src/atsam/Kconfig
Normal 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
58
src/atsam/Makefile
Normal 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
96
src/atsam/adc.c
Normal 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
142
src/atsam/gpio.c
Normal 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
51
src/atsam/gpio.h
Normal 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
175
src/atsam/i2c.c
Normal 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
24
src/atsam/internal.h
Normal 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
77
src/atsam/main.c
Normal 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
16
src/atsam/sam4_cache.c
Normal 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
238
src/atsam/sam4_usb.c
Normal 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
203
src/atsam/sam4e_afec.c
Normal 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
65
src/atsam/sam4s_sysinit.c
Normal 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
118
src/atsam/sam4s_timer.c
Normal 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
77
src/atsam/serial.c
Normal 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
285
src/atsam/spi.c
Normal 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
65
src/atsam/timer.c
Normal 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
10
src/atsam/usb_cdc_ep.h
Normal 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
|
||||
Reference in New Issue
Block a user