From 2728603fe6d73e805a539d337fd01051c46ca806 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Fri, 19 Nov 2021 18:41:02 +0000 Subject: Move tmk_core/common/ (#13918) --- platforms/arm_atsam/_timer.h | 19 + platforms/arm_atsam/_wait.h | 22 + platforms/arm_atsam/atomic_util.h | 37 ++ platforms/arm_atsam/bootloader.c | 57 +++ platforms/arm_atsam/eeprom.c | 184 ++++++++ platforms/arm_atsam/gpio.h | 77 ++++ platforms/arm_atsam/pin_defs.h | 84 ++++ platforms/arm_atsam/platform.c | 21 + platforms/arm_atsam/platform.mk | 67 +++ platforms/arm_atsam/platform_deps.h | 18 + platforms/arm_atsam/suspend.c | 77 ++++ platforms/arm_atsam/timer.c | 19 + platforms/atomic_util.h | 32 ++ platforms/avr/_print.h | 33 ++ platforms/avr/_timer.h | 19 + platforms/avr/_wait.h | 49 ++ platforms/avr/atomic_util.h | 22 + platforms/avr/bootloader.c | 293 ++++++++++++ platforms/avr/bootloader_size.c | 21 + platforms/avr/gpio.h | 49 ++ platforms/avr/pin_defs.h | 128 +++++ platforms/avr/platform.c | 21 + platforms/avr/platform.mk | 179 +++++++ platforms/avr/platform_deps.h | 20 + platforms/avr/printf.c | 20 + platforms/avr/printf.mk | 2 + platforms/avr/sleep_led.c | 124 +++++ platforms/avr/suspend.c | 152 ++++++ platforms/avr/timer.c | 133 ++++++ platforms/avr/timer_avr.h | 39 ++ platforms/avr/xprintf.S | 498 ++++++++++++++++++++ platforms/avr/xprintf.h | 103 +++++ platforms/bootloader.h | 21 + platforms/chibios/_timer.h | 19 + platforms/chibios/_wait.c | 89 ++++ platforms/chibios/_wait.h | 60 +++ platforms/chibios/atomic_util.h | 37 ++ platforms/chibios/bootloader.c | 145 ++++++ platforms/chibios/chibios_config.h | 78 ++++ platforms/chibios/eeprom_stm32.c | 687 +++++++++++++++++++++++++++ platforms/chibios/eeprom_stm32.h | 33 ++ platforms/chibios/eeprom_stm32_defs.h | 74 +++ platforms/chibios/eeprom_teensy.c | 795 ++++++++++++++++++++++++++++++++ platforms/chibios/flash_stm32.c | 208 +++++++++ platforms/chibios/flash_stm32.h | 44 ++ platforms/chibios/gd32v_compatibility.h | 120 +++++ platforms/chibios/gpio.h | 50 ++ platforms/chibios/pin_defs.h | 323 +++++++++++++ platforms/chibios/platform.c | 22 + platforms/chibios/platform.mk | 436 ++++++++++++++++++ platforms/chibios/platform_deps.h | 19 + platforms/chibios/sleep_led.c | 192 ++++++++ platforms/chibios/suspend.c | 92 ++++ platforms/chibios/syscall-fallbacks.c | 110 +++++ platforms/chibios/timer.c | 47 ++ platforms/chibios/wait.c | 41 ++ platforms/common.mk | 12 + platforms/eeprom.h | 21 + platforms/gpio.h | 22 + platforms/pin_defs.h | 23 + platforms/progmem.h | 19 + platforms/sleep_led.h | 17 + platforms/suspend.h | 20 + platforms/test/_wait.h | 22 + platforms/test/bootloader.c | 19 + platforms/test/eeprom.c | 95 ++++ platforms/test/eeprom_stm32_tests.cpp | 438 ++++++++++++++++++ platforms/test/flash_stm32_mock.c | 49 ++ platforms/test/hal.h | 18 + platforms/test/platform.c | 21 + platforms/test/platform.h | 18 + platforms/test/platform.mk | 34 ++ platforms/test/platform_deps.h | 18 + platforms/test/rules.mk | 24 + platforms/test/suspend.c | 15 + platforms/test/testlist.mk | 1 + platforms/test/timer.c | 33 ++ platforms/timer.h | 67 +++ platforms/wait.h | 30 ++ 79 files changed, 7397 insertions(+) create mode 100644 platforms/arm_atsam/_timer.h create mode 100644 platforms/arm_atsam/_wait.h create mode 100644 platforms/arm_atsam/atomic_util.h create mode 100644 platforms/arm_atsam/bootloader.c create mode 100644 platforms/arm_atsam/eeprom.c create mode 100644 platforms/arm_atsam/gpio.h create mode 100644 platforms/arm_atsam/pin_defs.h create mode 100644 platforms/arm_atsam/platform.c create mode 100644 platforms/arm_atsam/platform.mk create mode 100644 platforms/arm_atsam/platform_deps.h create mode 100644 platforms/arm_atsam/suspend.c create mode 100644 platforms/arm_atsam/timer.c create mode 100644 platforms/atomic_util.h create mode 100644 platforms/avr/_print.h create mode 100644 platforms/avr/_timer.h create mode 100644 platforms/avr/_wait.h create mode 100644 platforms/avr/atomic_util.h create mode 100644 platforms/avr/bootloader.c create mode 100644 platforms/avr/bootloader_size.c create mode 100644 platforms/avr/gpio.h create mode 100644 platforms/avr/pin_defs.h create mode 100644 platforms/avr/platform.c create mode 100644 platforms/avr/platform.mk create mode 100644 platforms/avr/platform_deps.h create mode 100644 platforms/avr/printf.c create mode 100644 platforms/avr/printf.mk create mode 100644 platforms/avr/sleep_led.c create mode 100644 platforms/avr/suspend.c create mode 100644 platforms/avr/timer.c create mode 100644 platforms/avr/timer_avr.h create mode 100644 platforms/avr/xprintf.S create mode 100644 platforms/avr/xprintf.h create mode 100644 platforms/bootloader.h create mode 100644 platforms/chibios/_timer.h create mode 100644 platforms/chibios/_wait.c create mode 100644 platforms/chibios/_wait.h create mode 100644 platforms/chibios/atomic_util.h create mode 100644 platforms/chibios/bootloader.c create mode 100644 platforms/chibios/chibios_config.h create mode 100644 platforms/chibios/eeprom_stm32.c create mode 100644 platforms/chibios/eeprom_stm32.h create mode 100644 platforms/chibios/eeprom_stm32_defs.h create mode 100644 platforms/chibios/eeprom_teensy.c create mode 100644 platforms/chibios/flash_stm32.c create mode 100644 platforms/chibios/flash_stm32.h create mode 100644 platforms/chibios/gd32v_compatibility.h create mode 100644 platforms/chibios/gpio.h create mode 100644 platforms/chibios/pin_defs.h create mode 100644 platforms/chibios/platform.c create mode 100644 platforms/chibios/platform.mk create mode 100644 platforms/chibios/platform_deps.h create mode 100644 platforms/chibios/sleep_led.c create mode 100644 platforms/chibios/suspend.c create mode 100644 platforms/chibios/syscall-fallbacks.c create mode 100644 platforms/chibios/timer.c create mode 100644 platforms/chibios/wait.c create mode 100644 platforms/common.mk create mode 100644 platforms/eeprom.h create mode 100644 platforms/gpio.h create mode 100644 platforms/pin_defs.h create mode 100644 platforms/progmem.h create mode 100644 platforms/sleep_led.h create mode 100644 platforms/suspend.h create mode 100644 platforms/test/_wait.h create mode 100644 platforms/test/bootloader.c create mode 100644 platforms/test/eeprom.c create mode 100644 platforms/test/eeprom_stm32_tests.cpp create mode 100644 platforms/test/flash_stm32_mock.c create mode 100644 platforms/test/hal.h create mode 100644 platforms/test/platform.c create mode 100644 platforms/test/platform.h create mode 100644 platforms/test/platform.mk create mode 100644 platforms/test/platform_deps.h create mode 100644 platforms/test/rules.mk create mode 100644 platforms/test/suspend.c create mode 100644 platforms/test/testlist.mk create mode 100644 platforms/test/timer.c create mode 100644 platforms/timer.h create mode 100644 platforms/wait.h (limited to 'platforms') diff --git a/platforms/arm_atsam/_timer.h b/platforms/arm_atsam/_timer.h new file mode 100644 index 0000000000..77402b612a --- /dev/null +++ b/platforms/arm_atsam/_timer.h @@ -0,0 +1,19 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// The platform is 32-bit, so prefer 32-bit timers to avoid overflow +#define FAST_TIMER_T_SIZE 32 diff --git a/platforms/arm_atsam/_wait.h b/platforms/arm_atsam/_wait.h new file mode 100644 index 0000000000..41b686b56c --- /dev/null +++ b/platforms/arm_atsam/_wait.h @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "clks.h" + +#define wait_ms(ms) CLK_delay_ms(ms) +#define wait_us(us) CLK_delay_us(us) +#define waitInputPinDelay() diff --git a/platforms/arm_atsam/atomic_util.h b/platforms/arm_atsam/atomic_util.h new file mode 100644 index 0000000000..848542d23a --- /dev/null +++ b/platforms/arm_atsam/atomic_util.h @@ -0,0 +1,37 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "samd51j18a.h" + +static __inline__ uint8_t __interrupt_disable__(void) { + __disable_irq(); + + return 1; +} + +static __inline__ void __interrupt_enable__(const uint8_t *__s) { + __enable_irq(); + + __asm__ volatile("" ::: "memory"); + (void)__s; +} + +#define ATOMIC_BLOCK(type) for (type, __ToDo = __interrupt_disable__(); __ToDo; __ToDo = 0) +#define ATOMIC_FORCEON uint8_t sreg_save __attribute__((__cleanup__(__interrupt_enable__))) = 0 + +#define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE not implemented") +#define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON) diff --git a/platforms/arm_atsam/bootloader.c b/platforms/arm_atsam/bootloader.c new file mode 100644 index 0000000000..9015b00aab --- /dev/null +++ b/platforms/arm_atsam/bootloader.c @@ -0,0 +1,57 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "bootloader.h" +#include "samd51j18a.h" +#include "md_bootloader.h" + +// Set watchdog timer to reset. Directs the bootloader to stay in programming mode. +void bootloader_jump(void) { +#ifdef KEYBOARD_massdrop_ctrl + // CTRL keyboards released with bootloader version below must use RAM method. Otherwise use WDT method. + uint8_t ver_ram_method[] = "v2.18Jun 22 2018 17:28:08"; // The version to match (NULL terminated by compiler) + uint8_t *ver_check = ver_ram_method; // Pointer to version match string for traversal + uint8_t *ver_rom = (uint8_t *)0x21A0; // Pointer to address in ROM where this specific bootloader version would exist + + while (*ver_check && *ver_rom == *ver_check) { // While there are check version characters to match and bootloader's version matches check's version + ver_check++; // Move check version pointer to next character + ver_rom++; // Move ROM version pointer to next character + } + + if (!*ver_check) { // If check version pointer is NULL, all characters have matched + *MAGIC_ADDR = BOOTLOADER_MAGIC; // Set magic number into RAM + NVIC_SystemReset(); // Perform system reset + while (1) { + } // Won't get here + } +#endif + + WDT->CTRLA.bit.ENABLE = 0; + while (WDT->SYNCBUSY.bit.ENABLE) { + } + while (WDT->CTRLA.bit.ENABLE) { + } + WDT->CONFIG.bit.WINDOW = 0; + WDT->CONFIG.bit.PER = 0; + WDT->EWCTRL.bit.EWOFFSET = 0; + WDT->CTRLA.bit.ENABLE = 1; + while (WDT->SYNCBUSY.bit.ENABLE) { + } + while (!WDT->CTRLA.bit.ENABLE) { + } + while (1) { + } // Wait on timeout +} diff --git a/platforms/arm_atsam/eeprom.c b/platforms/arm_atsam/eeprom.c new file mode 100644 index 0000000000..ff1a692623 --- /dev/null +++ b/platforms/arm_atsam/eeprom.c @@ -0,0 +1,184 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "eeprom.h" +#include "debug.h" +#include "samd51j18a.h" +#include "core_cm4.h" +#include "component/nvmctrl.h" + +#ifndef EEPROM_SIZE +# include "eeconfig.h" +# define EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO +#endif + +#ifndef MAX +# define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) +#endif + +#ifndef BUSY_RETRIES +# define BUSY_RETRIES 10000 +#endif + +// #define DEBUG_EEPROM_OUTPUT + +/* + * Debug print utils + */ +#if defined(DEBUG_EEPROM_OUTPUT) +# define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__); +#else /* NO_DEBUG */ +# define eeprom_printf(fmt, ...) +#endif /* NO_DEBUG */ + +__attribute__((aligned(4))) static uint8_t buffer[EEPROM_SIZE] = {0}; +volatile uint8_t * SmartEEPROM8 = (uint8_t *)SEEPROM_ADDR; + +static inline bool eeprom_is_busy(void) { + int timeout = BUSY_RETRIES; + while (NVMCTRL->SEESTAT.bit.BUSY && timeout-- > 0) + ; + + return NVMCTRL->SEESTAT.bit.BUSY; +} + +static uint32_t get_virtual_eeprom_size(void) { + // clang-format off + static const uint32_t VIRTUAL_EEPROM_MAP[11][8] = { + /* 4 8 16 32 64 128 256 512 */ + /* 0*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, + /* 1*/ { 512, 1024, 2048, 4096, 4096, 4096, 4096, 4096 }, + /* 2*/ { 512, 1024, 2048, 4096, 8192, 8192, 8192, 8192 }, + /* 3*/ { 512, 1024, 2048, 4096, 8192, 16384, 16384, 16384 }, + /* 4*/ { 512, 1024, 2048, 4096, 8192, 16384, 16384, 16384 }, + /* 5*/ { 512, 1024, 2048, 4096, 8192, 16384, 32768, 32768 }, + /* 6*/ { 512, 1024, 2048, 4096, 8192, 16384, 32768, 32768 }, + /* 7*/ { 512, 1024, 2048, 4096, 8192, 16384, 32768, 32768 }, + /* 8*/ { 512, 1024, 2048, 4096, 8192, 16384, 32768, 32768 }, + /* 9*/ { 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 }, + /*10*/ { 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 }, + }; + // clang-format on + + static uint32_t virtual_eeprom_size = UINT32_MAX; + if (virtual_eeprom_size == UINT32_MAX) { + virtual_eeprom_size = VIRTUAL_EEPROM_MAP[NVMCTRL->SEESTAT.bit.PSZ][NVMCTRL->SEESTAT.bit.SBLK]; + } + // eeprom_printf("get_virtual_eeprom_size:: %d:%d:%d\n", NVMCTRL->SEESTAT.bit.PSZ, NVMCTRL->SEESTAT.bit.SBLK, virtual_eeprom_size); + return virtual_eeprom_size; +} + +uint8_t eeprom_read_byte(const uint8_t *addr) { + uintptr_t offset = (uintptr_t)addr; + if (offset >= MAX(EEPROM_SIZE, get_virtual_eeprom_size())) { + eeprom_printf("eeprom_read_byte:: out of bounds\n"); + return 0x0; + } + + if (get_virtual_eeprom_size() == 0) { + return buffer[offset]; + } + + if (eeprom_is_busy()) { + eeprom_printf("eeprom_write_byte:: timeout\n"); + return 0x0; + } + + return SmartEEPROM8[offset]; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t value) { + uintptr_t offset = (uintptr_t)addr; + if (offset >= MAX(EEPROM_SIZE, get_virtual_eeprom_size())) { + eeprom_printf("eeprom_write_byte:: out of bounds\n"); + return; + } + + if (get_virtual_eeprom_size() == 0) { + buffer[offset] = value; + return; + } + + if (eeprom_is_busy()) { + eeprom_printf("eeprom_write_byte:: timeout\n"); + return; + } + + SmartEEPROM8[offset] = value; +} + +uint16_t eeprom_read_word(const uint16_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24); +} + +void eeprom_read_block(void *buf, const void *addr, size_t len) { + const uint8_t *p = (const uint8_t *)addr; + uint8_t * dest = (uint8_t *)buf; + while (len--) { + *dest++ = eeprom_read_byte(p++); + } +} + +void eeprom_write_word(uint16_t *addr, uint16_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_write_block(const void *buf, void *addr, size_t len) { + uint8_t * p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} + +void eeprom_update_byte(uint8_t *addr, uint8_t value) { eeprom_write_byte(addr, value); } + +void eeprom_update_word(uint16_t *addr, uint16_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_update_dword(uint32_t *addr, uint32_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_update_block(const void *buf, void *addr, size_t len) { + uint8_t * p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} diff --git a/platforms/arm_atsam/gpio.h b/platforms/arm_atsam/gpio.h new file mode 100644 index 0000000000..915ed0ef4f --- /dev/null +++ b/platforms/arm_atsam/gpio.h @@ -0,0 +1,77 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "stdint.h" +#include "samd51j18a.h" + +#include "pin_defs.h" + +typedef uint8_t pin_t; + +#define SAMD_PORT(pin) ((pin & 0x20) >> 5) +#define SAMD_PIN(pin) (pin & 0x1f) +#define SAMD_PIN_MASK(pin) (1 << (pin & 0x1f)) + +#define setPinInput(pin) \ + do { \ + PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.INEN = 1; \ + PORT->Group[SAMD_PORT(pin)].DIRCLR.reg = SAMD_PIN_MASK(pin); \ + } while (0) + +#define setPinInputHigh(pin) \ + do { \ + PORT->Group[SAMD_PORT(pin)].DIRCLR.reg = SAMD_PIN_MASK(pin); \ + PORT->Group[SAMD_PORT(pin)].OUTSET.reg = SAMD_PIN_MASK(pin); \ + PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.INEN = 1; \ + PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.PULLEN = 1; \ + } while (0) + +#define setPinInputLow(pin) \ + do { \ + PORT->Group[SAMD_PORT(pin)].DIRCLR.reg = SAMD_PIN_MASK(pin); \ + PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \ + PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.INEN = 1; \ + PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.PULLEN = 1; \ + } while (0) + +#define setPinOutput(pin) \ + do { \ + PORT->Group[SAMD_PORT(pin)].DIRSET.reg = SAMD_PIN_MASK(pin); \ + PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \ + } while (0) + +#define writePinHigh(pin) \ + do { \ + PORT->Group[SAMD_PORT(pin)].OUTSET.reg = SAMD_PIN_MASK(pin); \ + } while (0) + +#define writePinLow(pin) \ + do { \ + PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \ + } while (0) + +#define writePin(pin, level) \ + do { \ + if (level) \ + PORT->Group[SAMD_PORT(pin)].OUTSET.reg = SAMD_PIN_MASK(pin); \ + else \ + PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \ + } while (0) + +#define readPin(pin) ((PORT->Group[SAMD_PORT(pin)].IN.reg & SAMD_PIN_MASK(pin)) != 0) + +#define togglePin(pin) (PORT->Group[SAMD_PORT(pin)].OUTTGL.reg = SAMD_PIN_MASK(pin)) diff --git a/platforms/arm_atsam/pin_defs.h b/platforms/arm_atsam/pin_defs.h new file mode 100644 index 0000000000..5b50b23910 --- /dev/null +++ b/platforms/arm_atsam/pin_defs.h @@ -0,0 +1,84 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "samd51j18a.h" + +#define A00 PIN_PA00 +#define A01 PIN_PA01 +#define A02 PIN_PA02 +#define A03 PIN_PA03 +#define A04 PIN_PA04 +#define A05 PIN_PA05 +#define A06 PIN_PA06 +#define A07 PIN_PA07 +#define A08 PIN_PA08 +#define A09 PIN_PA09 +#define A10 PIN_PA10 +#define A11 PIN_PA11 +#define A12 PIN_PA12 +#define A13 PIN_PA13 +#define A14 PIN_PA14 +#define A15 PIN_PA15 +#define A16 PIN_PA16 +#define A17 PIN_PA17 +#define A18 PIN_PA18 +#define A19 PIN_PA19 +#define A20 PIN_PA20 +#define A21 PIN_PA21 +#define A22 PIN_PA22 +#define A23 PIN_PA23 +#define A24 PIN_PA24 +#define A25 PIN_PA25 +#define A26 PIN_PA26 +#define A27 PIN_PA27 +#define A28 PIN_PA28 +#define A29 PIN_PA29 +#define A30 PIN_PA30 +#define A31 PIN_PA31 + +#define B00 PIN_PB00 +#define B01 PIN_PB01 +#define B02 PIN_PB02 +#define B03 PIN_PB03 +#define B04 PIN_PB04 +#define B05 PIN_PB05 +#define B06 PIN_PB06 +#define B07 PIN_PB07 +#define B08 PIN_PB08 +#define B09 PIN_PB09 +#define B10 PIN_PB10 +#define B11 PIN_PB11 +#define B12 PIN_PB12 +#define B13 PIN_PB13 +#define B14 PIN_PB14 +#define B15 PIN_PB15 +#define B16 PIN_PB16 +#define B17 PIN_PB17 +#define B18 PIN_PB18 +#define B19 PIN_PB19 +#define B20 PIN_PB20 +#define B21 PIN_PB21 +#define B22 PIN_PB22 +#define B23 PIN_PB23 +#define B24 PIN_PB24 +#define B25 PIN_PB25 +#define B26 PIN_PB26 +#define B27 PIN_PB27 +#define B28 PIN_PB28 +#define B29 PIN_PB29 +#define B30 PIN_PB30 +#define B31 PIN_PB31 diff --git a/platforms/arm_atsam/platform.c b/platforms/arm_atsam/platform.c new file mode 100644 index 0000000000..3e35b4fe4c --- /dev/null +++ b/platforms/arm_atsam/platform.c @@ -0,0 +1,21 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform_deps.h" + +void platform_setup(void) { + // do nothing +} diff --git a/platforms/arm_atsam/platform.mk b/platforms/arm_atsam/platform.mk new file mode 100644 index 0000000000..b49bf764d7 --- /dev/null +++ b/platforms/arm_atsam/platform.mk @@ -0,0 +1,67 @@ +# Hey Emacs, this is a -*- makefile -*- +############################################################################## +# Compiler settings +# +CC = $(CC_PREFIX) arm-none-eabi-gcc +OBJCOPY = arm-none-eabi-objcopy +OBJDUMP = arm-none-eabi-objdump +SIZE = arm-none-eabi-size +AR = arm-none-eabi-ar +NM = arm-none-eabi-nm +HEX = $(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock -R .signature +EEP = $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT) +BIN = + +COMMON_VPATH += $(LIB_PATH)/arm_atsam/packs/atmel/SAMD51_DFP/1.0.70/include +COMMON_VPATH += $(LIB_PATH)/arm_atsam/packs/arm/cmsis/5.0.1/CMSIS/Include + +COMPILEFLAGS += -funsigned-char +COMPILEFLAGS += -funsigned-bitfields +COMPILEFLAGS += -ffunction-sections +COMPILEFLAGS += -fshort-enums +COMPILEFLAGS += -fno-inline-small-functions +COMPILEFLAGS += -fno-strict-aliasing +COMPILEFLAGS += -mfloat-abi=hard +COMPILEFLAGS += -mfpu=fpv4-sp-d16 +COMPILEFLAGS += -mthumb + +#ALLOW_WARNINGS = yes + +CFLAGS += $(COMPILEFLAGS) + +CXXFLAGS += $(COMPILEFLAGS) +CXXFLAGS += -fno-exceptions -std=c++11 + +LDFLAGS +=-Wl,--gc-sections +LDFLAGS += -Wl,-Map="%OUT%%PROJ_NAME%.map" +LDFLAGS += -Wl,--start-group +LDFLAGS += -Wl,--end-group +LDFLAGS += --specs=rdimon.specs +LDFLAGS += -T$(LIB_PATH)/arm_atsam/packs/atmel/SAMD51_DFP/1.0.70/gcc/gcc/samd51j18a_flash.ld + +OPT_DEFS += -DPROTOCOL_ARM_ATSAM + +MCUFLAGS = -mcpu=$(MCU) +MCUFLAGS += -D__$(ARM_ATSAM)__ + +# List any extra directories to look for libraries here. +# Each directory must be seperated by a space. +# Use forward slashes for directory separators. +# For a directory that has spaces, enclose it in quotes. +EXTRALIBDIRS = + +cpfirmware: warn-arm_atsam +.INTERMEDIATE: warn-arm_atsam +warn-arm_atsam: $(FIRMWARE_FORMAT) + $(info @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@) + $(info This MCU support package has a lack of support from the upstream provider (Massdrop).) + $(info There are currently questions about valid licensing, and at this stage it's likely) + $(info their boards and supporting code will be removed from QMK in the near future. Please) + $(info contact Massdrop for support, and encourage them to align their future board design) + $(info choices to gain proper license compatibility with QMK.) + $(info @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@) + +# Convert hex to bin. +bin: $(BUILD_DIR)/$(TARGET).hex + $(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin + $(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin; diff --git a/platforms/arm_atsam/platform_deps.h b/platforms/arm_atsam/platform_deps.h new file mode 100644 index 0000000000..f296d1d535 --- /dev/null +++ b/platforms/arm_atsam/platform_deps.h @@ -0,0 +1,18 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// here just to please the build diff --git a/platforms/arm_atsam/suspend.c b/platforms/arm_atsam/suspend.c new file mode 100644 index 0000000000..e51426128d --- /dev/null +++ b/platforms/arm_atsam/suspend.c @@ -0,0 +1,77 @@ +#include "matrix.h" +#include "i2c_master.h" +#include "md_rgb_matrix.h" +#include "suspend.h" + +/** \brief Suspend idle + * + * FIXME: needs doc + */ +void suspend_idle(uint8_t time) { /* Note: Not used anywhere currently */ +} + +/** \brief Run user level Power down + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_power_down_user(void) {} + +/** \brief Run keyboard level Power down + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_power_down_kb(void) { suspend_power_down_user(); } + +/** \brief Suspend power down + * + * FIXME: needs doc + */ +void suspend_power_down(void) { +#ifdef RGB_MATRIX_ENABLE + I2C3733_Control_Set(0); // Disable LED driver +#endif + + suspend_power_down_kb(); +} + +__attribute__((weak)) void matrix_power_up(void) {} +__attribute__((weak)) void matrix_power_down(void) {} +bool suspend_wakeup_condition(void) { + matrix_power_up(); + matrix_scan(); + matrix_power_down(); + for (uint8_t r = 0; r < MATRIX_ROWS; r++) { + if (matrix_get_row(r)) return true; + } + return false; +} + +/** \brief run user level code immediately after wakeup + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_wakeup_init_user(void) {} + +/** \brief run keyboard level code immediately after wakeup + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); } + +/** \brief run immediately after wakeup + * + * FIXME: needs doc + */ +void suspend_wakeup_init(void) { +#ifdef RGB_MATRIX_ENABLE +# ifdef USE_MASSDROP_CONFIGURATOR + if (led_enabled) { + I2C3733_Control_Set(1); + } +# else + I2C3733_Control_Set(1); +# endif +#endif + + suspend_wakeup_init_kb(); +} diff --git a/platforms/arm_atsam/timer.c b/platforms/arm_atsam/timer.c new file mode 100644 index 0000000000..b835dd5e75 --- /dev/null +++ b/platforms/arm_atsam/timer.c @@ -0,0 +1,19 @@ +#include "samd51j18a.h" +#include "timer.h" +#include "tmk_core/protocol/arm_atsam/clks.h" + +void set_time(uint64_t tset) { ms_clk = tset; } + +void timer_init(void) { timer_clear(); } + +uint16_t timer_read(void) { return (uint16_t)ms_clk; } + +uint32_t timer_read32(void) { return (uint32_t)ms_clk; } + +uint64_t timer_read64(void) { return ms_clk; } + +uint16_t timer_elapsed(uint16_t tlast) { return TIMER_DIFF_16(timer_read(), tlast); } + +uint32_t timer_elapsed32(uint32_t tlast) { return TIMER_DIFF_32(timer_read32(), tlast); } + +void timer_clear(void) { set_time(0); } diff --git a/platforms/atomic_util.h b/platforms/atomic_util.h new file mode 100644 index 0000000000..2c95302a13 --- /dev/null +++ b/platforms/atomic_util.h @@ -0,0 +1,32 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// Macro to help make GPIO and other controls atomic. + +#ifndef IGNORE_ATOMIC_BLOCK +# if __has_include_next("atomic_util.h") +# include_next "atomic_util.h" /* Include the platforms atomic.h */ +# else +# define ATOMIC_BLOCK _Static_assert(0, "ATOMIC_BLOCK not implemented") +# define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE not implemented") +# define ATOMIC_BLOCK_FORCEON _Static_assert(0, "ATOMIC_BLOCK_FORCEON not implemented") +# endif +#else /* do nothing atomic macro */ +# define ATOMIC_BLOCK for (uint8_t __ToDo = 1; __ToDo; __ToDo = 0) +# define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK +# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK +#endif diff --git a/platforms/avr/_print.h b/platforms/avr/_print.h new file mode 100644 index 0000000000..5c1fdd26d8 --- /dev/null +++ b/platforms/avr/_print.h @@ -0,0 +1,33 @@ +/* Copyright 2012 Jun Wako */ +/* Very basic print functions, intended to be used with usb_debug_only.c + * http://www.pjrc.com/teensy/ + * Copyright (c) 2008 PJRC.COM, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include "avr/xprintf.h" + +// Create user & normal print defines +#define print(s) xputs(PSTR(s)) +#define println(s) xputs(PSTR(s "\r\n")) +#define uprint(s) xputs(PSTR(s)) +#define uprintln(s) xputs(PSTR(s "\r\n")) +#define uprintf(fmt, ...) __xprintf(PSTR(fmt), ##__VA_ARGS__) \ No newline at end of file diff --git a/platforms/avr/_timer.h b/platforms/avr/_timer.h new file mode 100644 index 0000000000..b81e0f68b7 --- /dev/null +++ b/platforms/avr/_timer.h @@ -0,0 +1,19 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// The platform is 8-bit, so prefer 16-bit timers to reduce code size +#define FAST_TIMER_T_SIZE 16 diff --git a/platforms/avr/_wait.h b/platforms/avr/_wait.h new file mode 100644 index 0000000000..683db6ae57 --- /dev/null +++ b/platforms/avr/_wait.h @@ -0,0 +1,49 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +#define wait_ms(ms) \ + do { \ + if (__builtin_constant_p(ms)) { \ + _delay_ms(ms); \ + } else { \ + for (uint16_t i = ms; i > 0; i--) { \ + _delay_ms(1); \ + } \ + } \ + } while (0) +#define wait_us(us) \ + do { \ + if (__builtin_constant_p(us)) { \ + _delay_us(us); \ + } else { \ + for (uint16_t i = us; i > 0; i--) { \ + _delay_us(1); \ + } \ + } \ + } while (0) +#define wait_cpuclock(n) __builtin_avr_delay_cycles(n) +#define CPU_CLOCK F_CPU + +/* The AVR series GPIOs have a one clock read delay for changes in the digital input signal. + * But here's more margin to make it two clocks. */ +#ifndef GPIO_INPUT_PIN_DELAY +# define GPIO_INPUT_PIN_DELAY 2 +#endif + +#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) diff --git a/platforms/avr/atomic_util.h b/platforms/avr/atomic_util.h new file mode 100644 index 0000000000..7c5d2e7dcc --- /dev/null +++ b/platforms/avr/atomic_util.h @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +/* atomic macro for AVR */ +#include + +#define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK(ATOMIC_RESTORESTATE) +#define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON) diff --git a/platforms/avr/bootloader.c b/platforms/avr/bootloader.c new file mode 100644 index 0000000000..c0272903b8 --- /dev/null +++ b/platforms/avr/bootloader.c @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include +#include +#include +#include "bootloader.h" +#include + +#ifdef PROTOCOL_LUFA +# include +#endif + +/** \brief Bootloader Size in *bytes* + * + * AVR Boot section size are defined by setting BOOTSZ fuse in fact. Consult with your MCU datasheet. + * Note that 'Word'(2 bytes) size and address are used in datasheet while TMK uses 'Byte'. + * + * Size of Bootloaders in bytes: + * Atmel DFU loader(ATmega32U4) 4096 + * Atmel DFU loader(AT90USB128) 8192 + * LUFA bootloader(ATmega32U4) 4096 + * Arduino Caterina(ATmega32U4) 4096 + * USBaspLoader(ATmega***) 2048 + * Teensy halfKay(ATmega32U4) 512 + * Teensy++ halfKay(AT90USB128) 1024 + * + * AVR Boot section is located at the end of Flash memory like the followings. + * + * byte Atmel/LUFA(ATMega32u4) byte Atmel(AT90SUB128) + * 0x0000 +---------------+ 0x00000 +---------------+ + * | | | | + * | | | | + * | Application | | Application | + * | | | | + * = = = = + * | | 32KB-4KB | | 128KB-8KB + * 0x7000 +---------------+ 0x1E000 +---------------+ + * | Bootloader | 4KB | Bootloader | 8KB + * 0x7FFF +---------------+ 0x1FFFF +---------------+ + * + * + * byte Teensy(ATMega32u4) byte Teensy++(AT90SUB128) + * 0x0000 +---------------+ 0x00000 +---------------+ + * | | | | + * | | | | + * | Application | | Application | + * | | | | + * = = = = + * | | 32KB-512B | | 128KB-1KB + * 0x7E00 +---------------+ 0x1FC00 +---------------+ + * | Bootloader | 512B | Bootloader | 1KB + * 0x7FFF +---------------+ 0x1FFFF +---------------+ + */ +#define FLASH_SIZE (FLASHEND + 1L) + +#if !defined(BOOTLOADER_SIZE) +uint16_t bootloader_start; +#endif + +// compatibility between ATMega8 and ATMega88 +#if !defined(MCUCSR) +# if defined(MCUSR) +# define MCUCSR MCUSR +# endif +#endif + +/** \brief Entering the Bootloader via Software + * + * http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html + */ +#define BOOTLOADER_RESET_KEY 0xB007B007 +uint32_t reset_key __attribute__((section(".noinit,\"aw\",@nobits;"))); + +/** \brief initialize MCU status by watchdog reset + * + * FIXME: needs doc + */ +__attribute__((weak)) void bootloader_jump(void) { +#if !defined(BOOTLOADER_SIZE) + uint8_t high_fuse = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS); + + if (high_fuse & ~(FUSE_BOOTSZ0 & FUSE_BOOTSZ1)) { + bootloader_start = (FLASH_SIZE - 512) >> 1; + } else if (high_fuse & ~(FUSE_BOOTSZ1)) { + bootloader_start = (FLASH_SIZE - 1024) >> 1; + } else if (high_fuse & ~(FUSE_BOOTSZ0)) { + bootloader_start = (FLASH_SIZE - 2048) >> 1; + } else { + bootloader_start = (FLASH_SIZE - 4096) >> 1; + } +#endif + + // Something like this might work, but it compiled larger than the block above + // bootloader_start = FLASH_SIZE - (256 << (~high_fuse & 0b110 >> 1)); + +#if defined(BOOTLOADER_HALFKAY) + // http://www.pjrc.com/teensy/jump_to_bootloader.html + cli(); + // disable watchdog, if enabled (it's not) + // disable all peripherals + // a shutdown call might make sense here + UDCON = 1; + USBCON = (1 << FRZCLK); // disable USB + UCSR1B = 0; + _delay_ms(5); +# if defined(__AVR_AT90USB162__) // Teensy 1.0 + EIMSK = 0; + PCICR = 0; + SPCR = 0; + ACSR = 0; + EECR = 0; + TIMSK0 = 0; + TIMSK1 = 0; + UCSR1B = 0; + DDRB = 0; + DDRC = 0; + DDRD = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; + asm volatile("jmp 0x3E00"); +# elif defined(__AVR_ATmega32U4__) // Teensy 2.0 + EIMSK = 0; + PCICR = 0; + SPCR = 0; + ACSR = 0; + EECR = 0; + ADCSRA = 0; + TIMSK0 = 0; + TIMSK1 = 0; + TIMSK3 = 0; + TIMSK4 = 0; + UCSR1B = 0; + TWCR = 0; + DDRB = 0; + DDRC = 0; + DDRD = 0; + DDRE = 0; + DDRF = 0; + TWCR = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; + PORTE = 0; + PORTF = 0; + asm volatile("jmp 0x7E00"); +# elif defined(__AVR_AT90USB646__) // Teensy++ 1.0 + EIMSK = 0; + PCICR = 0; + SPCR = 0; + ACSR = 0; + EECR = 0; + ADCSRA = 0; + TIMSK0 = 0; + TIMSK1 = 0; + TIMSK2 = 0; + TIMSK3 = 0; + UCSR1B = 0; + TWCR = 0; + DDRA = 0; + DDRB = 0; + DDRC = 0; + DDRD = 0; + DDRE = 0; + DDRF = 0; + PORTA = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; + PORTE = 0; + PORTF = 0; + asm volatile("jmp 0xFC00"); +# elif defined(__AVR_AT90USB1286__) // Teensy++ 2.0 + EIMSK = 0; + PCICR = 0; + SPCR = 0; + ACSR = 0; + EECR = 0; + ADCSRA = 0; + TIMSK0 = 0; + TIMSK1 = 0; + TIMSK2 = 0; + TIMSK3 = 0; + UCSR1B = 0; + TWCR = 0; + DDRA = 0; + DDRB = 0; + DDRC = 0; + DDRD = 0; + DDRE = 0; + DDRF = 0; + PORTA = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; + PORTE = 0; + PORTF = 0; + asm volatile("jmp 0x1FC00"); +# endif + +#elif defined(BOOTLOADER_CATERINA) + // this block may be optional + // TODO: figure it out + + uint16_t *const bootKeyPtr = (uint16_t *)0x0800; + + // Value used by Caterina bootloader use to determine whether to run the + // sketch or the bootloader programmer. + uint16_t bootKey = 0x7777; + + *bootKeyPtr = bootKey; + + // setup watchdog timeout + wdt_enable(WDTO_60MS); + + while (1) { + } // wait for watchdog timer to trigger + +#elif defined(BOOTLOADER_USBASP) + // Taken with permission of Stephan Baerwolf from https://github.com/tinyusbboard/API/blob/master/apipage.c + wdt_enable(WDTO_15MS); + wdt_reset(); + asm volatile("cli \n\t" + "ldi r29 , %[ramendhi] \n\t" + "ldi r28 , %[ramendlo] \n\t" +# if (FLASHEND > 131071) + "ldi r18 , %[bootaddrhi] \n\t" + "st Y+, r18 \n\t" +# endif + "ldi r18 , %[bootaddrme] \n\t" + "st Y+, r18 \n\t" + "ldi r18 , %[bootaddrlo] \n\t" + "st Y+, r18 \n\t" + "out %[mcucsrio], __zero_reg__ \n\t" + "bootloader_startup_loop%=: \n\t" + "rjmp bootloader_startup_loop%= \n\t" + : + : [mcucsrio] "I"(_SFR_IO_ADDR(MCUCSR)), +# if (FLASHEND > 131071) + [ramendhi] "M"(((RAMEND - 2) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 2) >> 0) & 0xff), [bootaddrhi] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 16) & 0xff), +# else + [ramendhi] "M"(((RAMEND - 1) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 1) >> 0) & 0xff), +# endif + [bootaddrme] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 8) & 0xff), [bootaddrlo] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 0) & 0xff)); + +#else // Assume remaining boards are DFU, even if the flag isn't set + +# if !(defined(__AVR_ATmega32A__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATtiny85__)) // no USB - maybe BOOTLOADER_BOOTLOADHID instead though? + UDCON = 1; + USBCON = (1 << FRZCLK); // disable USB + UCSR1B = 0; + _delay_ms(5); // 5 seems to work fine +# endif + +# ifdef BOOTLOADER_BOOTLOADHID + // force bootloadHID to stay in bootloader mode, so that it waits + // for a new firmware to be flashed + eeprom_write_byte((uint8_t *)1, 0x00); +# endif + + // watchdog reset + reset_key = BOOTLOADER_RESET_KEY; + wdt_enable(WDTO_250MS); + for (;;) + ; +#endif +} + +/* this runs before main() */ +void bootloader_jump_after_watchdog_reset(void) __attribute__((used, naked, section(".init3"))); +void bootloader_jump_after_watchdog_reset(void) { +#ifndef BOOTLOADER_HALFKAY + if ((MCUCSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) { + reset_key = 0; + + // My custom USBasploader requires this to come up. + MCUCSR = 0; + + // Seems like Teensy halfkay loader requires clearing WDRF and disabling watchdog. + MCUCSR &= ~(1 << WDRF); + wdt_disable(); + +// This is compled into 'icall', address should be in word unit, not byte. +# ifdef BOOTLOADER_SIZE + ((void (*)(void))((FLASH_SIZE - BOOTLOADER_SIZE) >> 1))(); +# else + asm("ijmp" ::"z"(bootloader_start)); +# endif + } +#endif +} diff --git a/platforms/avr/bootloader_size.c b/platforms/avr/bootloader_size.c new file mode 100644 index 0000000000..a029f9321f --- /dev/null +++ b/platforms/avr/bootloader_size.c @@ -0,0 +1,21 @@ +// Copyright 2017 Jack Humbert +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include + +// clang-format off +// this is not valid C - it's for computing the size available on the chip +AVR_SIZE: FLASHEND + 1 - BOOTLOADER_SIZE diff --git a/platforms/avr/gpio.h b/platforms/avr/gpio.h new file mode 100644 index 0000000000..e9be68491d --- /dev/null +++ b/platforms/avr/gpio.h @@ -0,0 +1,49 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include "pin_defs.h" + +typedef uint8_t pin_t; + +/* Operation of GPIO by pin. */ + +#define setPinInput(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF)) +#define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF)) +#define setPinInputLow(pin) _Static_assert(0, "AVR processors cannot implement an input as pull low") +#define setPinOutput(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF)) + +#define writePinHigh(pin) (PORTx_ADDRESS(pin) |= _BV((pin)&0xF)) +#define writePinLow(pin) (PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF)) +#define writePin(pin, level) ((level) ? writePinHigh(pin) : writePinLow(pin)) + +#define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF))) + +#define togglePin(pin) (PORTx_ADDRESS(pin) ^= _BV((pin)&0xF)) + +/* Operation of GPIO by port. */ + +typedef uint8_t port_data_t; + +#define readPort(port) PINx_ADDRESS(port) + +#define setPortBitInput(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) &= ~_BV((bit)&0xF)) +#define setPortBitInputHigh(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) |= _BV((bit)&0xF)) +#define setPortBitOutput(port, bit) (DDRx_ADDRESS(port) |= _BV((bit)&0xF)) + +#define writePortBitLow(port, bit) (PORTx_ADDRESS(port) &= ~_BV((bit)&0xF)) +#define writePortBitHigh(port, bit) (PORTx_ADDRESS(port) |= _BV((bit)&0xF)) diff --git a/platforms/avr/pin_defs.h b/platforms/avr/pin_defs.h new file mode 100644 index 0000000000..23d948041d --- /dev/null +++ b/platforms/avr/pin_defs.h @@ -0,0 +1,128 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +#define PORT_SHIFTER 4 // this may be 4 for all AVR chips + +// If you want to add more to this list, reference the PINx definitions in these header +// files: https://github.com/vancegroup-mirrors/avr-libc/tree/master/avr-libc/include/avr + +#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) +# define ADDRESS_BASE 0x00 +# define PINB_ADDRESS 0x3 +# define PINC_ADDRESS 0x6 +# define PIND_ADDRESS 0x9 +# define PINE_ADDRESS 0xC +# define PINF_ADDRESS 0xF +#elif defined(__AVR_AT90USB162__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) +# define ADDRESS_BASE 0x00 +# define PINB_ADDRESS 0x3 +# define PINC_ADDRESS 0x6 +# define PIND_ADDRESS 0x9 +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) +# define ADDRESS_BASE 0x00 +# define PINA_ADDRESS 0x0 +# define PINB_ADDRESS 0x3 +# define PINC_ADDRESS 0x6 +# define PIND_ADDRESS 0x9 +# define PINE_ADDRESS 0xC +# define PINF_ADDRESS 0xF +#elif defined(__AVR_ATmega32A__) +# define ADDRESS_BASE 0x10 +# define PIND_ADDRESS 0x0 +# define PINC_ADDRESS 0x3 +# define PINB_ADDRESS 0x6 +# define PINA_ADDRESS 0x9 +#elif defined(__AVR_ATtiny85__) +# define ADDRESS_BASE 0x10 +# define PINB_ADDRESS 0x6 +#else +# error "Pins are not defined" +#endif + +#define PINDEF(port, pin) ((PIN##port##_ADDRESS << PORT_SHIFTER) | pin) + +#define _PIN_ADDRESS(p, offset) _SFR_IO8(ADDRESS_BASE + ((p) >> PORT_SHIFTER) + (offset)) +// Port X Input Pins Address +#define PINx_ADDRESS(p) _PIN_ADDRESS(p, 0) +// Port X Data Direction Register, 0:input 1:output +#define DDRx_ADDRESS(p) _PIN_ADDRESS(p, 1) +// Port X Data Register +#define PORTx_ADDRESS(p) _PIN_ADDRESS(p, 2) + +/* I/O pins */ +#ifdef PORTA +# define A0 PINDEF(A, 0) +# define A1 PINDEF(A, 1) +# define A2 PINDEF(A, 2) +# define A3 PINDEF(A, 3) +# define A4 PINDEF(A, 4) +# define A5 PINDEF(A, 5) +# define A6 PINDEF(A, 6) +# define A7 PINDEF(A, 7) +#endif +#ifdef PORTB +# define B0 PINDEF(B, 0) +# define B1 PINDEF(B, 1) +# define B2 PINDEF(B, 2) +# define B3 PINDEF(B, 3) +# define B4 PINDEF(B, 4) +# define B5 PINDEF(B, 5) +# define B6 PINDEF(B, 6) +# define B7 PINDEF(B, 7) +#endif +#ifdef PORTC +# define C0 PINDEF(C, 0) +# define C1 PINDEF(C, 1) +# define C2 PINDEF(C, 2) +# define C3 PINDEF(C, 3) +# define C4 PINDEF(C, 4) +# define C5 PINDEF(C, 5) +# define C6 PINDEF(C, 6) +# define C7 PINDEF(C, 7) +#endif +#ifdef PORTD +# define D0 PINDEF(D, 0) +# define D1 PINDEF(D, 1) +# define D2 PINDEF(D, 2) +# define D3 PINDEF(D, 3) +# define D4 PINDEF(D, 4) +# define D5 PINDEF(D, 5) +# define D6 PINDEF(D, 6) +# define D7 PINDEF(D, 7) +#endif +#ifdef PORTE +# define E0 PINDEF(E, 0) +# define E1 PINDEF(E, 1) +# define E2 PINDEF(E, 2) +# define E3 PINDEF(E, 3) +# define E4 PINDEF(E, 4) +# define E5 PINDEF(E, 5) +# define E6 PINDEF(E, 6) +# define E7 PINDEF(E, 7) +#endif +#ifdef PORTF +# define F0 PINDEF(F, 0) +# define F1 PINDEF(F, 1) +# define F2 PINDEF(F, 2) +# define F3 PINDEF(F, 3) +# define F4 PINDEF(F, 4) +# define F5 PINDEF(F, 5) +# define F6 PINDEF(F, 6) +# define F7 PINDEF(F, 7) +#endif diff --git a/platforms/avr/platform.c b/platforms/avr/platform.c new file mode 100644 index 0000000000..3e35b4fe4c --- /dev/null +++ b/platforms/avr/platform.c @@ -0,0 +1,21 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform_deps.h" + +void platform_setup(void) { + // do nothing +} diff --git a/platforms/avr/platform.mk b/platforms/avr/platform.mk new file mode 100644 index 0000000000..b45108736f --- /dev/null +++ b/platforms/avr/platform.mk @@ -0,0 +1,179 @@ +# Hey Emacs, this is a -*- makefile -*- +############################################################################## +# Compiler settings +# +CC = $(CC_PREFIX) avr-gcc +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size +AR = avr-ar +NM = avr-nm +HEX = $(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock -R .signature +EEP = $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT) +BIN = + +COMPILEFLAGS += -funsigned-char +COMPILEFLAGS += -funsigned-bitfields +COMPILEFLAGS += -ffunction-sections +COMPILEFLAGS += -fdata-sections +COMPILEFLAGS += -fpack-struct +COMPILEFLAGS += -fshort-enums + +ASFLAGS += $(AVR_ASFLAGS) + +CFLAGS += $(COMPILEFLAGS) $(AVR_CFLAGS) +CFLAGS += -fno-inline-small-functions +CFLAGS += -fno-strict-aliasing + +CXXFLAGS += $(COMPILEFLAGS) +CXXFLAGS += -fno-exceptions -std=c++11 + +LDFLAGS +=-Wl,--gc-sections + +OPT_DEFS += -DF_CPU=$(F_CPU)UL + +MCUFLAGS = -mmcu=$(MCU) + +# List any extra directories to look for libraries here. +# Each directory must be seperated by a space. +# Use forward slashes for directory separators. +# For a directory that has spaces, enclose it in quotes. +EXTRALIBDIRS = + + +#---------------- External Memory Options ---------------- + +# 64 KB of external RAM, starting after internal RAM (ATmega128!), +# used for variables (.data/.bss) and heap (malloc()). +#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff + +# 64 KB of external RAM, starting after internal RAM (ATmega128!), +# only used for heap (malloc()). +#EXTMEMOPTS = -Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff + +EXTMEMOPTS = + +#---------------- Debugging Options ---------------- + +# Debugging format. +# Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs. +# AVR Studio 4.10 requires dwarf-2. +# AVR [Extended] COFF format requires stabs, plus an avr-objcopy run. +DEBUG = dwarf-2 + +# For simulavr only - target MCU frequency. +DEBUG_MFREQ = $(F_CPU) + +# Set the DEBUG_UI to either gdb or insight. +# DEBUG_UI = gdb +DEBUG_UI = insight + +# Set the debugging back-end to either avarice, simulavr. +DEBUG_BACKEND = avarice +#DEBUG_BACKEND = simulavr + +# GDB Init Filename. +GDBINIT_FILE = __avr_gdbinit + +# When using avarice settings for the JTAG +JTAG_DEV = /dev/com1 + +# Debugging port used to communicate between GDB / avarice / simulavr. +DEBUG_PORT = 4242 + +# Debugging host used to communicate between GDB / avarice / simulavr, normally +# just set to localhost unless doing some sort of crazy debugging when +# avarice is running on a different computer. +DEBUG_HOST = localhost + +#============================================================================ + +# Convert hex to bin. +bin: $(BUILD_DIR)/$(TARGET).hex +ifeq ($(BOOTLOADER),lufa-ms) + $(eval BIN_PADDING=$(shell n=`expr 32768 - $(BOOTLOADER_SIZE)` && echo $$(($$n)) || echo 0)) + $(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin --pad-to $(BIN_PADDING) +else + $(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin +endif + $(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin; + +# copy bin to FLASH.bin +flashbin: bin + $(COPY) $(BUILD_DIR)/$(TARGET).bin FLASH.bin; + +# Generate avr-gdb config/init file which does the following: +# define the reset signal, load the target file, connect to target, and set +# a breakpoint at main(). +gdb-config: + @$(REMOVE) $(GDBINIT_FILE) + @echo define reset >> $(GDBINIT_FILE) + @echo SIGNAL SIGHUP >> $(GDBINIT_FILE) + @echo end >> $(GDBINIT_FILE) + @echo file $(BUILD_DIR)/$(TARGET).elf >> $(GDBINIT_FILE) + @echo target remote $(DEBUG_HOST):$(DEBUG_PORT) >> $(GDBINIT_FILE) +ifeq ($(DEBUG_BACKEND),simulavr) + @echo load >> $(GDBINIT_FILE) +endif + @echo break main >> $(GDBINIT_FILE) + +debug: gdb-config $(BUILD_DIR)/$(TARGET).elf +ifeq ($(DEBUG_BACKEND), avarice) + @echo Starting AVaRICE - Press enter when "waiting to connect" message displays. + @$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \ + $(BUILD_DIR)/$(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT) + @$(WINSHELL) /c pause + +else + @$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \ + $(DEBUG_MFREQ) --port $(DEBUG_PORT) +endif + @$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE) + + + + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT = $(OBJCOPY) --debugging +COFFCONVERT += --change-section-address .data-0x800000 +COFFCONVERT += --change-section-address .bss-0x800000 +COFFCONVERT += --change-section-address .noinit-0x800000 +COFFCONVERT += --change-section-address .eeprom-0x810000 + + + +coff: $(BUILD_DIR)/$(TARGET).elf + @$(SECHO) $(MSG_COFF) $(BUILD_DIR)/$(TARGET).cof + $(COFFCONVERT) -O coff-avr $< $(BUILD_DIR)/$(TARGET).cof + + +extcoff: $(BUILD_DIR)/$(TARGET).elf + @$(SECHO) $(MSG_EXTENDED_COFF) $(BUILD_DIR)/$(TARGET).cof + $(COFFCONVERT) -O coff-ext-avr $< $(BUILD_DIR)/$(TARGET).cof + +ifeq ($(strip $(BOOTLOADER)), qmk-dfu) +QMK_BOOTLOADER_TYPE = DFU +else ifeq ($(strip $(BOOTLOADER)), qmk-hid) +QMK_BOOTLOADER_TYPE = HID +endif + +bootloader: +ifeq ($(strip $(QMK_BOOTLOADER_TYPE)),) + $(error Please set BOOTLOADER to "qmk-dfu" or "qmk-hid" first!) +else + make -C lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/ clean + $(QMK_BIN) generate-dfu-header --quiet --keyboard $(KEYBOARD) --output lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/Keyboard.h + $(eval MAX_SIZE=$(shell n=`$(CC) -E -mmcu=$(MCU) -D__ASSEMBLER__ $(CFLAGS) $(OPT_DEFS) platforms/avr/bootloader_size.c 2> /dev/null | sed -ne 's/\r//;/^#/n;/^AVR_SIZE:/,$${s/^AVR_SIZE: //;p;}'` && echo $$(($$n)) || echo 0)) + $(eval PROGRAM_SIZE_KB=$(shell n=`expr $(MAX_SIZE) / 1024` && echo $$(($$n)) || echo 0)) + $(eval BOOT_SECTION_SIZE_KB=$(shell n=`expr $(BOOTLOADER_SIZE) / 1024` && echo $$(($$n)) || echo 0)) + $(eval FLASH_SIZE_KB=$(shell n=`expr $(PROGRAM_SIZE_KB) + $(BOOT_SECTION_SIZE_KB)` && echo $$(($$n)) || echo 0)) + make -C lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/ MCU=$(MCU) ARCH=$(ARCH) F_CPU=$(F_CPU) FLASH_SIZE_KB=$(FLASH_SIZE_KB) BOOT_SECTION_SIZE_KB=$(BOOT_SECTION_SIZE_KB) + printf "Bootloader$(QMK_BOOTLOADER_TYPE).hex copied to $(TARGET)_bootloader.hex\n" + cp lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/Bootloader$(QMK_BOOTLOADER_TYPE).hex $(TARGET)_bootloader.hex +endif + +production: $(BUILD_DIR)/$(TARGET).hex bootloader cpfirmware + @cat $(BUILD_DIR)/$(TARGET).hex | awk '/^:00000001FF/ == 0' > $(TARGET)_production.hex + @cat $(TARGET)_bootloader.hex >> $(TARGET)_production.hex + echo "File sizes:" + $(SIZE) $(TARGET).hex $(TARGET)_bootloader.hex $(TARGET)_production.hex diff --git a/platforms/avr/platform_deps.h b/platforms/avr/platform_deps.h new file mode 100644 index 0000000000..45d9dcebfa --- /dev/null +++ b/platforms/avr/platform_deps.h @@ -0,0 +1,20 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include diff --git a/platforms/avr/printf.c b/platforms/avr/printf.c new file mode 100644 index 0000000000..9ad7a38693 --- /dev/null +++ b/platforms/avr/printf.c @@ -0,0 +1,20 @@ +/* +Copyright 2011 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "xprintf.h" +#include "sendchar.h" + +void print_set_sendchar(sendchar_func_t func) { xdev_out(func); } diff --git a/platforms/avr/printf.mk b/platforms/avr/printf.mk new file mode 100644 index 0000000000..060ad88c57 --- /dev/null +++ b/platforms/avr/printf.mk @@ -0,0 +1,2 @@ +TMK_COMMON_SRC += $(PLATFORM_COMMON_DIR)/xprintf.S +TMK_COMMON_SRC += $(PLATFORM_COMMON_DIR)/printf.c diff --git a/platforms/avr/sleep_led.c b/platforms/avr/sleep_led.c new file mode 100644 index 0000000000..9a3b52abe5 --- /dev/null +++ b/platforms/avr/sleep_led.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include "led.h" +#include "sleep_led.h" + +#ifndef SLEEP_LED_TIMER +# define SLEEP_LED_TIMER 1 +#endif + +#if SLEEP_LED_TIMER == 1 +# define TCCRxB TCCR1B +# define TIMERx_COMPA_vect TIMER1_COMPA_vect +# if defined(__AVR_ATmega32A__) // This MCU has only one TIMSK register +# define TIMSKx TIMSK +# else +# define TIMSKx TIMSK1 +# endif +# define OCIExA OCIE1A +# define OCRxx OCR1A +#elif SLEEP_LED_TIMER == 3 +# define TCCRxB TCCR3B +# define TIMERx_COMPA_vect TIMER3_COMPA_vect +# define TIMSKx TIMSK3 +# define OCIExA OCIE3A +# define OCRxx OCR3A +#else +error("Invalid SLEEP_LED_TIMER config") +#endif + +/* Software PWM + * ______ ______ __ + * | ON |___OFF___| ON |___OFF___| .... + * |<-------------->|<-------------->|<- .... + * PWM period PWM period + * + * 256 interrupts/period[resolution] + * 64 periods/second[frequency] + * 256*64 interrupts/second + * F_CPU/(256*64) clocks/interrupt + */ +#define SLEEP_LED_TIMER_TOP F_CPU / (256 * 64) + +/** \brief Sleep LED initialization + * + * FIXME: needs doc + */ +void sleep_led_init(void) { + /* Timer1 setup */ + /* CTC mode */ + TCCRxB |= _BV(WGM12); + /* Clock selelct: clk/1 */ + TCCRxB |= _BV(CS10); + /* Set TOP value */ + uint8_t sreg = SREG; + cli(); + OCRxx = SLEEP_LED_TIMER_TOP; + SREG = sreg; +} + +/** \brief Sleep LED enable + * + * FIXME: needs doc + */ +void sleep_led_enable(void) { + /* Enable Compare Match Interrupt */ + TIMSKx |= _BV(OCIExA); +} + +/** \brief Sleep LED disable + * + * FIXME: needs doc + */ +void sleep_led_disable(void) { + /* Disable Compare Match Interrupt */ + TIMSKx &= ~_BV(OCIExA); +} + +/** \brief Sleep LED toggle + * + * FIXME: needs doc + */ +void sleep_led_toggle(void) { + /* Disable Compare Match Interrupt */ + TIMSKx ^= _BV(OCIExA); +} + +/** \brief Breathing Sleep LED brighness(PWM On period) table + * + * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle + * + * https://www.wolframalpha.com/input/?i=sin%28x%2F64*pi%29**8+*+255%2C+x%3D0+to+63 + * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i } + */ +static const uint8_t breathing_table[64] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, 15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, 255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +ISR(TIMERx_COMPA_vect) { + /* Software PWM + * timer:1111 1111 1111 1111 + * \_____/\/ \_______/____ count(0-255) + * \ \______________ duration of step(4) + * \__________________ index of step table(0-63) + */ + static union { + uint16_t row; + struct { + uint8_t count : 8; + uint8_t duration : 2; + uint8_t index : 6; + } pwm; + } timer = {.row = 0}; + + timer.row++; + + // LED on + if (timer.pwm.count == 0) { + led_set(1 << USB_LED_CAPS_LOCK); + } + // LED off + if (timer.pwm.count == pgm_read_byte(&breathing_table[timer.pwm.index])) { + led_set(0); + } +} diff --git a/platforms/avr/suspend.c b/platforms/avr/suspend.c new file mode 100644 index 0000000000..b614746e6c --- /dev/null +++ b/platforms/avr/suspend.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include "matrix.h" +#include "action.h" +#include "suspend.h" +#include "timer.h" +#include "led.h" +#include "host.h" + +#ifdef PROTOCOL_LUFA +# include "lufa.h" +#endif +#ifdef PROTOCOL_VUSB +# include "vusb.h" +#endif + +/** \brief Suspend idle + * + * FIXME: needs doc + */ +void suspend_idle(uint8_t time) { + cli(); + set_sleep_mode(SLEEP_MODE_IDLE); + sleep_enable(); + sei(); + sleep_cpu(); + sleep_disable(); +} + +// TODO: This needs some cleanup + +#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect) + +// clang-format off +#define wdt_intr_enable(value) \ +__asm__ __volatile__ ( \ + "in __tmp_reg__,__SREG__" "\n\t" \ + "cli" "\n\t" \ + "wdr" "\n\t" \ + "sts %0,%1" "\n\t" \ + "out __SREG__,__tmp_reg__" "\n\t" \ + "sts %0,%2" "\n\t" \ + : /* no outputs */ \ + : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \ + "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \ + "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | _BV(WDIE) | (value & 0x07))) \ + : "r0" \ +) +// clang-format on + +/** \brief Power down MCU with watchdog timer + * + * wdto: watchdog timer timeout defined in + * WDTO_15MS + * WDTO_30MS + * WDTO_60MS + * WDTO_120MS + * WDTO_250MS + * WDTO_500MS + * WDTO_1S + * WDTO_2S + * WDTO_4S + * WDTO_8S + */ +static uint8_t wdt_timeout = 0; + +/** \brief Power down + * + * FIXME: needs doc + */ +static void power_down(uint8_t wdto) { + wdt_timeout = wdto; + + // Watchdog Interrupt Mode + wdt_intr_enable(wdto); + + // TODO: more power saving + // See PicoPower application note + // - I/O port input with pullup + // - prescale clock + // - BOD disable + // - Power Reduction Register PRR + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_enable(); + sei(); + sleep_cpu(); + sleep_disable(); + + // Disable watchdog after sleep + wdt_disable(); +} +#endif + +/** \brief Suspend power down + * + * FIXME: needs doc + */ +void suspend_power_down(void) { +#ifdef PROTOCOL_LUFA + if (USB_DeviceState == DEVICE_STATE_Configured) return; +#endif +#ifdef PROTOCOL_VUSB + if (!vusb_suspended) return; +#endif + + suspend_power_down_quantum(); + +#ifndef NO_SUSPEND_POWER_DOWN + // Enter sleep state if possible (ie, the MCU has a watchdog timeout interrupt) +# if defined(WDT_vect) + power_down(WDTO_15MS); +# endif +#endif +} + +__attribute__((weak)) void matrix_power_up(void) {} +__attribute__((weak)) void matrix_power_down(void) {} +bool suspend_wakeup_condition(void) { + matrix_power_up(); + matrix_scan(); + matrix_power_down(); + for (uint8_t r = 0; r < MATRIX_ROWS; r++) { + if (matrix_get_row(r)) return true; + } + return false; +} + +/** \brief run immediately after wakeup + * + * FIXME: needs doc + */ +void suspend_wakeup_init(void) { + // clear keyboard state + clear_keyboard(); + + suspend_wakeup_init_quantum(); +} + +#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect) +/* watchdog timeout */ +ISR(WDT_vect) { + // compensate timer for sleep + switch (wdt_timeout) { + case WDTO_15MS: + timer_count += 15 + 2; // WDTO_15MS + 2(from observation) + break; + default:; + } +} +#endif diff --git a/platforms/avr/timer.c b/platforms/avr/timer.c new file mode 100644 index 0000000000..c2e6c6e081 --- /dev/null +++ b/platforms/avr/timer.c @@ -0,0 +1,133 @@ +/* +Copyright 2011 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include "timer_avr.h" +#include "timer.h" + +// counter resolution 1ms +// NOTE: union { uint32_t timer32; struct { uint16_t dummy; uint16_t timer16; }} +volatile uint32_t timer_count; + +/** \brief timer initialization + * + * FIXME: needs doc + */ +void timer_init(void) { +#if TIMER_PRESCALER == 1 + uint8_t prescaler = _BV(CS00); +#elif TIMER_PRESCALER == 8 + uint8_t prescaler = _BV(CS01); +#elif TIMER_PRESCALER == 64 + uint8_t prescaler = _BV(CS00) | _BV(CS01); +#elif TIMER_PRESCALER == 256 + uint8_t prescaler = _BV(CS02); +#elif TIMER_PRESCALER == 1024 + uint8_t prescaler = _BV(CS00) | _BV(CS02); +#else +# error "Timer prescaler value is not valid" +#endif + +#if defined(__AVR_ATmega32A__) + // Timer0 CTC mode + TCCR0 = _BV(WGM01) | prescaler; + + OCR0 = TIMER_RAW_TOP; + TIMSK = _BV(OCIE0); +#elif defined(__AVR_ATtiny85__) + // Timer0 CTC mode + TCCR0A = _BV(WGM01); + TCCR0B = prescaler; + + OCR0A = TIMER_RAW_TOP; + TIMSK = _BV(OCIE0A); +#else + // Timer0 CTC mode + TCCR0A = _BV(WGM01); + TCCR0B = prescaler; + + OCR0A = TIMER_RAW_TOP; + TIMSK0 = _BV(OCIE0A); +#endif +} + +/** \brief timer clear + * + * FIXME: needs doc + */ +inline void timer_clear(void) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { timer_count = 0; } +} + +/** \brief timer read + * + * FIXME: needs doc + */ +inline uint16_t timer_read(void) { + uint32_t t; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { t = timer_count; } + + return (t & 0xFFFF); +} + +/** \brief timer read32 + * + * FIXME: needs doc + */ +inline uint32_t timer_read32(void) { + uint32_t t; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { t = timer_count; } + + return t; +} + +/** \brief timer elapsed + * + * FIXME: needs doc + */ +inline uint16_t timer_elapsed(uint16_t last) { + uint32_t t; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { t = timer_count; } + + return TIMER_DIFF_16((t & 0xFFFF), last); +} + +/** \brief timer elapsed32 + * + * FIXME: needs doc + */ +inline uint32_t timer_elapsed32(uint32_t last) { + uint32_t t; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { t = timer_count; } + + return TIMER_DIFF_32(t, last); +} + +// excecuted once per 1ms.(excess for just timer count?) +#ifndef __AVR_ATmega32A__ +# define TIMER_INTERRUPT_VECTOR TIMER0_COMPA_vect +#else +# define TIMER_INTERRUPT_VECTOR TIMER0_COMP_vect +#endif +ISR(TIMER_INTERRUPT_VECTOR, ISR_NOBLOCK) { timer_count++; } diff --git a/platforms/avr/timer_avr.h b/platforms/avr/timer_avr.h new file mode 100644 index 0000000000..c1b726bd01 --- /dev/null +++ b/platforms/avr/timer_avr.h @@ -0,0 +1,39 @@ +/* +Copyright 2011 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include + +#ifndef TIMER_PRESCALER +# if F_CPU > 16000000 +# define TIMER_PRESCALER 256 +# elif F_CPU > 2000000 +# define TIMER_PRESCALER 64 +# elif F_CPU > 250000 +# define TIMER_PRESCALER 8 +# else +# define TIMER_PRESCALER 1 +# endif +#endif +#define TIMER_RAW_FREQ (F_CPU / TIMER_PRESCALER) +#define TIMER_RAW TCNT0 +#define TIMER_RAW_TOP (TIMER_RAW_FREQ / 1000) + +#if (TIMER_RAW_TOP > 255) +# error "Timer0 can't count 1ms at this clock freq. Use larger prescaler." +#endif diff --git a/platforms/avr/xprintf.S b/platforms/avr/xprintf.S new file mode 100644 index 0000000000..c5a414c35c --- /dev/null +++ b/platforms/avr/xprintf.S @@ -0,0 +1,498 @@ +;---------------------------------------------------------------------------; +; Extended itoa, puts, printf and atoi (C)ChaN, 2011 +;---------------------------------------------------------------------------; + + // Base size is 152 bytes +#define CR_CRLF 0 // Convert \n to \r\n (+10 bytes) +#define USE_XPRINTF 1 // Enable xprintf function (+194 bytes) +#define USE_XSPRINTF 0 // Add xsprintf function (+78 bytes) +#define USE_XFPRINTF 0 // Add xfprintf function (+54 bytes) +#define USE_XATOI 0 // Enable xatoi function (+182 bytes) + + +#if FLASHEND > 0x1FFFF +#error xitoa module does not support 256K devices +#endif + +.nolist +#include // Include device specific definitions. +.list + +#ifdef SPM_PAGESIZE // Recent devices have "lpm Rd,Z+" and "movw". +.macro _LPMI reg + lpm \reg, Z+ +.endm +.macro _MOVW dh,dl, sh,sl + movw \dl, \sl +.endm +#else // Earlier devices do not have "lpm Rd,Z+" nor "movw". +.macro _LPMI reg + lpm + mov \reg, r0 + adiw ZL, 1 +.endm +.macro _MOVW dh,dl, sh,sl + mov \dl, \sl + mov \dh, \sh +.endm +#endif + + + +;--------------------------------------------------------------------------- +; Stub function to forward to user output function +; +;Prototype: void xputc (char chr // a character to be output +; ); +;Size: 12/12 words + +.section .bss +.global xfunc_out ; xfunc_out must be initialized before using this module. +xfunc_out: .ds.w 1 +.section .text + + +.func xputc +.global xputc +xputc: +#if CR_CRLF + cpi r24, 10 ;LF --> CRLF + brne 1f ; + ldi r24, 13 ; + rcall 1f ; + ldi r24, 10 ;/ +1: +#endif + push ZH + push ZL + lds ZL, xfunc_out+0 ;Pointer to the registered output function. + lds ZH, xfunc_out+1 ;/ + sbiw ZL, 0 ;Skip if null + breq 2f ;/ + icall +2: pop ZL + pop ZH + ret +.endfunc + + + +;--------------------------------------------------------------------------- +; Direct ROM string output +; +;Prototype: void xputs (const char *str_p // rom string to be output +; ); + +.func xputs +.global xputs +xputs: + _MOVW ZH,ZL, r25,r24 ; Z = pointer to rom string +1: _LPMI r24 + cpi r24, 0 + breq 2f + rcall xputc + rjmp 1b +2: ret +.endfunc + + +;--------------------------------------------------------------------------- +; Extended direct numeral string output (32bit version) +; +;Prototype: void xitoa (long value, // value to be output +; char radix, // radix +; char width); // minimum width +; + +.func xitoa +.global xitoa +xitoa: + ;r25:r22 = value, r20 = base, r18 = digits + clr r31 ;r31 = stack level + ldi r30, ' ' ;r30 = sign + ldi r19, ' ' ;r19 = filler + sbrs r20, 7 ;When base indicates signd format and the value + rjmp 0f ;is minus, add a '-'. + neg r20 ; + sbrs r25, 7 ; + rjmp 0f ; + ldi r30, '-' ; + com r22 ; + com r23 ; + com r24 ; + com r25 ; + adc r22, r1 ; + adc r23, r1 ; + adc r24, r1 ; + adc r25, r1 ;/ +0: sbrs r18, 7 ;When digits indicates zero filled, + rjmp 1f ;filler is '0'. + neg r18 ; + ldi r19, '0' ;/ + ;----- string conversion loop +1: ldi r21, 32 ;r26 = r25:r22 % r20 + clr r26 ;r25:r22 /= r20 +2: lsl r22 ; + rol r23 ; + rol r24 ; + rol r25 ; + rol r26 ; + cp r26, r20 ; + brcs 3f ; + sub r26, r20 ; + inc r22 ; +3: dec r21 ; + brne 2b ;/ + cpi r26, 10 ;r26 is a numeral digit '0'-'F' + brcs 4f ; + subi r26, -7 ; +4: subi r26, -'0' ;/ + push r26 ;Stack it + inc r31 ;/ + cp r22, r1 ;Repeat until r25:r22 gets zero + cpc r23, r1 ; + cpc r24, r1 ; + cpc r25, r1 ; + brne 1b ;/ + + cpi r30, '-' ;Minus sign if needed + brne 5f ; + push r30 ; + inc r31 ;/ +5: cp r31, r18 ;Filler + brcc 6f ; + push r19 ; + inc r31 ; + rjmp 5b ;/ + +6: pop r24 ;Flush stacked digits and exit + rcall xputc ; + dec r31 ; + brne 6b ;/ + + ret +.endfunc + + + +;---------------------------------------------------------------------------; +; Formatted string output (16/32bit version) +; +;Prototype: +; void __xprintf (const char *format_p, ...); +; void __xsprintf(char*, const char *format_p, ...); +; void __xfprintf(void(*func)(char), const char *format_p, ...); +; + +#if USE_XPRINTF + +.func xvprintf +xvprintf: + ld ZL, Y+ ;Z = pointer to format string + ld ZH, Y+ ;/ + +0: _LPMI r24 ;Get a format char + cpi r24, 0 ;End of format string? + breq 90f ;/ + cpi r24, '%' ;Is format? + breq 20f ;/ +1: rcall xputc ;Put a normal character + rjmp 0b ;/ +90: ret + +20: ldi r18, 0 ;r18: digits + clt ;T: filler + _LPMI r21 ;Get flags + cpi r21, '%' ;Is a %? + breq 1b ;/ + cpi r21, '0' ;Zero filled? + brne 23f ; + set ;/ +22: _LPMI r21 ;Get width +23: cpi r21, '9'+1 ; + brcc 24f ; + subi r21, '0' ; + brcs 90b ; + lsl r18 ; + mov r0, r18 ; + lsl r18 ; + lsl r18 ; + add r18, r0 ; + add r18, r21 ; + rjmp 22b ;/ + +24: brtc 25f ;get value (low word) + neg r18 ; +25: ld r24, Y+ ; + ld r25, Y+ ;/ + cpi r21, 'c' ;Is type character? + breq 1b ;/ + cpi r21, 's' ;Is type RAM string? + breq 50f ;/ + cpi r21, 'S' ;Is type ROM string? + breq 60f ;/ + _MOVW r23,r22,r25,r24 ;r25:r22 = value + clr r24 ; + clr r25 ; + clt ;/ + cpi r21, 'l' ;Is long int? + brne 26f ; + ld r24, Y+ ;get value (high word) + ld r25, Y+ ; + set ; + _LPMI r21 ;/ +26: cpi r21, 'd' ;Is type signed decimal? + brne 27f ;/ + ldi r20, -10 ; + brts 40f ; + sbrs r23, 7 ; + rjmp 40f ; + ldi r24, -1 ; + ldi r25, -1 ; + rjmp 40f ;/ +27: cpi r21, 'u' ;Is type unsigned decimal? + ldi r20, 10 ; + breq 40f ;/ + cpi r21, 'X' ;Is type hexdecimal? + ldi r20, 16 ; + breq 40f ;/ + cpi r21, 'b' ;Is type binary? + ldi r20, 2 ; + breq 40f ;/ + ret ;abort +40: push ZH ;Output the value + push ZL ; + rcall xitoa ; +42: pop ZL ; + pop ZH ; + rjmp 0b ;/ + +50: push ZH ;Put a string on the RAM + push ZL + _MOVW ZH,ZL, r25,r24 +51: ld r24, Z+ + cpi r24, 0 + breq 42b + rcall xputc + rjmp 51b + +60: push ZH ;Put a string on the ROM + push ZL + rcall xputs + rjmp 42b +.endfunc + + +.func __xprintf +.global __xprintf +__xprintf: + push YH + push YL + in YL, _SFR_IO_ADDR(SPL) +#ifdef SPH + in YH, _SFR_IO_ADDR(SPH) +#else + clr YH +#endif + adiw YL, 5 ;Y = pointer to arguments + rcall xvprintf + pop YL + pop YH + ret +.endfunc + + +#if USE_XSPRINTF + +.func __xsprintf +putram: + _MOVW ZH,ZL, r15,r14 + st Z+, r24 + _MOVW r15,r14, ZH,ZL + ret +.global __xsprintf +__xsprintf: + push YH + push YL + in YL, _SFR_IO_ADDR(SPL) +#ifdef SPH + in YH, _SFR_IO_ADDR(SPH) +#else + clr YH +#endif + adiw YL, 5 ;Y = pointer to arguments + lds ZL, xfunc_out+0 ;Save registered output function + lds ZH, xfunc_out+1 ; + push ZL ; + push ZH ;/ + ldi ZL, lo8(pm(putram));Set local output function + ldi ZH, hi8(pm(putram)); + sts xfunc_out+0, ZL ; + sts xfunc_out+1, ZH ;/ + push r15 ;Initialize pointer to string buffer + push r14 ; + ld r14, Y+ ; + ld r15, Y+ ;/ + rcall xvprintf + _MOVW ZH,ZL, r15,r14 ;Terminate string + st Z, r1 ; + pop r14 ; + pop r15 ;/ + pop ZH ;Restore registered output function + pop ZL ; + sts xfunc_out+0, ZL ; + sts xfunc_out+1, ZH ;/ + pop YL + pop YH + ret +.endfunc +#endif + + +#if USE_XFPRINTF +.func __xfprintf +.global __xfprintf +__xfprintf: + push YH + push YL + in YL, _SFR_IO_ADDR(SPL) +#ifdef SPH + in YH, _SFR_IO_ADDR(SPH) +#else + clr YH +#endif + adiw YL, 5 ;Y = pointer to arguments + lds ZL, xfunc_out+0 ;Save registered output function + lds ZH, xfunc_out+1 ; + push ZL ; + push ZH ;/ + ld ZL, Y+ ;Set output function + ld ZH, Y+ ; + sts xfunc_out+0, ZL ; + sts xfunc_out+1, ZH ;/ + rcall xvprintf + pop ZH ;Restore registered output function + pop ZL ; + sts xfunc_out+0, ZL ; + sts xfunc_out+1, ZH ;/ + pop YL + pop YH + ret +.endfunc +#endif + +#endif + + + +;--------------------------------------------------------------------------- +; Extended numeral string input +; +;Prototype: +; char xatoi ( /* 1: Successful, 0: Failed */ +; const char **str, /* pointer to pointer to source string */ +; long *res /* result */ +; ); +; + + +#if USE_XATOI +.func xatoi +.global xatoi +xatoi: + _MOVW r1, r0, r23, r22 + _MOVW XH, XL, r25, r24 + ld ZL, X+ + ld ZH, X+ + clr r18 ;r21:r18 = 0; + clr r19 ; + clr r20 ; + clr r21 ;/ + clt ;T = 0; + + ldi r25, 10 ;r25 = 10; + rjmp 41f ;/ +40: adiw ZL, 1 ;Z++; +41: ld r22, Z ;r22 = *Z; + cpi r22, ' ' ;if(r22 == ' ') continue + breq 40b ;/ + brcs 70f ;if(r22 < ' ') error; + cpi r22, '-' ;if(r22 == '-') { + brne 42f ; T = 1; + set ; continue; + rjmp 40b ;} +42: cpi r22, '9'+1 ;if(r22 > '9') error; + brcc 70f ;/ + cpi r22, '0' ;if(r22 < '0') error; + brcs 70f ;/ + brne 51f ;if(r22 > '0') cv_start; + ldi r25, 8 ;r25 = 8; + adiw ZL, 1 ;r22 = *(++Z); + ld r22, Z ;/ + cpi r22, ' '+1 ;if(r22 <= ' ') exit; + brcs 80f ;/ + cpi r22, 'b' ;if(r22 == 'b') { + brne 43f ; r25 = 2; + ldi r25, 2 ; cv_start; + rjmp 50f ;} +43: cpi r22, 'x' ;if(r22 != 'x') error; + brne 51f ;/ + ldi r25, 16 ;r25 = 16; + +50: adiw ZL, 1 ;Z++; + ld r22, Z ;r22 = *Z; +51: cpi r22, ' '+1 ;if(r22 <= ' ') break; + brcs 80f ;/ + cpi r22, 'a' ;if(r22 >= 'a') r22 =- 0x20; + brcs 52f ; + subi r22, 0x20 ;/ +52: subi r22, '0' ;if((r22 -= '0') < 0) error; + brcs 70f ;/ + cpi r22, 10 ;if(r22 >= 10) { + brcs 53f ; r22 -= 7; + subi r22, 7 ; if(r22 < 10) + cpi r22, 10 ; + brcs 70f ;} +53: cp r22, r25 ;if(r22 >= r25) error; + brcc 70f ;/ +60: ldi r24, 33 ;r21:r18 *= r25; + sub r23, r23 ; +61: brcc 62f ; + add r23, r25 ; +62: lsr r23 ; + ror r21 ; + ror r20 ; + ror r19 ; + ror r18 ; + dec r24 ; + brne 61b ;/ + add r18, r22 ;r21:r18 += r22; + adc r19, r24 ; + adc r20, r24 ; + adc r21, r24 ;/ + rjmp 50b ;repeat + +70: ldi r24, 0 + rjmp 81f +80: ldi r24, 1 +81: brtc 82f + clr r22 + com r18 + com r19 + com r20 + com r21 + adc r18, r22 + adc r19, r22 + adc r20, r22 + adc r21, r22 +82: st -X, ZH + st -X, ZL + _MOVW XH, XL, r1, r0 + st X+, r18 + st X+, r19 + st X+, r20 + st X+, r21 + clr r1 + ret +.endfunc +#endif diff --git a/platforms/avr/xprintf.h b/platforms/avr/xprintf.h new file mode 100644 index 0000000000..80834f1714 --- /dev/null +++ b/platforms/avr/xprintf.h @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------- + Extended itoa, puts and printf (C)ChaN, 2011 +-----------------------------------------------------------------------------*/ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void (*xfunc_out)(uint8_t); +#define xdev_out(func) xfunc_out = (void (*)(uint8_t))(func) + +/* This is a pointer to user defined output function. It must be initialized + before using this modle. +*/ + +void xputc(char chr); + +/* This is a stub function to forward outputs to user defined output function. + All outputs from this module are output via this function. +*/ + +/*-----------------------------------------------------------------------------*/ +void xputs(const char *string_p); + +/* The string placed in the ROM is forwarded to xputc() directly. + */ + +/*-----------------------------------------------------------------------------*/ +void xitoa(long value, char radix, char width); + +/* Extended itoa(). + + value radix width output + 100 10 6 " 100" + 100 10 -6 "000100" + 100 10 0 "100" + 4294967295 10 0 "4294967295" + 4294967295 -10 0 "-1" + 655360 16 -8 "000A0000" + 1024 16 0 "400" + 0x55 2 -8 "01010101" +*/ + +/*-----------------------------------------------------------------------------*/ +#define xprintf(format, ...) __xprintf(PSTR(format), ##__VA_ARGS__) +#define xsprintf(str, format, ...) __xsprintf(str, PSTR(format), ##__VA_ARGS__) +#define xfprintf(func, format, ...) __xfprintf(func, PSTR(format), ##__VA_ARGS__) + +void __xprintf(const char *format_p, ...); /* Send formatted string to the registered device */ +// void __xsprintf(char*, const char *format_p, ...); /* Put formatted string to the memory */ +// void __xfprintf(void(*func)(uint8_t), const char *format_p, ...); /* Send formatted string to the specified device */ + +/* Format string is placed in the ROM. The format flags is similar to printf(). + + %[flag][width][size]type + + flag + A '0' means filled with '0' when output is shorter than width. + ' ' is used in default. This is effective only numeral type. + width + Minimum width in decimal number. This is effective only numeral type. + Default width is zero. + size + A 'l' means the argument is long(32bit). Default is short(16bit). + This is effective only numeral type. + type + 'c' : Character, argument is the value + 's' : String placed on the RAM, argument is the pointer + 'S' : String placed on the ROM, argument is the pointer + 'd' : Signed decimal, argument is the value + 'u' : Unsigned decimal, argument is the value + 'X' : Hexdecimal, argument is the value + 'b' : Binary, argument is the value + '%' : '%' + +*/ + +/*-----------------------------------------------------------------------------*/ +char xatoi(char **str, long *ret); + +/* Get value of the numeral string. + + str + Pointer to pointer to source string + + "0b11001010" binary + "0377" octal + "0xff800" hexdecimal + "1250000" decimal + "-25000" decimal + + ret + Pointer to return value +*/ + +#ifdef __cplusplus +} +#endif diff --git a/platforms/bootloader.h b/platforms/bootloader.h new file mode 100644 index 0000000000..25ebd95288 --- /dev/null +++ b/platforms/bootloader.h @@ -0,0 +1,21 @@ +/* +Copyright 2011 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +/* give code for your bootloader to come up if needed */ +void bootloader_jump(void); diff --git a/platforms/chibios/_timer.h b/platforms/chibios/_timer.h new file mode 100644 index 0000000000..77402b612a --- /dev/null +++ b/platforms/chibios/_timer.h @@ -0,0 +1,19 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// The platform is 32-bit, so prefer 32-bit timers to avoid overflow +#define FAST_TIMER_T_SIZE 32 diff --git a/platforms/chibios/_wait.c b/platforms/chibios/_wait.c new file mode 100644 index 0000000000..1fbea2dd5e --- /dev/null +++ b/platforms/chibios/_wait.c @@ -0,0 +1,89 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __OPTIMIZE__ +# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed" +#endif + +#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t" + +__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */ + /* The argument n must be a constant expression. + * That way, compiler optimization will remove unnecessary code. */ + if (n < 1) { + return; + } + if (n > 8) { + unsigned int n8 = n / 8; + n = n - n8 * 8; + switch (n8) { + case 16: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 15: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 14: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 13: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 12: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 11: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 10: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 9: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 8: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 7: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 6: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 5: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 4: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 3: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 2: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 1: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 0: + break; + } + } + switch (n) { + case 8: + asm volatile("nop" ::: "memory"); + case 7: + asm volatile("nop" ::: "memory"); + case 6: + asm volatile("nop" ::: "memory"); + case 5: + asm volatile("nop" ::: "memory"); + case 4: + asm volatile("nop" ::: "memory"); + case 3: + asm volatile("nop" ::: "memory"); + case 2: + asm volatile("nop" ::: "memory"); + case 1: + asm volatile("nop" ::: "memory"); + case 0: + break; + } +} diff --git a/platforms/chibios/_wait.h b/platforms/chibios/_wait.h new file mode 100644 index 0000000000..2f36c64a2e --- /dev/null +++ b/platforms/chibios/_wait.h @@ -0,0 +1,60 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +/* chThdSleepX of zero maps to infinite - so we map to a tiny delay to still yield */ +#define wait_ms(ms) \ + do { \ + if (ms != 0) { \ + chThdSleepMilliseconds(ms); \ + } else { \ + chThdSleepMicroseconds(1); \ + } \ + } while (0) + +#ifdef WAIT_US_TIMER +void wait_us(uint16_t duration); +#else +# define wait_us(us) \ + do { \ + if (us != 0) { \ + chThdSleepMicroseconds(us); \ + } else { \ + chThdSleepMicroseconds(1); \ + } \ + } while (0) +#endif + +#include "_wait.c" + +/* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus + * to which the GPIO is connected. + * The connected buses differ depending on the various series of MCUs. + * And since the instruction execution clock of the CPU and the bus clock of GPIO are different, + * there is a delay of several clocks to read the change of the input signal. + * + * Define this delay with the GPIO_INPUT_PIN_DELAY macro. + * If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used. + * (A fairly large value of 0.25 microseconds is set.) + */ +#ifndef GPIO_INPUT_PIN_DELAY +# define GPIO_INPUT_PIN_DELAY (CPU_CLOCK / 1000000L / 4) +#endif + +#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) diff --git a/platforms/chibios/atomic_util.h b/platforms/chibios/atomic_util.h new file mode 100644 index 0000000000..8975045153 --- /dev/null +++ b/platforms/chibios/atomic_util.h @@ -0,0 +1,37 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +static __inline__ uint8_t __interrupt_disable__(void) { + chSysLock(); + + return 1; +} + +static __inline__ void __interrupt_enable__(const uint8_t *__s) { + chSysUnlock(); + + __asm__ volatile("" ::: "memory"); + (void)__s; +} + +#define ATOMIC_BLOCK(type) for (type, __ToDo = __interrupt_disable__(); __ToDo; __ToDo = 0) +#define ATOMIC_FORCEON uint8_t sreg_save __attribute__((__cleanup__(__interrupt_enable__))) = 0 + +#define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE not implemented") +#define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON) diff --git a/platforms/chibios/bootloader.c b/platforms/chibios/bootloader.c new file mode 100644 index 0000000000..5cadadeeeb --- /dev/null +++ b/platforms/chibios/bootloader.c @@ -0,0 +1,145 @@ +#include "bootloader.h" + +#include +#include +#include "wait.h" + +/* This code should be checked whether it runs correctly on platforms */ +#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) +#define BOOTLOADER_MAGIC 0xDEADBEEF +#define MAGIC_ADDR (unsigned long *)(SYMVAL(__ram0_end__) - 4) + +#ifndef STM32_BOOTLOADER_DUAL_BANK +# define STM32_BOOTLOADER_DUAL_BANK FALSE +#endif + +#ifdef BOOTLOADER_TINYUF2 + +# define DBL_TAP_MAGIC 0xf01669ef // From tinyuf2's board_api.h + +// defined by linker script +extern uint32_t _board_dfu_dbl_tap[]; +# define DBL_TAP_REG _board_dfu_dbl_tap[0] + +void bootloader_jump(void) { + DBL_TAP_REG = DBL_TAP_MAGIC; + NVIC_SystemReset(); +} + +void enter_bootloader_mode_if_requested(void) { /* not needed, no two-stage reset */ +} + +#elif STM32_BOOTLOADER_DUAL_BANK + +// Need pin definitions +# include "config_common.h" + +# ifndef STM32_BOOTLOADER_DUAL_BANK_GPIO +# error "No STM32_BOOTLOADER_DUAL_BANK_GPIO defined, don't know which pin to toggle" +# endif + +# ifndef STM32_BOOTLOADER_DUAL_BANK_POLARITY +# define STM32_BOOTLOADER_DUAL_BANK_POLARITY 0 +# endif + +# ifndef STM32_BOOTLOADER_DUAL_BANK_DELAY +# define STM32_BOOTLOADER_DUAL_BANK_DELAY 100000 +# endif + +extern uint32_t __ram0_end__; + +__attribute__((weak)) void bootloader_jump(void) { + // For STM32 MCUs with dual-bank flash, and we're incapable of jumping to the bootloader. The first valid flash + // bank is executed unconditionally after a reset, so it doesn't enter DFU unless BOOT0 is high. Instead, we do + // it with hardware...in this case, we pull a GPIO high/low depending on the configuration, connects 3.3V to + // BOOT0's RC charging circuit, lets it charge the capacitor, and issue a system reset. See the QMK discord + // #hardware channel pins for an example circuit. + palSetPadMode(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_MODE_OUTPUT_PUSHPULL); +# if STM32_BOOTLOADER_DUAL_BANK_POLARITY + palSetPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO)); +# else + palClearPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO)); +# endif + + // Wait for a while for the capacitor to charge + wait_ms(100); + + // Issue a system reset to get the ROM bootloader to execute, with BOOT0 high + NVIC_SystemReset(); +} + +void enter_bootloader_mode_if_requested(void) {} // not needed at all, but if anybody attempts to invoke it.... + +#elif defined(STM32_BOOTLOADER_ADDRESS) // STM32_BOOTLOADER_DUAL_BANK + +extern uint32_t __ram0_end__; + +__attribute__((weak)) void bootloader_jump(void) { + *MAGIC_ADDR = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader + NVIC_SystemReset(); +} + +void enter_bootloader_mode_if_requested(void) { + unsigned long *check = MAGIC_ADDR; + if (*check == BOOTLOADER_MAGIC) { + *check = 0; + __set_CONTROL(0); + __set_MSP(*(__IO uint32_t *)STM32_BOOTLOADER_ADDRESS); + __enable_irq(); + + typedef void (*BootJump_t)(void); + BootJump_t boot_jump = *(BootJump_t *)(STM32_BOOTLOADER_ADDRESS + 4); + boot_jump(); + while (1) + ; + } +} + +#elif defined(GD32VF103) + +# define DBGMCU_KEY_UNLOCK 0x4B5A6978 +# define DBGMCU_CMD_RESET 0x1 + +__IO uint32_t *DBGMCU_KEY = (uint32_t *)DBGMCU_BASE + 0x0CU; +__IO uint32_t *DBGMCU_CMD = (uint32_t *)DBGMCU_BASE + 0x08U; + +__attribute__((weak)) void bootloader_jump(void) { + /* The MTIMER unit of the GD32VF103 doesn't have the MSFRST + * register to generate a software reset request. + * BUT instead two undocumented registers in the debug peripheral + * that allow issueing a software reset. WHO would need the MSFRST + * register anyway? Source: + * https://github.com/esmil/gd32vf103inator/blob/master/include/gd32vf103/dbg.h */ + *DBGMCU_KEY = DBGMCU_KEY_UNLOCK; + *DBGMCU_CMD = DBGMCU_CMD_RESET; +} + +void enter_bootloader_mode_if_requested(void) { /* Jumping to bootloader is not possible from user code. */ +} + +#elif defined(KL2x) || defined(K20x) || defined(MK66F18) || defined(MIMXRT1062) // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS +/* Kinetis */ + +# if defined(BOOTLOADER_KIIBOHD) +/* Kiibohd Bootloader (MCHCK and Infinity KB) */ +# define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000 +const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff"; +__attribute__((weak)) void bootloader_jump(void) { + void *volatile vbat = (void *)VBAT; + __builtin_memcpy(vbat, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); + // request reset + SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk; +} + +# else /* defined(BOOTLOADER_KIIBOHD) */ +/* Default for Kinetis - expecting an ARM Teensy */ +# include "wait.h" +__attribute__((weak)) void bootloader_jump(void) { + wait_ms(100); + __BKPT(0); +} +# endif /* defined(BOOTLOADER_KIIBOHD) */ + +#else /* neither STM32 nor KINETIS */ +__attribute__((weak)) void bootloader_jump(void) {} +#endif diff --git a/platforms/chibios/chibios_config.h b/platforms/chibios/chibios_config.h new file mode 100644 index 0000000000..ad2f808a95 --- /dev/null +++ b/platforms/chibios/chibios_config.h @@ -0,0 +1,78 @@ +/* Copyright 2019 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#ifndef USB_VBUS_PIN +# define SPLIT_USB_DETECT // Force this on when dedicated pin is not used +#endif + +// STM32 compatibility +#if defined(MCU_STM32) +# define CPU_CLOCK STM32_SYSCLK + +# if defined(STM32F1XX) +# define USE_GPIOV1 +# define PAL_MODE_ALTERNATE_OPENDRAIN PAL_MODE_STM32_ALTERNATE_OPENDRAIN +# define PAL_MODE_ALTERNATE_PUSHPULL PAL_MODE_STM32_ALTERNATE_PUSHPULL +# else +# define PAL_OUTPUT_TYPE_OPENDRAIN PAL_STM32_OTYPE_OPENDRAIN +# define PAL_OUTPUT_TYPE_PUSHPULL PAL_STM32_OTYPE_PUSHPULL +# define PAL_OUTPUT_SPEED_HIGHEST PAL_STM32_OSPEED_HIGHEST +# define PAL_PUPDR_FLOATING PAL_STM32_PUPDR_FLOATING +# endif + +# if defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32L1XX) +# define USE_I2CV1 +# endif +#endif + +// GD32 compatibility +#if defined(MCU_GD32V) +# define CPU_CLOCK GD32_SYSCLK + +# if defined(GD32VF103) +# define USE_GPIOV1 +# define USE_I2CV1 +# define PAL_MODE_ALTERNATE_OPENDRAIN PAL_MODE_GD32_ALTERNATE_OPENDRAIN +# define PAL_MODE_ALTERNATE_PUSHPULL PAL_MODE_GD32_ALTERNATE_PUSHPULL +# endif +#endif + +#if defined(GD32VF103) +/* This chip has the same API as STM32F103, but uses different names for literally the same thing. + * As of 4.7.2021 QMK is tailored to use STM32 defines/names, for compatibility sake + * we just redefine the GD32 names. */ +# include "gd32v_compatibility.h" +#endif + +// teensy compatibility +#if defined(MCU_KINETIS) +# define CPU_CLOCK KINETIS_SYSCLK_FREQUENCY + +# if defined(K20x) || defined(KL2x) +# define USE_I2CV1 +# define USE_I2CV1_CONTRIB // for some reason a bunch of ChibiOS-Contrib boards only have clock_speed +# define USE_GPIOV1 +# endif +#endif + +#if defined(HT32) +# define CPU_CLOCK HT32_CK_SYS_FREQUENCY +# define PAL_MODE_ALTERNATE PAL_HT32_MODE_AF +# define PAL_OUTPUT_TYPE_OPENDRAIN (PAL_HT32_MODE_OD | PAL_HT32_MODE_DIR) +# define PAL_OUTPUT_TYPE_PUSHPULL PAL_HT32_MODE_DIR +# define PAL_OUTPUT_SPEED_HIGHEST 0 +#endif diff --git a/platforms/chibios/eeprom_stm32.c b/platforms/chibios/eeprom_stm32.c new file mode 100644 index 0000000000..acc6a48516 --- /dev/null +++ b/platforms/chibios/eeprom_stm32.c @@ -0,0 +1,687 @@ +/* + * This software is experimental and a work in progress. + * Under no circumstances should these files be used in relation to any critical system(s). + * Use of these files is at your own risk. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by + * Artur F. + * + * Modifications for QMK and STM32F303 by Yiancar + * Modifications to add flash wear leveling by Ilya Zhuravlev + * Modifications to increase flash density by Don Kjer + */ + +#include +#include +#include "util.h" +#include "debug.h" +#include "eeprom_stm32.h" +#include "flash_stm32.h" + +/* + * We emulate eeprom by writing a snapshot compacted view of eeprom contents, + * followed by a write log of any change since that snapshot: + * + * === SIMULATED EEPROM CONTENTS === + * + * ┌─ Compacted ┬ Write Log ─┐ + * │............│[BYTE][BYTE]│ + * │FFFF....FFFF│[WRD0][WRD1]│ + * │FFFFFFFFFFFF│[WORD][NEXT]│ + * │....FFFFFFFF│[BYTE][WRD0]│ + * ├────────────┼────────────┤ + * └──PAGE_BASE │ │ + * PAGE_LAST─┴─WRITE_BASE │ + * WRITE_LAST ┘ + * + * Compacted contents are the 1's complement of the actual EEPROM contents. + * e.g. An 'FFFF' represents a '0000' value. + * + * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom. + * The size of the compacted-area and write log are configurable, and the combined + * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent. + * Simulated Eeprom contents are located at the end of available flash space. + * + * The following configuration defines can be set: + * + * FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log) + * FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT) + * NOTE: The current implementation does not include page swapping, + * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents. + * + * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals + * FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES. + * The larger the write log, the less frequently the compacted area needs to be rewritten. + * + * + * *** General Algorithm *** + * + * During initialization: + * The contents of the Compacted-flash area are loaded and the 1's complement value + * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache). + * Write log entries are processed until a 0xFFFF is reached. + * Each log entry updates a byte or word in the cache. + * + * During reads: + * EEPROM contents are given back directly from the cache in memory. + * + * During writes: + * The contents of the cache is updated first. + * If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash + * Otherwise: + * If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area. + * Otherwise a Write log entry is constructed and appended to the next free position in the Write log. + * + * + * *** Write Log Structure *** + * + * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned. + * + * === WRITE LOG ENTRY FORMATS === + * + * ╔═══ Byte-Entry ══╗ + * ║0XXXXXXX║YYYYYYYY║ + * ║ └──┬──┘║└──┬───┘║ + * ║ Address║ Value ║ + * ╚════════╩════════╝ + * 0 <= Address < 0x80 (128) + * + * ╔ Word-Encoded 0 ╗ + * ║100XXXXXXXXXXXXX║ + * ║ │└─────┬─────┘║ + * ║ │Address >> 1 ║ + * ║ └── Value: 0 ║ + * ╚════════════════╝ + * 0 <= Address <= 0x3FFE (16382) + * + * ╔ Word-Encoded 1 ╗ + * ║101XXXXXXXXXXXXX║ + * ║ │└─────┬─────┘║ + * ║ │Address >> 1 ║ + * ║ └── Value: 1 ║ + * ╚════════════════╝ + * 0 <= Address <= 0x3FFE (16382) + * + * ╔═══ Reserved ═══╗ + * ║110XXXXXXXXXXXXX║ + * ╚════════════════╝ + * + * ╔═══════════ Word-Next ═══════════╗ + * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║ + * ║ └─────┬─────┘║└───────┬──────┘║ + * ║(Address-128)>>1║ ~Value ║ + * ╚════════════════╩════════════════╝ + * ( 0 <= Address < 0x0080 (128): Reserved) + * 0x80 <= Address <= 0x3FFE (16382) + * + * Write Log entry ranges: + * 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF) + * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0 + * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1 + * 0xC000 ... 0xDFFF - Reserved + * 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry) + * 0xFFC0 ... 0xFFFE - Reserved + * 0xFFFF - Unprogrammed + * + */ + +#include "eeprom_stm32_defs.h" +#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS) +# error "not implemented." +#endif + +/* These bits are used for optimizing encoding of bytes, 0 and 1 */ +#define FEE_WORD_ENCODING 0x8000 +#define FEE_VALUE_NEXT 0x6000 +#define FEE_VALUE_RESERVED 0x4000 +#define FEE_VALUE_ENCODED 0x2000 +#define FEE_BYTE_RANGE 0x80 + +/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */ +#define FEE_ADDRESS_MAX_SIZE 0x4000 + +/* Flash word value after erase */ +#define FEE_EMPTY_WORD ((uint16_t)0xFFFF) + +/* Size of combined compacted eeprom and write log pages */ +#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE) + +#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */ +# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024) +# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024) +# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size +# endif +#endif + +/* Size of emulated eeprom */ +#ifdef FEE_DENSITY_BYTES +# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE) +# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE) +# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE +# endif +# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE) +# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE) +# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate! +# endif +# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE +# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE) +# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows +# endif +# if ((FEE_DENSITY_BYTES) % 2) == 1 +# error emulated eeprom: FEE_DENSITY_BYTES must be even +# endif +#else +/* Default to half of allocated space used for emulated eeprom, half for write log */ +# define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2) +#endif + +/* Size of write log */ +#ifdef FEE_WRITE_LOG_BYTES +# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE) +# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE) +# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE +# endif +# if ((FEE_WRITE_LOG_BYTES) % 2) == 1 +# error emulated eeprom: FEE_WRITE_LOG_BYTES must be even +# endif +#else +/* Default to use all remaining space */ +# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES) +#endif + +/* Start of the emulated eeprom compacted flash area */ +#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS +/* End of the emulated eeprom compacted flash area */ +#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES) +/* Start of the emulated eeprom write log */ +#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS +/* End of the emulated eeprom write log */ +#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES) + +#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES) +# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available +#endif + +/* In-memory contents of emulated eeprom for faster access */ +/* *TODO: Implement page swapping */ +static uint16_t WordBuf[FEE_DENSITY_BYTES / 2]; +static uint8_t *DataBuf = (uint8_t *)WordBuf; + +/* Pointer to the first available slot within the write log */ +static uint16_t *empty_slot; + +// #define DEBUG_EEPROM_OUTPUT + +/* + * Debug print utils + */ + +#if defined(DEBUG_EEPROM_OUTPUT) + +# define debug_eeprom debug_enable +# define eeprom_println(s) println(s) +# define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__); + +#else /* NO_DEBUG */ + +# define debug_eeprom false +# define eeprom_println(s) +# define eeprom_printf(fmt, ...) + +#endif /* NO_DEBUG */ + +void print_eeprom(void) { +#ifndef NO_DEBUG + int empty_rows = 0; + for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) { + if (i % 16 == 0) { + if (i >= FEE_DENSITY_BYTES - 16) { + /* Make sure we display the last row */ + empty_rows = 0; + } + /* Check if this row is uninitialized */ + ++empty_rows; + for (uint16_t j = 0; j < 16; j++) { + if (DataBuf[i + j]) { + empty_rows = 0; + break; + } + } + if (empty_rows > 1) { + /* Repeat empty row */ + if (empty_rows == 2) { + /* Only display the first repeat empty row */ + println("*"); + } + i += 15; + continue; + } + xprintf("%04x", i); + } + if (i % 8 == 0) print(" "); + + xprintf(" %02x", DataBuf[i]); + if ((i + 1) % 16 == 0) { + println(""); + } + } +#endif +} + +uint16_t EEPROM_Init(void) { + /* Load emulated eeprom contents from compacted flash into memory */ + uint16_t *src = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS; + uint16_t *dest = (uint16_t *)DataBuf; + for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) { + *dest = ~*src; + } + + if (debug_eeprom) { + println("EEPROM_Init Compacted Pages:"); + print_eeprom(); + println("EEPROM_Init Write Log:"); + } + + /* Replay write log */ + uint16_t *log_addr; + for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) { + uint16_t address = *log_addr; + if (address == FEE_EMPTY_WORD) { + break; + } + /* Check for lowest 128-bytes optimization */ + if (!(address & FEE_WORD_ENCODING)) { + uint8_t bvalue = (uint8_t)address; + address >>= 8; + DataBuf[address] = bvalue; + eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue); + } else { + uint16_t wvalue; + /* Check if value is in next word */ + if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) { + /* Read value from next word */ + if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { + break; + } + wvalue = ~*log_addr; + if (!wvalue) { + eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr); + /* Possibly incomplete write. Ignore and continue */ + continue; + } + address &= 0x1FFF; + address <<= 1; + /* Writes to addresses less than 128 are byte log entries */ + address += FEE_BYTE_RANGE; + } else { + /* Reserved for future use */ + if (address & FEE_VALUE_RESERVED) { + eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr); + continue; + } + /* Optimization for 0 or 1 values. */ + wvalue = (address & FEE_VALUE_ENCODED) >> 13; + address &= 0x1FFF; + address <<= 1; + } + if (address < FEE_DENSITY_BYTES) { + eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue); + *(uint16_t *)(&DataBuf[address]) = wvalue; + } else { + eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue); + } + } + } + + empty_slot = log_addr; + + if (debug_eeprom) { + println("EEPROM_Init Final DataBuf:"); + print_eeprom(); + } + + return FEE_DENSITY_BYTES; +} + +/* Clear flash contents (doesn't touch in-memory DataBuf) */ +static void eeprom_clear(void) { + FLASH_Unlock(); + + for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) { + eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE))); + FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); + } + + FLASH_Lock(); + + empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; + eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot); +} + +/* Erase emulated eeprom */ +void EEPROM_Erase(void) { + eeprom_println("EEPROM_Erase"); + /* Erase compacted pages and write log */ + eeprom_clear(); + /* re-initialize to reset DataBuf */ + EEPROM_Init(); +} + +/* Compact write log */ +static uint8_t eeprom_compact(void) { + /* Erase compacted pages and write log */ + eeprom_clear(); + + FLASH_Unlock(); + + FLASH_Status final_status = FLASH_COMPLETE; + + /* Write emulated eeprom contents from memory to compacted flash */ + uint16_t *src = (uint16_t *)DataBuf; + uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS; + uint16_t value; + for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) { + value = *src; + if (value) { + eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value); + FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value); + if (status != FLASH_COMPLETE) final_status = status; + } + } + + FLASH_Lock(); + + if (debug_eeprom) { + println("eeprom_compacted:"); + print_eeprom(); + } + + return final_status; +} + +static uint8_t eeprom_write_direct_entry(uint16_t Address) { + /* Check if we can just write this directly to the compacted flash area */ + uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE); + if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) { + /* Write the value directly to the compacted area without a log entry */ + uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]); + /* Early exit if a write isn't needed */ + if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE; + + FLASH_Unlock(); + + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value); + FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value); + + FLASH_Lock(); + return status; + } + return 0; +} + +static uint8_t eeprom_write_log_word_entry(uint16_t Address) { + FLASH_Status final_status = FLASH_COMPLETE; + + uint16_t value = *(uint16_t *)(&DataBuf[Address]); + eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value); + + /* MSB signifies the lowest 128-byte optimization is not in effect */ + uint16_t encoding = FEE_WORD_ENCODING; + uint8_t entry_size; + if (value <= 1) { + encoding |= value << 13; + entry_size = 2; + } else { + encoding |= FEE_VALUE_NEXT; + entry_size = 4; + /* Writes to addresses less than 128 are byte log entries */ + Address -= FEE_BYTE_RANGE; + } + + /* if we can't find an empty spot, we must compact emulated eeprom */ + if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) { + /* compact the write log into the compacted flash area */ + return eeprom_compact(); + } + + /* Word log writes should be word-aligned. Take back a bit */ + Address >>= 1; + Address |= encoding; + + /* ok we found a place let's write our data */ + FLASH_Unlock(); + + /* address */ + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address); + final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address); + + /* value */ + if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) { + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value); + FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value); + if (status != FLASH_COMPLETE) final_status = status; + } + + FLASH_Lock(); + + return final_status; +} + +static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { + eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]); + + /* if couldn't find an empty spot, we must compact emulated eeprom */ + if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { + /* compact the write log into the compacted flash area */ + return eeprom_compact(); + } + + /* ok we found a place let's write our data */ + FLASH_Unlock(); + + /* Pack address and value into the same word */ + uint16_t value = (Address << 8) | DataBuf[Address]; + + /* write to flash */ + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value); + FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value); + + FLASH_Lock(); + + return status; +} + +uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= FEE_DENSITY_BYTES) { + eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte); + return FLASH_BAD_ADDRESS; + } + + /* if the value is the same, don't bother writing it */ + if (DataBuf[Address] == DataByte) { + eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte); + return 0; + } + + /* keep DataBuf cache in sync */ + DataBuf[Address] = DataByte; + eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]); + + /* perform the write into flash memory */ + /* First, attempt to write directly into the compacted flash area */ + FLASH_Status status = eeprom_write_direct_entry(Address); + if (!status) { + /* Otherwise append to the write log */ + if (Address < FEE_BYTE_RANGE) { + status = eeprom_write_log_byte_entry(Address); + } else { + status = eeprom_write_log_word_entry(Address & 0xFFFE); + } + } + if (status != 0 && status != FLASH_COMPLETE) { + eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status); + } + return status; +} + +uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= FEE_DENSITY_BYTES) { + eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord); + return FLASH_BAD_ADDRESS; + } + + /* Check for word alignment */ + FLASH_Status final_status = FLASH_COMPLETE; + if (Address % 2) { + final_status = EEPROM_WriteDataByte(Address, DataWord); + FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8); + if (status != FLASH_COMPLETE) final_status = status; + if (final_status != 0 && final_status != FLASH_COMPLETE) { + eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + } + return final_status; + } + + /* if the value is the same, don't bother writing it */ + uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]); + if (oldValue == DataWord) { + eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord); + return 0; + } + + /* keep DataBuf cache in sync */ + *(uint16_t *)(&DataBuf[Address]) = DataWord; + eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address])); + + /* perform the write into flash memory */ + /* First, attempt to write directly into the compacted flash area */ + final_status = eeprom_write_direct_entry(Address); + if (!final_status) { + /* Otherwise append to the write log */ + /* Check if we need to fall back to byte write */ + if (Address < FEE_BYTE_RANGE) { + final_status = FLASH_COMPLETE; + /* Only write a byte if it has changed */ + if ((uint8_t)oldValue != (uint8_t)DataWord) { + final_status = eeprom_write_log_byte_entry(Address); + } + FLASH_Status status = FLASH_COMPLETE; + /* Only write a byte if it has changed */ + if ((oldValue >> 8) != (DataWord >> 8)) { + status = eeprom_write_log_byte_entry(Address + 1); + } + if (status != FLASH_COMPLETE) final_status = status; + } else { + final_status = eeprom_write_log_word_entry(Address); + } + } + if (final_status != 0 && final_status != FLASH_COMPLETE) { + eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + } + return final_status; +} + +uint8_t EEPROM_ReadDataByte(uint16_t Address) { + uint8_t DataByte = 0xFF; + + if (Address < FEE_DENSITY_BYTES) { + DataByte = DataBuf[Address]; + } + + eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte); + + return DataByte; +} + +uint16_t EEPROM_ReadDataWord(uint16_t Address) { + uint16_t DataWord = 0xFFFF; + + if (Address < FEE_DENSITY_BYTES - 1) { + /* Check word alignment */ + if (Address % 2) { + DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8); + } else { + DataWord = *(uint16_t *)(&DataBuf[Address]); + } + } + + eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord); + + return DataWord; +} + +/***************************************************************************** + * Bind to eeprom_driver.c + *******************************************************************************/ +void eeprom_driver_init(void) { EEPROM_Init(); } + +void eeprom_driver_erase(void) { EEPROM_Erase(); } + +void eeprom_read_block(void *buf, const void *addr, size_t len) { + const uint8_t *src = (const uint8_t *)addr; + uint8_t * dest = (uint8_t *)buf; + + /* Check word alignment */ + if (len && (uintptr_t)src % 2) { + /* Read the unaligned first byte */ + *dest++ = EEPROM_ReadDataByte((const uintptr_t)src++); + --len; + } + + uint16_t value; + bool aligned = ((uintptr_t)dest % 2 == 0); + while (len > 1) { + value = EEPROM_ReadDataWord((const uintptr_t)((uint16_t *)src)); + if (aligned) { + *(uint16_t *)dest = value; + dest += 2; + } else { + *dest++ = value; + *dest++ = value >> 8; + } + src += 2; + len -= 2; + } + if (len) { + *dest = EEPROM_ReadDataByte((const uintptr_t)src); + } +} + +void eeprom_write_block(const void *buf, void *addr, size_t len) { + uint8_t * dest = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + + /* Check word alignment */ + if (len && (uintptr_t)dest % 2) { + /* Write the unaligned first byte */ + EEPROM_WriteDataByte((uintptr_t)dest++, *src++); + --len; + } + + uint16_t value; + bool aligned = ((uintptr_t)src % 2 == 0); + while (len > 1) { + if (aligned) { + value = *(uint16_t *)src; + } else { + value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8); + } + EEPROM_WriteDataWord((uintptr_t)((uint16_t *)dest), value); + dest += 2; + src += 2; + len -= 2; + } + + if (len) { + EEPROM_WriteDataByte((uintptr_t)dest, *src); + } +} diff --git a/platforms/chibios/eeprom_stm32.h b/platforms/chibios/eeprom_stm32.h new file mode 100644 index 0000000000..8fcfb556b8 --- /dev/null +++ b/platforms/chibios/eeprom_stm32.h @@ -0,0 +1,33 @@ +/* + * This software is experimental and a work in progress. + * Under no circumstances should these files be used in relation to any critical system(s). + * Use of these files is at your own risk. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by + * Artur F. + * + * Modifications for QMK and STM32F303 by Yiancar + * + * This library assumes 8-bit data locations. To add a new MCU, please provide the flash + * page size and the total flash size in Kb. The number of available pages must be a multiple + * of 2. Only half of the pages account for the total EEPROM size. + * This library also assumes that the pages are not used by the firmware. + */ + +#pragma once + +uint16_t EEPROM_Init(void); +void EEPROM_Erase(void); +uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); +uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord); +uint8_t EEPROM_ReadDataByte(uint16_t Address); +uint16_t EEPROM_ReadDataWord(uint16_t Address); + +void print_eeprom(void); diff --git a/platforms/chibios/eeprom_stm32_defs.h b/platforms/chibios/eeprom_stm32_defs.h new file mode 100644 index 0000000000..66904f247f --- /dev/null +++ b/platforms/chibios/eeprom_stm32_defs.h @@ -0,0 +1,74 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) +# if defined(STM32F103xB) || defined(STM32F042x6) || defined(GD32VF103C8) || defined(GD32VF103CB) +# ifndef FEE_PAGE_SIZE +# define FEE_PAGE_SIZE 0x400 // Page size = 1KByte +# endif +# ifndef FEE_PAGE_COUNT +# define FEE_PAGE_COUNT 2 // How many pages are used +# endif +# elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F072xB) || defined(STM32F070xB) +# ifndef FEE_PAGE_SIZE +# define FEE_PAGE_SIZE 0x800 // Page size = 2KByte +# endif +# ifndef FEE_PAGE_COUNT +# define FEE_PAGE_COUNT 4 // How many pages are used +# endif +# elif defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE) +# ifndef FEE_PAGE_SIZE +# define FEE_PAGE_SIZE 0x4000 // Page size = 16KByte +# endif +# ifndef FEE_PAGE_COUNT +# define FEE_PAGE_COUNT 1 // How many pages are used +# endif +# endif +#endif + +#if !defined(FEE_MCU_FLASH_SIZE) +# if defined(STM32F042x6) +# define FEE_MCU_FLASH_SIZE 32 // Size in Kb +# elif defined(GD32VF103C8) +# define FEE_MCU_FLASH_SIZE 64 // Size in Kb +# elif defined(STM32F103xB) || defined(STM32F072xB) || defined(STM32F070xB) || defined(GD32VF103CB) +# define FEE_MCU_FLASH_SIZE 128 // Size in Kb +# elif defined(STM32F303xC) || defined(STM32F401xC) +# define FEE_MCU_FLASH_SIZE 256 // Size in Kb +# elif defined(STM32F103xE) || defined(STM32F401xE) || defined(STM32F411xE) +# define FEE_MCU_FLASH_SIZE 512 // Size in Kb +# elif defined(STM32F405xG) +# define FEE_MCU_FLASH_SIZE 1024 // Size in Kb +# endif +#endif + +/* Start of the emulated eeprom */ +#if !defined(FEE_PAGE_BASE_ADDRESS) +# if defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE) +# ifndef FEE_PAGE_BASE_ADDRESS +# define FEE_PAGE_BASE_ADDRESS 0x08004000 // bodge to force 2nd 16k page +# endif +# else +# ifndef FEE_FLASH_BASE +# define FEE_FLASH_BASE 0x8000000 +# endif +/* Default to end of flash */ +# define FEE_PAGE_BASE_ADDRESS ((uintptr_t)(FEE_FLASH_BASE) + FEE_MCU_FLASH_SIZE * 1024 - (FEE_PAGE_COUNT * FEE_PAGE_SIZE)) +# endif +#endif diff --git a/platforms/chibios/eeprom_teensy.c b/platforms/chibios/eeprom_teensy.c new file mode 100644 index 0000000000..97da6f9e14 --- /dev/null +++ b/platforms/chibios/eeprom_teensy.c @@ -0,0 +1,795 @@ +#include +#include + +#include "eeconfig.h" + +/*************************************/ +/* Hardware backend */ +/* */ +/* Code from PJRC/Teensyduino */ +/*************************************/ + +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define SMC_PMSTAT_RUN ((uint8_t)0x01) +#define SMC_PMSTAT_HSRUN ((uint8_t)0x80) + +#define F_CPU KINETIS_SYSCLK_FREQUENCY + +static inline int kinetis_hsrun_disable(void) { +#if defined(MK66F18) + if (SMC->PMSTAT == SMC_PMSTAT_HSRUN) { +// First, reduce the CPU clock speed, but do not change +// the peripheral speed (F_BUS). Serial1 & Serial2 baud +// rates will be impacted, but most other peripherals +// will continue functioning at the same speed. +# if F_CPU == 256000000 && F_BUS == 64000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // TODO: TEST +# elif F_CPU == 256000000 && F_BUS == 128000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // TODO: TEST +# elif F_CPU == 240000000 && F_BUS == 60000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // ok +# elif F_CPU == 240000000 && F_BUS == 80000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok +# elif F_CPU == 240000000 && F_BUS == 120000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok +# elif F_CPU == 216000000 && F_BUS == 54000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // ok +# elif F_CPU == 216000000 && F_BUS == 72000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok +# elif F_CPU == 216000000 && F_BUS == 108000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok +# elif F_CPU == 192000000 && F_BUS == 48000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // ok +# elif F_CPU == 192000000 && F_BUS == 64000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok +# elif F_CPU == 192000000 && F_BUS == 96000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok +# elif F_CPU == 180000000 && F_BUS == 60000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok +# elif F_CPU == 180000000 && F_BUS == 90000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok +# elif F_CPU == 168000000 && F_BUS == 56000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 5); // ok +# elif F_CPU == 144000000 && F_BUS == 48000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 5); // ok +# elif F_CPU == 144000000 && F_BUS == 72000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 5); // ok +# elif F_CPU == 120000000 && F_BUS == 60000000 + SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV1(KINETIS_CLKDIV1_OUTDIV1 - 1) | SIM_CLKDIV1_OUTDIV2(KINETIS_CLKDIV1_OUTDIV2 - 1) | +# if defined(MK66F18) + SIM_CLKDIV1_OUTDIV3(KINETIS_CLKDIV1_OUTDIV3 - 1) | +# endif + SIM_CLKDIV1_OUTDIV4(KINETIS_CLKDIV1_OUTDIV4 - 1); +# else + return 0; +# endif + // Then turn off HSRUN mode + SMC->PMCTRL = SMC_PMCTRL_RUNM_SET(0); + while (SMC->PMSTAT == SMC_PMSTAT_HSRUN) + ; // wait + return 1; + } +#endif + return 0; +} + +static inline int kinetis_hsrun_enable(void) { +#if defined(MK66F18) + if (SMC->PMSTAT == SMC_PMSTAT_RUN) { + // Turn HSRUN mode on + SMC->PMCTRL = SMC_PMCTRL_RUNM_SET(3); + while (SMC->PMSTAT != SMC_PMSTAT_HSRUN) { + ; + } // wait +// Then configure clock for full speed +# if F_CPU == 256000000 && F_BUS == 64000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7); +# elif F_CPU == 256000000 && F_BUS == 128000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7); +# elif F_CPU == 240000000 && F_BUS == 60000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7); +# elif F_CPU == 240000000 && F_BUS == 80000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 7); +# elif F_CPU == 240000000 && F_BUS == 120000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7); +# elif F_CPU == 216000000 && F_BUS == 54000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7); +# elif F_CPU == 216000000 && F_BUS == 72000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 7); +# elif F_CPU == 216000000 && F_BUS == 108000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7); +# elif F_CPU == 192000000 && F_BUS == 48000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 6); +# elif F_CPU == 192000000 && F_BUS == 64000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 6); +# elif F_CPU == 192000000 && F_BUS == 96000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 6); +# elif F_CPU == 180000000 && F_BUS == 60000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 6); +# elif F_CPU == 180000000 && F_BUS == 90000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 6); +# elif F_CPU == 168000000 && F_BUS == 56000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 5); +# elif F_CPU == 144000000 && F_BUS == 48000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 4); +# elif F_CPU == 144000000 && F_BUS == 72000000 + SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 4); +# elif F_CPU == 120000000 && F_BUS == 60000000 + SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV1(KINETIS_CLKDIV1_OUTDIV1 - 1) | SIM_CLKDIV1_OUTDIV2(KINETIS_CLKDIV1_OUTDIV2 - 1) | +# if defined(MK66F18) + SIM_CLKDIV1_OUTDIV3(KINETIS_CLKDIV1_OUTDIV3 - 1) | +# endif + SIM_CLKDIV1_OUTDIV4(KINETIS_CLKDIV1_OUTDIV4 - 1); +# else + return 0; +# endif + return 1; + } +#endif + return 0; +} + +#if defined(K20x) || defined(MK66F18) /* chip selection */ +/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */ + +// The EEPROM is really RAM with a hardware-based backup system to +// flash memory. Selecting a smaller size EEPROM allows more wear +// leveling, for higher write endurance. If you edit this file, +// set this to the smallest size your application can use. Also, +// due to Freescale's implementation, writing 16 or 32 bit words +// (aligned to 2 or 4 byte boundaries) has twice the endurance +// compared to writing 8 bit bytes. +// +# ifndef EEPROM_SIZE +# define EEPROM_SIZE 32 +# endif + +/* + ^^^ Here be dragons: + NXP AppNote AN4282 section 3.1 states that partitioning must only be done once. + Once EEPROM partitioning is done, the size is locked to this initial configuration. + Attempts to modify the EEPROM_SIZE setting may brick your board. +*/ + +// Writing unaligned 16 or 32 bit data is handled automatically when +// this is defined, but at a cost of extra code size. Without this, +// any unaligned write will cause a hard fault exception! If you're +// absolutely sure all 16 and 32 bit writes will be aligned, you can +// remove the extra unnecessary code. +// +# define HANDLE_UNALIGNED_WRITES + +# if defined(K20x) +# define EEPROM_MAX 2048 +# define EEPARTITION 0x03 // all 32K dataflash for EEPROM, none for Data +# define EEESPLIT 0x30 // must be 0x30 on these chips +# elif defined(MK66F18) +# define EEPROM_MAX 4096 +# define EEPARTITION 0x05 // 128K dataflash for EEPROM, 128K for Data +# define EEESPLIT 0x10 // best endurance: 0x00 = first 12%, 0x10 = first 25%, 0x30 = all equal +# endif + +// Minimum EEPROM Endurance +// ------------------------ +# if (EEPROM_SIZE == 4096) +# define EEESIZE 0x02 +# elif (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word +# define EEESIZE 0x03 +# elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word +# define EEESIZE 0x04 +# elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word +# define EEESIZE 0x05 +# elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word +# define EEESIZE 0x06 +# elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word +# define EEESIZE 0x07 +# elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word +# define EEESIZE 0x08 +# elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word +# define EEESIZE 0x09 +# endif + +/** \brief eeprom initialization + * + * FIXME: needs doc + */ +void eeprom_initialize(void) { + uint32_t count = 0; + uint16_t do_flash_cmd[] = {0xf06f, 0x037f, 0x7003, 0x7803, 0xf013, 0x0f80, 0xd0fb, 0x4770}; + uint8_t status; + + if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) { + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + + // FlexRAM is configured as traditional RAM + // We need to reconfigure for EEPROM usage + kinetis_hsrun_disable(); + FTFL->FCCOB0 = 0x80; // PGMPART = Program Partition Command + FTFL->FCCOB3 = 0; + FTFL->FCCOB4 = EEESPLIT | EEESIZE; + FTFL->FCCOB5 = EEPARTITION; + __disable_irq(); + // do_flash_cmd() must execute from RAM. Luckily the C syntax is simple... + (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT)); + __enable_irq(); + kinetis_hsrun_enable(); + status = FTFL->FSTAT; + if (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)) { + FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)); + return; // error + } + } + // wait for eeprom to become ready (is this really necessary?) + while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { + if (++count > 200000) break; + } +} + +# define FlexRAM ((volatile uint8_t *)0x14000000) + +/** \brief eeprom read byte + * + * FIXME: needs doc + */ +uint8_t eeprom_read_byte(const uint8_t *addr) { + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return FlexRAM[offset]; +} + +/** \brief eeprom read word + * + * FIXME: needs doc + */ +uint16_t eeprom_read_word(const uint16_t *addr) { + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE - 1) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return *(uint16_t *)(&FlexRAM[offset]); +} + +/** \brief eeprom read dword + * + * FIXME: needs doc + */ +uint32_t eeprom_read_dword(const uint32_t *addr) { + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE - 3) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return *(uint32_t *)(&FlexRAM[offset]); +} + +/** \brief eeprom read block + * + * FIXME: needs doc + */ +void eeprom_read_block(void *buf, const void *addr, uint32_t len) { + uint32_t offset = (uint32_t)addr; + uint8_t *dest = (uint8_t *)buf; + uint32_t end = offset + len; + + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (end > EEPROM_SIZE) end = EEPROM_SIZE; + while (offset < end) { + *dest++ = FlexRAM[offset++]; + } +} + +/** \brief eeprom is ready + * + * FIXME: needs doc + */ +int eeprom_is_ready(void) { return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; } + +/** \brief flexram wait + * + * FIXME: needs doc + */ +static void flexram_wait(void) { + while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { + // TODO: timeout + } +} + +/** \brief eeprom_write_byte + * + * FIXME: needs doc + */ +void eeprom_write_byte(uint8_t *addr, uint8_t value) { + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (FlexRAM[offset] != value) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + FlexRAM[offset] = value; + flexram_wait(); + kinetis_hsrun_enable(); + } +} + +/** \brief eeprom write word + * + * FIXME: needs doc + */ +void eeprom_write_word(uint16_t *addr, uint16_t value) { + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE - 1) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); +# ifdef HANDLE_UNALIGNED_WRITES + if ((offset & 1) == 0) { +# endif + if (*(uint16_t *)(&FlexRAM[offset]) != value) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + *(uint16_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + kinetis_hsrun_enable(); + } +# ifdef HANDLE_UNALIGNED_WRITES + } else { + if (FlexRAM[offset] != value) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + FlexRAM[offset] = value; + flexram_wait(); + kinetis_hsrun_enable(); + } + if (FlexRAM[offset + 1] != (value >> 8)) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + FlexRAM[offset + 1] = value >> 8; + flexram_wait(); + kinetis_hsrun_enable(); + } + } +# endif +} + +/** \brief eeprom write dword + * + * FIXME: needs doc + */ +void eeprom_write_dword(uint32_t *addr, uint32_t value) { + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE - 3) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); +# ifdef HANDLE_UNALIGNED_WRITES + switch (offset & 3) { + case 0: +# endif + if (*(uint32_t *)(&FlexRAM[offset]) != value) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + *(uint32_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + kinetis_hsrun_enable(); + } + return; +# ifdef HANDLE_UNALIGNED_WRITES + case 2: + if (*(uint16_t *)(&FlexRAM[offset]) != value) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + *(uint16_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + kinetis_hsrun_enable(); + } + if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16; + flexram_wait(); + kinetis_hsrun_enable(); + } + return; + default: + if (FlexRAM[offset] != value) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + FlexRAM[offset] = value; + flexram_wait(); + kinetis_hsrun_enable(); + } + if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8; + flexram_wait(); + kinetis_hsrun_enable(); + } + if (FlexRAM[offset + 3] != (value >> 24)) { + kinetis_hsrun_disable(); + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + FlexRAM[offset + 3] = value >> 24; + flexram_wait(); + kinetis_hsrun_enable(); + } + } +# endif +} + +/** \brief eeprom write block + * + * FIXME: needs doc + */ +void eeprom_write_block(const void *buf, void *addr, uint32_t len) { + uint32_t offset = (uint32_t)addr; + const uint8_t *src = (const uint8_t *)buf; + + if (offset >= EEPROM_SIZE) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (len >= EEPROM_SIZE) len = EEPROM_SIZE; + if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset; + kinetis_hsrun_disable(); + while (len > 0) { + uint32_t lsb = offset & 3; + if (lsb == 0 && len >= 4) { + // write aligned 32 bits + uint32_t val32; + val32 = *src++; + val32 |= (*src++ << 8); + val32 |= (*src++ << 16); + val32 |= (*src++ << 24); + if (*(uint32_t *)(&FlexRAM[offset]) != val32) { + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + *(uint32_t *)(&FlexRAM[offset]) = val32; + flexram_wait(); + } + offset += 4; + len -= 4; + } else if ((lsb == 0 || lsb == 2) && len >= 2) { + // write aligned 16 bits + uint16_t val16; + val16 = *src++; + val16 |= (*src++ << 8); + if (*(uint16_t *)(&FlexRAM[offset]) != val16) { + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + *(uint16_t *)(&FlexRAM[offset]) = val16; + flexram_wait(); + } + offset += 2; + len -= 2; + } else { + // write 8 bits + uint8_t val8 = *src++; + if (FlexRAM[offset] != val8) { + uint8_t stat = FTFL->FSTAT & 0x70; + if (stat) FTFL->FSTAT = stat; + FlexRAM[offset] = val8; + flexram_wait(); + } + offset++; + len--; + } + } + kinetis_hsrun_enable(); +} + +/* +void do_flash_cmd(volatile uint8_t *fstat) +{ + *fstat = 0x80; + while ((*fstat & 0x80) == 0) ; // wait +} +00000000 : + 0: f06f 037f mvn.w r3, #127 ; 0x7f + 4: 7003 strb r3, [r0, #0] + 6: 7803 ldrb r3, [r0, #0] + 8: f013 0f80 tst.w r3, #128 ; 0x80 + c: d0fb beq.n 6 + e: 4770 bx lr +*/ + +#elif defined(KL2x) /* chip selection */ +/* Teensy LC (emulated) */ + +# define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) + +extern uint32_t __eeprom_workarea_start__; +extern uint32_t __eeprom_workarea_end__; + +# define EEPROM_SIZE 128 + +static uint32_t flashend = 0; + +void eeprom_initialize(void) { + const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); + + do { + if (*p++ == 0xFFFF) { + flashend = (uint32_t)(p - 2); + return; + } + } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__)); + flashend = (uint32_t)(p - 1); +} + +uint8_t eeprom_read_byte(const uint8_t *addr) { + uint32_t offset = (uint32_t)addr; + const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); + const uint16_t *end = (const uint16_t *)((uint32_t)flashend); + uint16_t val; + uint8_t data = 0xFF; + + if (!end) { + eeprom_initialize(); + end = (const uint16_t *)((uint32_t)flashend); + } + if (offset < EEPROM_SIZE) { + while (p <= end) { + val = *p++; + if ((val & 255) == offset) data = val >> 8; + } + } + return data; +} + +static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) { + // with great power comes great responsibility.... + uint32_t stat; + *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC); + *(uint32_t *)&(FTFA->FCCOB7) = data; + __disable_irq(); + (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT)); + __enable_irq(); + stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL); + if (stat) { + FTFA->FSTAT = stat; + } + MCM->PLACR |= MCM_PLACR_CFCC; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t data) { + uint32_t offset = (uint32_t)addr; + const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend); + uint32_t i, val, flashaddr; + uint16_t do_flash_cmd[] = {0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770}; + uint8_t buf[EEPROM_SIZE]; + + if (offset >= EEPROM_SIZE) return; + if (!end) { + eeprom_initialize(); + end = (const uint16_t *)((uint32_t)flashend); + } + if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) { + val = (data << 8) | offset; + flashaddr = (uint32_t)end; + flashend = flashaddr; + if ((flashaddr & 2) == 0) { + val |= 0xFFFF0000; + } else { + val <<= 16; + val |= 0x0000FFFF; + } + flash_write(do_flash_cmd, flashaddr, val); + } else { + for (i = 0; i < EEPROM_SIZE; i++) { + buf[i] = 0xFF; + } + val = 0; + for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) { + val = *p; + if ((val & 255) < EEPROM_SIZE) { + buf[val & 255] = val >> 8; + } + } + buf[offset] = data; + for (flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) { + *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr; + __disable_irq(); + (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT)); + __enable_irq(); + val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL); + ; + if (val) FTFA->FSTAT = val; + MCM->PLACR |= MCM_PLACR_CFCC; + } + flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); + for (i = 0; i < EEPROM_SIZE; i++) { + if (buf[i] == 0xFF) continue; + if ((flashaddr & 2) == 0) { + val = (buf[i] << 8) | i; + } else { + val = val | (buf[i] << 24) | (i << 16); + flash_write(do_flash_cmd, flashaddr, val); + } + flashaddr += 2; + } + flashend = flashaddr; + if ((flashaddr & 2)) { + val |= 0xFFFF0000; + flash_write(do_flash_cmd, flashaddr, val); + } + } +} + +/* +void do_flash_cmd(volatile uint8_t *fstat) +{ + *fstat = 0x80; + while ((*fstat & 0x80) == 0) ; // wait +} +00000000 : + 0: 2380 movs r3, #128 ; 0x80 + 2: 7003 strb r3, [r0, #0] + 4: 7803 ldrb r3, [r0, #0] + 6: b25b sxtb r3, r3 + 8: 2b00 cmp r3, #0 + a: dafb bge.n 4 + c: 4770 bx lr +*/ + +uint16_t eeprom_read_word(const uint16_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24); +} + +void eeprom_read_block(void *buf, const void *addr, uint32_t len) { + const uint8_t *p = (const uint8_t *)addr; + uint8_t * dest = (uint8_t *)buf; + while (len--) { + *dest++ = eeprom_read_byte(p++); + } +} + +int eeprom_is_ready(void) { return 1; } + +void eeprom_write_word(uint16_t *addr, uint16_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_write_block(const void *buf, void *addr, uint32_t len) { + uint8_t * p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} + +#else +// No EEPROM supported, so emulate it + +# ifndef EEPROM_SIZE +# include "eeconfig.h" +# define EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO +# endif +__attribute__((aligned(4))) static uint8_t buffer[EEPROM_SIZE]; + +uint8_t eeprom_read_byte(const uint8_t *addr) { + uint32_t offset = (uint32_t)addr; + return buffer[offset]; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t value) { + uint32_t offset = (uint32_t)addr; + buffer[offset] = value; +} + +uint16_t eeprom_read_word(const uint16_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24); +} + +void eeprom_read_block(void *buf, const void *addr, size_t len) { + const uint8_t *p = (const uint8_t *)addr; + uint8_t * dest = (uint8_t *)buf; + while (len--) { + *dest++ = eeprom_read_byte(p++); + } +} + +void eeprom_write_word(uint16_t *addr, uint16_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_write_block(const void *buf, void *addr, size_t len) { + uint8_t * p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} + +#endif /* chip selection */ +// The update functions just calls write for now, but could probably be optimized + +void eeprom_update_byte(uint8_t *addr, uint8_t value) { eeprom_write_byte(addr, value); } + +void eeprom_update_word(uint16_t *addr, uint16_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_update_dword(uint32_t *addr, uint32_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_update_block(const void *buf, void *addr, size_t len) { + uint8_t * p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} diff --git a/platforms/chibios/flash_stm32.c b/platforms/chibios/flash_stm32.c new file mode 100644 index 0000000000..72c41b8b78 --- /dev/null +++ b/platforms/chibios/flash_stm32.c @@ -0,0 +1,208 @@ +/* + * This software is experimental and a work in progress. + * Under no circumstances should these files be used in relation to any critical system(s). + * Use of these files is at your own risk. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and + * https://github.com/leaflabs/libmaple + * + * Modifications for QMK and STM32F303 by Yiancar + */ + +#include +#include "flash_stm32.h" + +#if defined(STM32F1XX) +# define FLASH_SR_WRPERR FLASH_SR_WRPRTERR +#endif + +#if defined(MCU_GD32V) +/* GigaDevice GD32VF103 is a STM32F103 clone at heart. */ +# include "gd32v_compatibility.h" +#endif + +#if defined(STM32F4XX) +# define FLASH_SR_PGERR (FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR) + +# define FLASH_KEY1 0x45670123U +# define FLASH_KEY2 0xCDEF89ABU + +static uint8_t ADDR2PAGE(uint32_t Page_Address) { + switch (Page_Address) { + case 0x08000000 ... 0x08003FFF: + return 0; + case 0x08004000 ... 0x08007FFF: + return 1; + case 0x08008000 ... 0x0800BFFF: + return 2; + case 0x0800C000 ... 0x0800FFFF: + return 3; + } + + // TODO: bad times... + return 7; +} +#endif + +/* Delay definition */ +#define EraseTimeout ((uint32_t)0x00000FFF) +#define ProgramTimeout ((uint32_t)0x0000001F) + +#define ASSERT(exp) (void)((0)) + +/** + * @brief Inserts a time delay. + * @param None + * @retval None + */ +static void delay(void) { + __IO uint32_t i = 0; + for (i = 0xFF; i != 0; i--) { + } +} + +/** + * @brief Returns the FLASH Status. + * @param None + * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG, + * FLASH_ERROR_WRP or FLASH_COMPLETE + */ +FLASH_Status FLASH_GetStatus(void) { + if ((FLASH->SR & FLASH_SR_BSY) == FLASH_SR_BSY) return FLASH_BUSY; + + if ((FLASH->SR & FLASH_SR_PGERR) != 0) return FLASH_ERROR_PG; + + if ((FLASH->SR & FLASH_SR_WRPERR) != 0) return FLASH_ERROR_WRP; + +#if defined(FLASH_OBR_OPTERR) + if ((FLASH->SR & FLASH_OBR_OPTERR) != 0) return FLASH_ERROR_OPT; +#endif + + return FLASH_COMPLETE; +} + +/** + * @brief Waits for a Flash operation to complete or a TIMEOUT to occur. + * @param Timeout: FLASH progamming Timeout + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) { + FLASH_Status status; + + /* Check for the Flash Status */ + status = FLASH_GetStatus(); + /* Wait for a Flash operation to complete or a TIMEOUT to occur */ + while ((status == FLASH_BUSY) && (Timeout != 0x00)) { + delay(); + status = FLASH_GetStatus(); + Timeout--; + } + if (Timeout == 0) status = FLASH_TIMEOUT; + /* Return the operation status */ + return status; +} + +/** + * @brief Erases a specified FLASH page. + * @param Page_Address: The page address to be erased. + * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { + FLASH_Status status = FLASH_COMPLETE; + /* Check the parameters */ + ASSERT(IS_FLASH_ADDRESS(Page_Address)); + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + + if (status == FLASH_COMPLETE) { + /* if the previous operation is completed, proceed to erase the page */ +#if defined(FLASH_CR_SNB) + FLASH->CR &= ~FLASH_CR_SNB; + FLASH->CR |= FLASH_CR_SER | (ADDR2PAGE(Page_Address) << FLASH_CR_SNB_Pos); +#else + FLASH->CR |= FLASH_CR_PER; + FLASH->AR = Page_Address; +#endif + FLASH->CR |= FLASH_CR_STRT; + + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(EraseTimeout); + if (status != FLASH_TIMEOUT) { + /* if the erase operation is completed, disable the configured Bits */ +#if defined(FLASH_CR_SNB) + FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB); +#else + FLASH->CR &= ~FLASH_CR_PER; +#endif + } + FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR); + } + /* Return the Erase Status */ + return status; +} + +/** + * @brief Programs a half word at a specified address. + * @param Address: specifies the address to be programmed. + * @param Data: specifies the data to be programmed. + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) { + FLASH_Status status = FLASH_BAD_ADDRESS; + + if (IS_FLASH_ADDRESS(Address)) { + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + if (status == FLASH_COMPLETE) { + /* if the previous operation is completed, proceed to program the new data */ + +#if defined(FLASH_CR_PSIZE) + FLASH->CR &= ~FLASH_CR_PSIZE; + FLASH->CR |= FLASH_CR_PSIZE_0; +#endif + FLASH->CR |= FLASH_CR_PG; + *(__IO uint16_t*)Address = Data; + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + if (status != FLASH_TIMEOUT) { + /* if the program operation is completed, disable the PG Bit */ + FLASH->CR &= ~FLASH_CR_PG; + } + FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR); + } + } + return status; +} + +/** + * @brief Unlocks the FLASH Program Erase Controller. + * @param None + * @retval None + */ +void FLASH_Unlock(void) { + if (FLASH->CR & FLASH_CR_LOCK) { + /* Authorize the FPEC Access */ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } +} + +/** + * @brief Locks the FLASH Program Erase Controller. + * @param None + * @retval None + */ +void FLASH_Lock(void) { + /* Set the Lock Bit to lock the FPEC and the FCR */ + FLASH->CR |= FLASH_CR_LOCK; +} diff --git a/platforms/chibios/flash_stm32.h b/platforms/chibios/flash_stm32.h new file mode 100644 index 0000000000..6c66642ec5 --- /dev/null +++ b/platforms/chibios/flash_stm32.h @@ -0,0 +1,44 @@ +/* + * This software is experimental and a work in progress. + * Under no circumstances should these files be used in relation to any critical system(s). + * Use of these files is at your own risk. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and + * https://github.com/leaflabs/libmaple + * + * Modifications for QMK and STM32F303 by Yiancar + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef FLASH_STM32_MOCKED +extern uint8_t FlashBuf[MOCK_FLASH_SIZE]; +#endif + +typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status; + +#define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x0807FFFF)) + +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout); +FLASH_Status FLASH_ErasePage(uint32_t Page_Address); +FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); + +void FLASH_Unlock(void); +void FLASH_Lock(void); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/chibios/gd32v_compatibility.h b/platforms/chibios/gd32v_compatibility.h new file mode 100644 index 0000000000..f4dcfd8c55 --- /dev/null +++ b/platforms/chibios/gd32v_compatibility.h @@ -0,0 +1,120 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +/* GD32VF103 has the same API as STM32F103, but uses different names for literally the same thing. + * As of 23.7.2021 QMK is tailored to use STM32 defines/names, for compatibility sake + * we just redefine the GD32 names. */ + +/* Close your eyes kids. */ +#define MCU_STM32 + +/* AFIO redefines */ +#define MAPR PCF0 +#define AFIO_MAPR_USART1_REMAP AFIO_PCF0_USART0_REMAP +#define AFIO_MAPR_USART2_REMAP AFIO_PCF0_USART1_REMAP +#define AFIO_MAPR_USART3_REMAP_PARTIALREMAP AFIO_PCF0_USART2_REMAP_PARTIALREMAP +#define AFIO_MAPR_USART3_REMAP_FULLREMAP AFIO_PCF0_USART2_REMAP_FULLREMAP + +/* DMA redefines. */ +#define STM32_DMA_STREAM(stream) GD32_DMA_STREAM(stream) +#define STM32_DMA_STREAM_ID(peripheral, channel) GD32_DMA_STREAM_ID(peripheral - 1, channel - 1) +#define STM32_DMA_CR_DIR_M2P GD32_DMA_CTL_DIR_M2P +#define STM32_DMA_CR_PSIZE_WORD GD32_DMA_CTL_PWIDTH_WORD +#define STM32_DMA_CR_MSIZE_WORD GD32_DMA_CTL_MWIDTH_WORD +#define STM32_DMA_CR_MINC GD32_DMA_CTL_MNAGA +#define STM32_DMA_CR_CIRC GD32_DMA_CTL_CMEN +#define STM32_DMA_CR_PL GD32_DMA_CTL_PRIO +#define STM32_DMA_CR_CHSEL GD32_DMA_CTL_CHSEL +#define cr1 ctl0 +#define cr2 ctl1 +#define cr3 ctl2 +#define dier dmainten + +/* ADC redefines */ +#if HAL_USE_ADC +# define STM32_ADC_USE_ADC1 GD32_ADC_USE_ADC0 + +# define smpr1 sampt0 +# define smpr2 sampt1 +# define sqr1 rsq0 +# define sqr2 rsq1 +# define sqr3 rsq2 + +# define ADC_SMPR2_SMP_AN0 ADC_SAMPT1_SMP_SPT0 +# define ADC_SMPR2_SMP_AN1 ADC_SAMPT1_SMP_SPT1 +# define ADC_SMPR2_SMP_AN2 ADC_SAMPT1_SMP_SPT2 +# define ADC_SMPR2_SMP_AN3 ADC_SAMPT1_SMP_SPT3 +# define ADC_SMPR2_SMP_AN4 ADC_SAMPT1_SMP_SPT4 +# define ADC_SMPR2_SMP_AN5 ADC_SAMPT1_SMP_SPT5 +# define ADC_SMPR2_SMP_AN6 ADC_SAMPT1_SMP_SPT6 +# define ADC_SMPR2_SMP_AN7 ADC_SAMPT1_SMP_SPT7 +# define ADC_SMPR2_SMP_AN8 ADC_SAMPT1_SMP_SPT8 +# define ADC_SMPR2_SMP_AN9 ADC_SAMPT1_SMP_SPT9 + +# define ADC_SMPR1_SMP_AN10 ADC_SAMPT0_SMP_SPT10 +# define ADC_SMPR1_SMP_AN11 ADC_SAMPT0_SMP_SPT11 +# define ADC_SMPR1_SMP_AN12 ADC_SAMPT0_SMP_SPT12 +# define ADC_SMPR1_SMP_AN13 ADC_SAMPT0_SMP_SPT13 +# define ADC_SMPR1_SMP_AN14 ADC_SAMPT0_SMP_SPT14 +# define ADC_SMPR1_SMP_AN15 ADC_SAMPT0_SMP_SPT15 + +# define ADC_SQR3_SQ1_N ADC_RSQ2_RSQ1_N +#endif + +/* FLASH redefines */ +#if defined(EEPROM_ENABLE) +# define SR STAT +# define FLASH_SR_BSY FLASH_STAT_BUSY +# define FLASH_SR_PGERR FLASH_STAT_PGERR +# define FLASH_SR_EOP FLASH_STAT_ENDF +# define FLASH_SR_WRPRTERR FLASH_STAT_WPERR +# define FLASH_SR_WRPERR FLASH_SR_WRPRTERR +# define FLASH_OBR_OPTERR FLASH_OBSTAT_OBERR +# define AR ADDR +# define CR CTL +# define FLASH_CR_PER FLASH_CTL_PER +# define FLASH_CR_STRT FLASH_CTL_START +# define FLASH_CR_LOCK FLASH_CTL_LK +# define FLASH_CR_PG FLASH_CTL_PG +# define KEYR KEY +#endif + +/* Serial USART redefines. */ +#if HAL_USE_SERIAL +# if !defined(SERIAL_USART_CR1) +# define SERIAL_USART_CR1 (USART_CTL0_PCEN | USART_CTL0_PM | USART_CTL0_WL) // parity enable, odd parity, 9 bit length +# endif +# if !defined(SERIAL_USART_CR2) +# define SERIAL_USART_CR2 (USART_CTL1_STB_1) // 2 stop bits +# endif +# if !defined(SERIAL_USART_CR3) +# define SERIAL_USART_CR3 0x0 +# endif +# define USART_CR3_HDSEL USART_CTL2_HDEN +# define CCR CHCV +#endif + +/* SPI redefines. */ +#if HAL_USE_SPI +# define SPI_CR1_LSBFIRST SPI_CTL0_LF +# define SPI_CR1_CPHA SPI_CTL0_CKPH +# define SPI_CR1_CPOL SPI_CTL0_CKPL +# define SPI_CR1_BR_0 SPI_CTL0_PSC_0 +# define SPI_CR1_BR_1 SPI_CTL0_PSC_1 +# define SPI_CR1_BR_2 SPI_CTL0_PSC_2 +#endif diff --git a/platforms/chibios/gpio.h b/platforms/chibios/gpio.h new file mode 100644 index 0000000000..4d057f1cab --- /dev/null +++ b/platforms/chibios/gpio.h @@ -0,0 +1,50 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include "pin_defs.h" + +typedef ioline_t pin_t; + +/* Operation of GPIO by pin. */ + +#define setPinInput(pin) palSetLineMode(pin, PAL_MODE_INPUT) +#define setPinInputHigh(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLUP) +#define setPinInputLow(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN) +#define setPinOutput(pin) palSetLineMode(pin, PAL_MODE_OUTPUT_PUSHPULL) + +#define writePinHigh(pin) palSetLine(pin) +#define writePinLow(pin) palClearLine(pin) +#define writePin(pin, level) ((level) ? (writePinHigh(pin)) : (writePinLow(pin))) + +#define readPin(pin) palReadLine(pin) + +#define togglePin(pin) palToggleLine(pin) + +/* Operation of GPIO by port. */ + +typedef uint16_t port_data_t; + +#define readPort(pin) palReadPort(PAL_PORT(pin)) + +#define setPortBitInput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT) +#define setPortBitInputHigh(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLUP) +#define setPortBitInputLow(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLDOWN) +#define setPortBitOutput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_OUTPUT_PUSHPULL) + +#define writePortBitLow(pin, bit) palClearLine(PAL_LINE(PAL_PORT(pin), bit)) +#define writePortBitHigh(pin, bit) palSetLine(PAL_LINE(PAL_PORT(pin), bit)) diff --git a/platforms/chibios/pin_defs.h b/platforms/chibios/pin_defs.h new file mode 100644 index 0000000000..c03f8de0c2 --- /dev/null +++ b/platforms/chibios/pin_defs.h @@ -0,0 +1,323 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// Defines mapping for Proton C replacement +#ifdef CONVERT_TO_PROTON_C +// Left side (front) +# define D3 PAL_LINE(GPIOA, 9) +# define D2 PAL_LINE(GPIOA, 10) +// GND +// GND +# define D1 PAL_LINE(GPIOB, 7) +# define D0 PAL_LINE(GPIOB, 6) +# define D4 PAL_LINE(GPIOB, 5) +# define C6 PAL_LINE(GPIOB, 4) +# define D7 PAL_LINE(GPIOB, 3) +# define E6 PAL_LINE(GPIOB, 2) +# define B4 PAL_LINE(GPIOB, 1) +# define B5 PAL_LINE(GPIOB, 0) + +// Right side (front) +// RAW +// GND +// RESET +// VCC +# define F4 PAL_LINE(GPIOA, 2) +# define F5 PAL_LINE(GPIOA, 1) +# define F6 PAL_LINE(GPIOA, 0) +# define F7 PAL_LINE(GPIOB, 8) +# define B1 PAL_LINE(GPIOB, 13) +# define B3 PAL_LINE(GPIOB, 14) +# define B2 PAL_LINE(GPIOB, 15) +# define B6 PAL_LINE(GPIOB, 9) + +// LEDs (only D5/C13 uses an actual LED) +# ifdef CONVERT_TO_PROTON_C_RXLED +# define D5 PAL_LINE(GPIOC, 14) +# define B0 PAL_LINE(GPIOC, 13) +# else +# define D5 PAL_LINE(GPIOC, 13) +# define B0 PAL_LINE(GPIOC, 14) +# endif +#else +# define A0 PAL_LINE(GPIOA, 0) +# define A1 PAL_LINE(GPIOA, 1) +# define A2 PAL_LINE(GPIOA, 2) +# define A3 PAL_LINE(GPIOA, 3) +# define A4 PAL_LINE(GPIOA, 4) +# define A5 PAL_LINE(GPIOA, 5) +# define A6 PAL_LINE(GPIOA, 6) +# define A7 PAL_LINE(GPIOA, 7) +# define A8 PAL_LINE(GPIOA, 8) +# define A9 PAL_LINE(GPIOA, 9) +# define A10 PAL_LINE(GPIOA, 10) +# define A11 PAL_LINE(GPIOA, 11) +# define A12 PAL_LINE(GPIOA, 12) +# define A13 PAL_LINE(GPIOA, 13) +# define A14 PAL_LINE(GPIOA, 14) +# define A15 PAL_LINE(GPIOA, 15) +# define A16 PAL_LINE(GPIOA, 16) +# define A17 PAL_LINE(GPIOA, 17) +# define A18 PAL_LINE(GPIOA, 18) +# define A19 PAL_LINE(GPIOA, 19) +# define A20 PAL_LINE(GPIOA, 20) +# define A21 PAL_LINE(GPIOA, 21) +# define A22 PAL_LINE(GPIOA, 22) +# define A23 PAL_LINE(GPIOA, 23) +# define A24 PAL_LINE(GPIOA, 24) +# define A25 PAL_LINE(GPIOA, 25) +# define A26 PAL_LINE(GPIOA, 26) +# define A27 PAL_LINE(GPIOA, 27) +# define A28 PAL_LINE(GPIOA, 28) +# define A29 PAL_LINE(GPIOA, 29) +# define A30 PAL_LINE(GPIOA, 30) +# define A31 PAL_LINE(GPIOA, 31) +# define A32 PAL_LINE(GPIOA, 32) +# define B0 PAL_LINE(GPIOB, 0) +# define B1 PAL_LINE(GPIOB, 1) +# define B2 PAL_LINE(GPIOB, 2) +# define B3 PAL_LINE(GPIOB, 3) +# define B4 PAL_LINE(GPIOB, 4) +# define B5 PAL_LINE(GPIOB, 5) +# define B6 PAL_LINE(GPIOB, 6) +# define B7 PAL_LINE(GPIOB, 7) +# define B8 PAL_LINE(GPIOB, 8) +# define B9 PAL_LINE(GPIOB, 9) +# define B10 PAL_LINE(GPIOB, 10) +# define B11 PAL_LINE(GPIOB, 11) +# define B12 PAL_LINE(GPIOB, 12) +# define B13 PAL_LINE(GPIOB, 13) +# define B14 PAL_LINE(GPIOB, 14) +# define B15 PAL_LINE(GPIOB, 15) +# define B16 PAL_LINE(GPIOB, 16) +# define B17 PAL_LINE(GPIOB, 17) +# define B18 PAL_LINE(GPIOB, 18) +# define B19 PAL_LINE(GPIOB, 19) +# define B20 PAL_LINE(GPIOB, 20) +# define B21 PAL_LINE(GPIOB, 21) +# define B22 PAL_LINE(GPIOB, 22) +# define B23 PAL_LINE(GPIOB, 23) +# define B24 PAL_LINE(GPIOB, 24) +# define B25 PAL_LINE(GPIOB, 25) +# define B26 PAL_LINE(GPIOB, 26) +# define B27 PAL_LINE(GPIOB, 27) +# define B28 PAL_LINE(GPIOB, 28) +# define B29 PAL_LINE(GPIOB, 29) +# define B30 PAL_LINE(GPIOB, 30) +# define B31 PAL_LINE(GPIOB, 31) +# define B32 PAL_LINE(GPIOB, 32) +# define C0 PAL_LINE(GPIOC, 0) +# define C1 PAL_LINE(GPIOC, 1) +# define C2 PAL_LINE(GPIOC, 2) +# define C3 PAL_LINE(GPIOC, 3) +# define C4 PAL_LINE(GPIOC, 4) +# define C5 PAL_LINE(GPIOC, 5) +# define C6 PAL_LINE(GPIOC, 6) +# define C7 PAL_LINE(GPIOC, 7) +# define C8 PAL_LINE(GPIOC, 8) +# define C9 PAL_LINE(GPIOC, 9) +# define C10 PAL_LINE(GPIOC, 10) +# define C11 PAL_LINE(GPIOC, 11) +# define C12 PAL_LINE(GPIOC, 12) +# define C13 PAL_LINE(GPIOC, 13) +# define C14 PAL_LINE(GPIOC, 14) +# define C15 PAL_LINE(GPIOC, 15) +# define C16 PAL_LINE(GPIOC, 16) +# define C17 PAL_LINE(GPIOC, 17) +# define C18 PAL_LINE(GPIOC, 18) +# define C19 PAL_LINE(GPIOC, 19) +# define C20 PAL_LINE(GPIOC, 20) +# define C21 PAL_LINE(GPIOC, 21) +# define C22 PAL_LINE(GPIOC, 22) +# define C23 PAL_LINE(GPIOC, 23) +# define C24 PAL_LINE(GPIOC, 24) +# define C25 PAL_LINE(GPIOC, 25) +# define C26 PAL_LINE(GPIOC, 26) +# define C27 PAL_LINE(GPIOC, 27) +# define C28 PAL_LINE(GPIOC, 28) +# define C29 PAL_LINE(GPIOC, 29) +# define C30 PAL_LINE(GPIOC, 30) +# define C31 PAL_LINE(GPIOC, 31) +# define C32 PAL_LINE(GPIOC, 32) +# define D0 PAL_LINE(GPIOD, 0) +# define D1 PAL_LINE(GPIOD, 1) +# define D2 PAL_LINE(GPIOD, 2) +# define D3 PAL_LINE(GPIOD, 3) +# define D4 PAL_LINE(GPIOD, 4) +# define D5 PAL_LINE(GPIOD, 5) +# define D6 PAL_LINE(GPIOD, 6) +# define D7 PAL_LINE(GPIOD, 7) +# define D8 PAL_LINE(GPIOD, 8) +# define D9 PAL_LINE(GPIOD, 9) +# define D10 PAL_LINE(GPIOD, 10) +# define D11 PAL_LINE(GPIOD, 11) +# define D12 PAL_LINE(GPIOD, 12) +# define D13 PAL_LINE(GPIOD, 13) +# define D14 PAL_LINE(GPIOD, 14) +# define D15 PAL_LINE(GPIOD, 15) +# define D16 PAL_LINE(GPIOD, 16) +# define D17 PAL_LINE(GPIOD, 17) +# define D18 PAL_LINE(GPIOD, 18) +# define D19 PAL_LINE(GPIOD, 19) +# define D20 PAL_LINE(GPIOD, 20) +# define D21 PAL_LINE(GPIOD, 21) +# define D22 PAL_LINE(GPIOD, 22) +# define D23 PAL_LINE(GPIOD, 23) +# define D24 PAL_LINE(GPIOD, 24) +# define D25 PAL_LINE(GPIOD, 25) +# define D26 PAL_LINE(GPIOD, 26) +# define D27 PAL_LINE(GPIOD, 27) +# define D28 PAL_LINE(GPIOD, 28) +# define D29 PAL_LINE(GPIOD, 29) +# define D30 PAL_LINE(GPIOD, 30) +# define D31 PAL_LINE(GPIOD, 31) +# define D32 PAL_LINE(GPIOD, 32) +# define E0 PAL_LINE(GPIOE, 0) +# define E1 PAL_LINE(GPIOE, 1) +# define E2 PAL_LINE(GPIOE, 2) +# define E3 PAL_LINE(GPIOE, 3) +# define E4 PAL_LINE(GPIOE, 4) +# define E5 PAL_LINE(GPIOE, 5) +# define E6 PAL_LINE(GPIOE, 6) +# define E7 PAL_LINE(GPIOE, 7) +# define E8 PAL_LINE(GPIOE, 8) +# define E9 PAL_LINE(GPIOE, 9) +# define E10 PAL_LINE(GPIOE, 10) +# define E11 PAL_LINE(GPIOE, 11) +# define E12 PAL_LINE(GPIOE, 12) +# define E13 PAL_LINE(GPIOE, 13) +# define E14 PAL_LINE(GPIOE, 14) +# define E15 PAL_LINE(GPIOE, 15) +# define E16 PAL_LINE(GPIOE, 16) +# define E17 PAL_LINE(GPIOE, 17) +# define E18 PAL_LINE(GPIOE, 18) +# define E19 PAL_LINE(GPIOE, 19) +# define E20 PAL_LINE(GPIOE, 20) +# define E21 PAL_LINE(GPIOE, 21) +# define E22 PAL_LINE(GPIOE, 22) +# define E23 PAL_LINE(GPIOE, 23) +# define E24 PAL_LINE(GPIOE, 24) +# define E25 PAL_LINE(GPIOE, 25) +# define E26 PAL_LINE(GPIOE, 26) +# define E27 PAL_LINE(GPIOE, 27) +# define E28 PAL_LINE(GPIOE, 28) +# define E29 PAL_LINE(GPIOE, 29) +# define E30 PAL_LINE(GPIOE, 30) +# define E31 PAL_LINE(GPIOE, 31) +# define E32 PAL_LINE(GPIOE, 32) +# define F0 PAL_LINE(GPIOF, 0) +# define F1 PAL_LINE(GPIOF, 1) +# define F2 PAL_LINE(GPIOF, 2) +# define F3 PAL_LINE(GPIOF, 3) +# define F4 PAL_LINE(GPIOF, 4) +# define F5 PAL_LINE(GPIOF, 5) +# define F6 PAL_LINE(GPIOF, 6) +# define F7 PAL_LINE(GPIOF, 7) +# define F8 PAL_LINE(GPIOF, 8) +# define F9 PAL_LINE(GPIOF, 9) +# define F10 PAL_LINE(GPIOF, 10) +# define F11 PAL_LINE(GPIOF, 11) +# define F12 PAL_LINE(GPIOF, 12) +# define F13 PAL_LINE(GPIOF, 13) +# define F14 PAL_LINE(GPIOF, 14) +# define F15 PAL_LINE(GPIOF, 15) +# define G0 PAL_LINE(GPIOG, 0) +# define G1 PAL_LINE(GPIOG, 1) +# define G2 PAL_LINE(GPIOG, 2) +# define G3 PAL_LINE(GPIOG, 3) +# define G4 PAL_LINE(GPIOG, 4) +# define G5 PAL_LINE(GPIOG, 5) +# define G6 PAL_LINE(GPIOG, 6) +# define G7 PAL_LINE(GPIOG, 7) +# define G8 PAL_LINE(GPIOG, 8) +# define G9 PAL_LINE(GPIOG, 9) +# define G10 PAL_LINE(GPIOG, 10) +# define G11 PAL_LINE(GPIOG, 11) +# define G12 PAL_LINE(GPIOG, 12) +# define G13 PAL_LINE(GPIOG, 13) +# define G14 PAL_LINE(GPIOG, 14) +# define G15 PAL_LINE(GPIOG, 15) +# define H0 PAL_LINE(GPIOH, 0) +# define H1 PAL_LINE(GPIOH, 1) +# define H2 PAL_LINE(GPIOH, 2) +# define H3 PAL_LINE(GPIOH, 3) +# define H4 PAL_LINE(GPIOH, 4) +# define H5 PAL_LINE(GPIOH, 5) +# define H6 PAL_LINE(GPIOH, 6) +# define H7 PAL_LINE(GPIOH, 7) +# define H8 PAL_LINE(GPIOH, 8) +# define H9 PAL_LINE(GPIOH, 9) +# define H10 PAL_LINE(GPIOH, 10) +# define H11 PAL_LINE(GPIOH, 11) +# define H12 PAL_LINE(GPIOH, 12) +# define H13 PAL_LINE(GPIOH, 13) +# define H14 PAL_LINE(GPIOH, 14) +# define H15 PAL_LINE(GPIOH, 15) +# define I0 PAL_LINE(GPIOI, 0) +# define I1 PAL_LINE(GPIOI, 1) +# define I2 PAL_LINE(GPIOI, 2) +# define I3 PAL_LINE(GPIOI, 3) +# define I4 PAL_LINE(GPIOI, 4) +# define I5 PAL_LINE(GPIOI, 5) +# define I6 PAL_LINE(GPIOI, 6) +# define I7 PAL_LINE(GPIOI, 7) +# define I8 PAL_LINE(GPIOI, 8) +# define I9 PAL_LINE(GPIOI, 9) +# define I10 PAL_LINE(GPIOI, 10) +# define I11 PAL_LINE(GPIOI, 11) +# define I12 PAL_LINE(GPIOI, 12) +# define I13 PAL_LINE(GPIOI, 13) +# define I14 PAL_LINE(GPIOI, 14) +# define I15 PAL_LINE(GPIOI, 15) +# define J0 PAL_LINE(GPIOJ, 0) +# define J1 PAL_LINE(GPIOJ, 1) +# define J2 PAL_LINE(GPIOJ, 2) +# define J3 PAL_LINE(GPIOJ, 3) +# define J4 PAL_LINE(GPIOJ, 4) +# define J5 PAL_LINE(GPIOJ, 5) +# define J6 PAL_LINE(GPIOJ, 6) +# define J7 PAL_LINE(GPIOJ, 7) +# define J8 PAL_LINE(GPIOJ, 8) +# define J9 PAL_LINE(GPIOJ, 9) +# define J10 PAL_LINE(GPIOJ, 10) +# define J11 PAL_LINE(GPIOJ, 11) +# define J12 PAL_LINE(GPIOJ, 12) +# define J13 PAL_LINE(GPIOJ, 13) +# define J14 PAL_LINE(GPIOJ, 14) +# define J15 PAL_LINE(GPIOJ, 15) +// Keyboards can `#define KEYBOARD_REQUIRES_GPIOK` if they need to access GPIO-K pins. These conflict with a whole +// bunch of layout definitions, so it's intentionally left out unless absolutely required -- in that case, the +// keyboard designer should use a different symbol when defining their layout macros. +# ifdef KEYBOARD_REQUIRES_GPIOK +# define K0 PAL_LINE(GPIOK, 0) +# define K1 PAL_LINE(GPIOK, 1) +# define K2 PAL_LINE(GPIOK, 2) +# define K3 PAL_LINE(GPIOK, 3) +# define K4 PAL_LINE(GPIOK, 4) +# define K5 PAL_LINE(GPIOK, 5) +# define K6 PAL_LINE(GPIOK, 6) +# define K7 PAL_LINE(GPIOK, 7) +# define K8 PAL_LINE(GPIOK, 8) +# define K9 PAL_LINE(GPIOK, 9) +# define K10 PAL_LINE(GPIOK, 10) +# define K11 PAL_LINE(GPIOK, 11) +# define K12 PAL_LINE(GPIOK, 12) +# define K13 PAL_LINE(GPIOK, 13) +# define K14 PAL_LINE(GPIOK, 14) +# define K15 PAL_LINE(GPIOK, 15) +# endif +#endif diff --git a/platforms/chibios/platform.c b/platforms/chibios/platform.c new file mode 100644 index 0000000000..d4a229f278 --- /dev/null +++ b/platforms/chibios/platform.c @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform_deps.h" + +void platform_setup(void) { + halInit(); + chSysInit(); +} \ No newline at end of file diff --git a/platforms/chibios/platform.mk b/platforms/chibios/platform.mk new file mode 100644 index 0000000000..6b298732c2 --- /dev/null +++ b/platforms/chibios/platform.mk @@ -0,0 +1,436 @@ +# Hey Emacs, this is a -*- makefile -*- +############################################################################## +# Architecture or project specific options +# + +# Stack size to be allocated to the Cortex-M process stack. This stack is +# the stack used by the main() thread. +ifeq ($(USE_PROCESS_STACKSIZE),) + USE_PROCESS_STACKSIZE = 0x800 +endif + +# Stack size to the allocated to the Cortex-M main/exceptions stack. This +# stack is used for processing interrupts and exceptions. +ifeq ($(USE_EXCEPTIONS_STACKSIZE),) + USE_EXCEPTIONS_STACKSIZE = 0x400 +endif + +# +# Architecture or project specific options +############################################################################## + +############################################################################## +# Project, sources and paths +# + +# Imported source files and paths +OPT_OS = chibios +CHIBIOS = $(TOP_DIR)/lib/chibios +CHIBIOS_CONTRIB = $(TOP_DIR)/lib/chibios-contrib + +# +# Startup, Port and Platform support selection +############################################################################## + +ifeq ($(strip $(MCU)), risc-v) + # RISC-V Support + # As of 7.4.2021 there is only one supported RISC-V platform in Chibios-Contrib, + # therefore all required settings are hard-coded + STARTUP_MK = $(CHIBIOS_CONTRIB)/os/common/startup/RISCV-ECLIC/compilers/GCC/mk/startup_$(MCU_STARTUP).mk + PORT_V = $(CHIBIOS_CONTRIB)/os/common/ports/RISCV-ECLIC/compilers/GCC/mk/port.mk + RULESPATH = $(CHIBIOS_CONTRIB)/os/common/startup/RISCV-ECLIC/compilers/GCC + PLATFORM_MK = $(CHIBIOS_CONTRIB)/os/hal/ports/GD/GD32VF103/platform.mk +else + # ARM Support + CHIBIOS_PORT ?= + ifeq ("$(CHIBIOS_PORT)","") + CHIBIOS_PORT = ARMv$(ARMV)-M + endif + + # Startup files. Try a few different locations, for compability with old versions and + # for things hardware in the contrib repository + STARTUP_MK = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk + ifeq ("$(wildcard $(STARTUP_MK))","") + STARTUP_MK = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk + ifeq ("$(wildcard $(STARTUP_MK))","") + STARTUP_MK = $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk + endif + endif + + # Port files. Try a few different locations, for compability with old versions and + # for things hardware in the contrib repository + PORT_V = $(CHIBIOS)/os/common/ports/$(CHIBIOS_PORT)/compilers/GCC/mk/port.mk + ifeq ("$(wildcard $(PORT_V))","") + PORT_V = $(CHIBIOS)/os/rt/ports/ARMCMx/compilers/GCC/mk/port_v$(ARMV)m.mk + ifeq ("$(wildcard $(PORT_V))","") + PORT_V = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v$(ARMV)m.mk + endif + endif + + # Rules location. Try a few different locations, for compability with old versions and + # for things hardware in the contrib repository + RULESPATH = $(CHIBIOS)/os/common/ports/$(CHIBIOS_PORT)/compilers/GCC + ifeq ("$(wildcard $(RULESPATH)/rules.mk)","") + RULESPATH = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC + ifeq ("$(wildcard $(RULESPATH)/rules.mk)","") + RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC + endif + endif +endif + +ifeq ("$(PLATFORM_NAME)","") + PLATFORM_NAME = platform +endif + +ifeq ("$(wildcard $(PLATFORM_MK))","") + PLATFORM_MK = $(CHIBIOS)/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)/$(PLATFORM_NAME).mk + ifeq ("$(wildcard $(PLATFORM_MK))","") + PLATFORM_MK = $(CHIBIOS_CONTRIB)/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)/$(PLATFORM_NAME).mk + endif +endif + +include $(STARTUP_MK) +include $(PORT_V) +include $(PLATFORM_MK) + +# +# Board support selection. +############################################################################## + +BOARD_MK := + +ifneq ("$(wildcard $(KEYBOARD_PATH_5)/boards/$(BOARD)/board.mk)","") + BOARD_PATH = $(KEYBOARD_PATH_5) + BOARD_MK += $(KEYBOARD_PATH_5)/boards/$(BOARD)/board.mk +else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/boards/$(BOARD)/board.mk)","") + BOARD_PATH = $(KEYBOARD_PATH_4) + BOARD_MK += $(KEYBOARD_PATH_4)/boards/$(BOARD)/board.mk +else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/boards/$(BOARD)/board.mk)","") + BOARD_PATH = $(KEYBOARD_PATH_3) + BOARD_MK += $(KEYBOARD_PATH_3)/boards/$(BOARD)/board.mk +else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/boards/$(BOARD)/board.mk)","") + BOARD_PATH = $(KEYBOARD_PATH_2) + BOARD_MK += $(KEYBOARD_PATH_2)/boards/$(BOARD)/board.mk +else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/boards/$(BOARD)/board.mk)","") + BOARD_PATH = $(KEYBOARD_PATH_1) + BOARD_MK += $(KEYBOARD_PATH_1)/boards/$(BOARD)/board.mk +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/board/board.mk)","") + BOARD_PATH = $(TOP_DIR)/platforms/chibios/boards/$(BOARD) + BOARD_MK += $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/board/board.mk + KEYBOARD_PATHS += $(BOARD_PATH)/configs + ifneq ("$(wildcard $(BOARD_PATH)/rules.mk)","") + include $(BOARD_PATH)/rules.mk + endif +endif + +ifeq ("$(wildcard $(BOARD_MK))","") + BOARD_MK = $(CHIBIOS)/os/hal/boards/$(BOARD)/board.mk + ifeq ("$(wildcard $(BOARD_MK))","") + BOARD_MK = $(CHIBIOS_CONTRIB)/os/hal/boards/$(BOARD)/board.mk + endif +endif + +include $(BOARD_MK) + +# +# Bootloader selection. +############################################################################## + +# Set bootloader address if supplied. +ifdef STM32_BOOTLOADER_ADDRESS + OPT_DEFS += -DSTM32_BOOTLOADER_ADDRESS=$(STM32_BOOTLOADER_ADDRESS) +endif + +# Work out if we need to set up the include for the bootloader definitions +ifneq ("$(wildcard $(KEYBOARD_PATH_5)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_5)/bootloader_defs.h +else ifneq ("$(wildcard $(KEYBOARD_PATH_5)/boards/$(BOARD)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_5)/boards/$(BOARD)/bootloader_defs.h +else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_4)/bootloader_defs.h +else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/boards/$(BOARD)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_4)/boards/$(BOARD)/bootloader_defs.h +else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_3)/bootloader_defs.h +else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/boards/$(BOARD)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_3)/boards/$(BOARD)/bootloader_defs.h +else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_2)/bootloader_defs.h +else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/boards/$(BOARD)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_2)/boards/$(BOARD)/bootloader_defs.h +else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_1)/bootloader_defs.h +else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/boards/$(BOARD)/bootloader_defs.h)","") + OPT_DEFS += -include $(KEYBOARD_PATH_1)/boards/$(BOARD)/bootloader_defs.h +else ifneq ("$(wildcard $(BOARD_PATH)/configs/bootloader_defs.h)","") + OPT_DEFS += -include $(BOARD_PATH)/configs/bootloader_defs.h +endif + +# +# ChibiOS config selection. +############################################################################## + +# Work out the config file directories +ifneq ("$(wildcard $(KEYBOARD_PATH_5)/chconf.h)","") + CHCONFDIR = $(KEYBOARD_PATH_5) +else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/chconf.h)","") + CHCONFDIR = $(KEYBOARD_PATH_4) +else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/chconf.h)","") + CHCONFDIR = $(KEYBOARD_PATH_3) +else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/chconf.h)","") + CHCONFDIR = $(KEYBOARD_PATH_2) +else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/chconf.h)","") + CHCONFDIR = $(KEYBOARD_PATH_1) +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/chconf.h)","") + CHCONFDIR = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs +else ifneq ("$(wildcard $(TOP_DIR)/platforms/boards/chibios/common/configs/chconf.h)","") + CHCONFDIR = $(TOP_DIR)/platforms/chibios/boards/common/configs +endif + +# +# HAL config selection. +############################################################################## + +ifneq ("$(wildcard $(KEYBOARD_PATH_5)/halconf.h)","") + HALCONFDIR = $(KEYBOARD_PATH_5) +else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/halconf.h)","") + HALCONFDIR = $(KEYBOARD_PATH_4) +else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/halconf.h)","") + HALCONFDIR = $(KEYBOARD_PATH_3) +else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/halconf.h)","") + HALCONFDIR = $(KEYBOARD_PATH_2) +else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/halconf.h)","") + HALCONFDIR = $(KEYBOARD_PATH_1) +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/halconf.h)","") + HALCONFDIR = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/common/configs/halconf.h)","") + HALCONFDIR = $(TOP_DIR)/platforms/chibios/boards/common/configs +endif + +# +# Linker script selection. +############################################################################## + +ifneq ("$(wildcard $(KEYBOARD_PATH_5)/ld/$(MCU_LDSCRIPT).ld)","") + LDSCRIPT = $(KEYBOARD_PATH_5)/ld/$(MCU_LDSCRIPT).ld +else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/ld/$(MCU_LDSCRIPT).ld)","") + LDSCRIPT = $(KEYBOARD_PATH_4)/ld/$(MCU_LDSCRIPT).ld +else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/ld/$(MCU_LDSCRIPT).ld)","") + LDSCRIPT = $(KEYBOARD_PATH_3)/ld/$(MCU_LDSCRIPT).ld +else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld)","") + LDSCRIPT = $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld +else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld)","") + LDSCRIPT = $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT).ld)","") + LDFLAGS += -L$(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld + LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT).ld +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT).ld)","") + LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT).ld +else ifneq ("$(wildcard $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld)","") + LDSCRIPT = $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld + USE_CHIBIOS_CONTRIB = yes +else + LDSCRIPT = $(STARTUPLD)/$(MCU_LDSCRIPT).ld +endif + +# +# Include ChibiOS makefiles. +############################################################################## + +# HAL-OSAL files (optional). +include $(CHIBIOS)/os/hal/hal.mk +-include $(CHIBIOS)/os/hal/osal/rt/osal.mk # ChibiOS <= 19.x +-include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk # ChibiOS >= 20.x +# RTOS files (optional). +include $(CHIBIOS)/os/rt/rt.mk +# Other files (optional). +include $(CHIBIOS)/os/hal/lib/streams/streams.mk + +PLATFORM_SRC = \ + $(STARTUPSRC) \ + $(KERNSRC) \ + $(PORTSRC) \ + $(OSALSRC) \ + $(HALSRC) \ + $(PLATFORMSRC) \ + $(BOARDSRC) \ + $(STREAMSSRC) \ + $(CHIBIOS)/os/various/syscalls.c \ + $(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \ + $(PLATFORM_COMMON_DIR)/wait.c + +# Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise. +QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM) + +PLATFORM_SRC := $(patsubst $(TOP_DIR)/%,%,$(PLATFORM_SRC)) + +EXTRAINCDIRS += $(CHIBIOS)/os/license $(CHIBIOS)/os/oslib/include \ + $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs \ + $(TOP_DIR)/platforms/chibios/boards/common/configs \ + $(HALCONFDIR) $(CHCONFDIR) \ + $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ + $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \ + $(STREAMSINC) $(CHIBIOS)/os/various $(COMMON_VPATH) + +# +# ChibiOS-Contrib +############################################################################## + +# Work out if we're using ChibiOS-Contrib by checking if halconf_community.h exists +ifneq ("$(wildcard $(KEYBOARD_PATH_5)/halconf_community.h)","") + USE_CHIBIOS_CONTRIB = yes +else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/halconf_community.h)","") + USE_CHIBIOS_CONTRIB = yes +else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/halconf_community.h)","") + USE_CHIBIOS_CONTRIB = yes +else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/halconf_community.h)","") + USE_CHIBIOS_CONTRIB = yes +else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/halconf_community.h)","") + USE_CHIBIOS_CONTRIB = yes +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/halconf_community.h)","") + USE_CHIBIOS_CONTRIB = yes +endif + +ifeq ($(strip $(USE_CHIBIOS_CONTRIB)),yes) + include $(CHIBIOS_CONTRIB)/os/hal/hal.mk + PLATFORM_SRC += $(PLATFORMSRC_CONTRIB) $(HALSRC_CONTRIB) + EXTRAINCDIRS += $(PLATFORMINC_CONTRIB) $(HALINC_CONTRIB) $(CHIBIOS_CONTRIB)/os/various +endif + +# +# Project, sources and paths +############################################################################## + +############################################################################## +# Injected configs +# +ifneq ("$(wildcard $(BOARD_PATH)/configs/config.h)","") + CONFIG_H += $(BOARD_PATH)/configs/config.h +endif +ifneq ("$(wildcard $(BOARD_PATH)/configs/post_config.h)","") + POST_CONFIG_H += $(BOARD_PATH)/configs/post_config.h +endif + +############################################################################## +# Compiler and Linker configuration +# + +# Use defined stack sizes of the main thread in linker scripts +LDSYMBOLS =--defsym=__process_stack_size__=$(USE_PROCESS_STACKSIZE),--defsym=__main_stack_size__=$(USE_EXCEPTIONS_STACKSIZE) + +# Shared Compiler flags for all toolchains +SHARED_CFLAGS = -fomit-frame-pointer \ + -ffunction-sections \ + -fdata-sections \ + -fno-common \ + -fshort-wchar + +# Shared Linker flags for all toolchains +SHARED_LDFLAGS = -T $(LDSCRIPT) \ + -Wl,$(LDSYMBOLS) \ + -Wl,--gc-sections \ + -nostartfiles + +ifeq ($(strip $(MCU)), risc-v) + # RISC-V toolchain specific configuration + # Find suitable GCC compiler + ifeq ($(strip $(TOOLCHAIN)),) + ifneq ($(shell which riscv32-unknown-elf-gcc 2>/dev/null),) + TOOLCHAIN = riscv32-unknown-elf- + else + ifneq ($(shell which riscv64-unknown-elf-gcc 2>/dev/null),) + TOOLCHAIN = riscv64-unknown-elf- + else + $(error "No RISC-V toolchain found. Can't find riscv32-unknown-elf-gcc or riscv64-unknown-elf-gcc found in your systems PATH variable. Please install a valid toolchain and make it accessible!") + endif + endif + endif + + # Default to compiling with picolibc for RISC-V targets if available, + # which is available by default on current (bullseye) debian based systems. + ifeq ($(shell $(TOOLCHAIN)gcc --specs=picolibc.specs -E - 2>/dev/null >/dev/null . + */ +#pragma once + +#include +#include "chibios_config.h" diff --git a/platforms/chibios/sleep_led.c b/platforms/chibios/sleep_led.c new file mode 100644 index 0000000000..477056a454 --- /dev/null +++ b/platforms/chibios/sleep_led.c @@ -0,0 +1,192 @@ +#include +#include + +#include "led.h" +#include "sleep_led.h" + +/* All right, we go the "software" way: timer, toggle LED in interrupt. + * Based on hasu's code for AVRs. + * Use LP timer on Kinetises, TIM14 on STM32F0. + */ + +#ifndef SLEEP_LED_GPT_DRIVER +# if defined(STM32F0XX) +# define SLEEP_LED_GPT_DRIVER GPTD14 +# endif +#endif + +#if defined(KL2x) || defined(K20x) || defined(SLEEP_LED_GPT_DRIVER) /* common parts for timers/interrupts */ + +/* Breathing Sleep LED brighness(PWM On period) table + * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle + * + * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63 + * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i } + */ +static const uint8_t breathing_table[64] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, 15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, 255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +void sleep_led_timer_callback(void) { + /* Software PWM + * timer:1111 1111 1111 1111 + * \_____/\/ \_______/____ count(0-255) + * \ \______________ duration of step(4) + * \__________________ index of step table(0-63) + */ + + // this works for cca 65536 irqs/sec + static union { + uint16_t row; + struct { + uint8_t count : 8; + uint8_t duration : 2; + uint8_t index : 6; + } pwm; + } timer = {.row = 0}; + + timer.row++; + + // LED on + if (timer.pwm.count == 0) { + led_set(1 << USB_LED_CAPS_LOCK); + } + // LED off + if (timer.pwm.count == breathing_table[timer.pwm.index]) { + led_set(0); + } +} + +#endif /* common parts for known platforms */ + +#if defined(KL2x) || defined(K20x) /* platform selection: familiar Kinetis chips */ + +/* Use Low Power Timer (LPTMR) */ +# define TIMER_INTERRUPT_VECTOR KINETIS_LPTMR0_IRQ_VECTOR +# define RESET_COUNTER LPTMR0->CSR |= LPTMRx_CSR_TCF + +/* LPTMR clock options */ +# define LPTMR_CLOCK_MCGIRCLK 0 /* 4MHz clock */ +# define LPTMR_CLOCK_LPO 1 /* 1kHz clock */ +# define LPTMR_CLOCK_ERCLK32K 2 /* external 32kHz crystal */ +# define LPTMR_CLOCK_OSCERCLK 3 /* output from OSC */ + +/* Work around inconsistencies in Freescale naming */ +# if !defined(SIM_SCGC5_LPTMR) +# define SIM_SCGC5_LPTMR SIM_SCGC5_LPTIMER +# endif + +/* interrupt handler */ +OSAL_IRQ_HANDLER(TIMER_INTERRUPT_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + sleep_led_timer_callback(); + + /* Reset the counter */ + RESET_COUNTER; + + OSAL_IRQ_EPILOGUE(); +} + +/* Initialise the timer */ +void sleep_led_init(void) { + /* Make sure the clock to the LPTMR is enabled */ + SIM->SCGC5 |= SIM_SCGC5_LPTMR; + /* Reset LPTMR settings */ + LPTMR0->CSR = 0; + /* Set the compare value */ + LPTMR0->CMR = 0; // trigger on counter value (i.e. every time) + +/* Set up clock source and prescaler */ +/* Software PWM + * ______ ______ __ + * | ON |___OFF___| ON |___OFF___| .... + * |<-------------->|<-------------->|<- .... + * PWM period PWM period + * + * R interrupts/period[resolution] + * F periods/second[frequency] + * R * F interrupts/second + */ + +/* === OPTION 1 === */ +# if 0 + // 1kHz LPO + // No prescaler => 1024 irqs/sec + // Note: this is too slow for a smooth breathe + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_LPO)|LPTMRx_PSR_PBYP; +# endif /* OPTION 1 */ + +/* === OPTION 2 === */ +# if 1 + // nMHz IRC (n=4 on KL25Z, KL26Z and K20x; n=2 or 8 on KL27Z) + MCG->C2 |= MCG_C2_IRCS; // fast (4MHz) internal ref clock +# if defined(KL27) // divide the 8MHz IRC by 2, to have the same MCGIRCLK speed as others + MCG->MC |= MCG_MC_LIRC_DIV2_DIV2; +# endif /* KL27 */ + MCG->C1 |= MCG_C1_IRCLKEN; // enable internal ref clock + // to work in stop mode, also MCG_C1_IREFSTEN + // Divide 4MHz by 2^N (N=6) => 62500 irqs/sec => + // => approx F=61, R=256, duration = 4 + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_MCGIRCLK) | LPTMRx_PSR_PRESCALE(6); +# endif /* OPTION 2 */ + +/* === OPTION 3 === */ +# if 0 + // OSC output (external crystal), usually 8MHz or 16MHz + OSC0->CR |= OSC_CR_ERCLKEN; // enable ext ref clock + // to work in stop mode, also OSC_CR_EREFSTEN + // Divide by 2^N + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_OSCERCLK)|LPTMRx_PSR_PRESCALE(7); +# endif /* OPTION 3 */ + /* === END OPTIONS === */ + + /* Interrupt on TCF set (compare flag) */ + nvicEnableVector(LPTMR0_IRQn, 2); // vector, priority + LPTMR0->CSR |= LPTMRx_CSR_TIE; +} + +void sleep_led_enable(void) { + /* Enable the timer */ + LPTMR0->CSR |= LPTMRx_CSR_TEN; +} + +void sleep_led_disable(void) { + /* Disable the timer */ + LPTMR0->CSR &= ~LPTMRx_CSR_TEN; +} + +void sleep_led_toggle(void) { + /* Toggle the timer */ + LPTMR0->CSR ^= LPTMRx_CSR_TEN; +} + +#elif defined(SLEEP_LED_GPT_DRIVER) + +static void gptTimerCallback(GPTDriver *gptp) { + (void)gptp; + sleep_led_timer_callback(); +} + +static const GPTConfig gptcfg = {1000000, gptTimerCallback, 0, 0}; + +/* Initialise the timer */ +void sleep_led_init(void) { gptStart(&SLEEP_LED_GPT_DRIVER, &gptcfg); } + +void sleep_led_enable(void) { gptStartContinuous(&SLEEP_LED_GPT_DRIVER, gptcfg.frequency / 0xFFFF); } + +void sleep_led_disable(void) { gptStopTimer(&SLEEP_LED_GPT_DRIVER); } + +void sleep_led_toggle(void) { (SLEEP_LED_GPT_DRIVER.state == GPT_READY) ? sleep_led_enable() : sleep_led_disable(); } + +#else /* platform selection: not on familiar chips */ + +void sleep_led_init(void) {} + +void sleep_led_enable(void) { led_set(1 << USB_LED_CAPS_LOCK); } + +void sleep_led_disable(void) { led_set(0); } + +void sleep_led_toggle(void) { + // not implemented +} + +#endif /* platform selection */ diff --git a/platforms/chibios/suspend.c b/platforms/chibios/suspend.c new file mode 100644 index 0000000000..9310a99920 --- /dev/null +++ b/platforms/chibios/suspend.c @@ -0,0 +1,92 @@ +/* TODO */ + +#include +#include + +#include "matrix.h" +#include "action.h" +#include "action_util.h" +#include "mousekey.h" +#include "programmable_button.h" +#include "host.h" +#include "suspend.h" +#include "led.h" +#include "wait.h" + +/** \brief suspend idle + * + * FIXME: needs doc + */ +void suspend_idle(uint8_t time) { + // TODO: this is not used anywhere - what units is 'time' in? + wait_ms(time); +} + +/** \brief suspend power down + * + * FIXME: needs doc + */ +void suspend_power_down(void) { + suspend_power_down_quantum(); + // on AVR, this enables the watchdog for 15ms (max), and goes to + // SLEEP_MODE_PWR_DOWN + + wait_ms(17); +} + +/** \brief suspend wakeup condition + * + * FIXME: needs doc + */ +__attribute__((weak)) void matrix_power_up(void) {} +__attribute__((weak)) void matrix_power_down(void) {} +bool suspend_wakeup_condition(void) { + matrix_power_up(); + matrix_scan(); + matrix_power_down(); + for (uint8_t r = 0; r < MATRIX_ROWS; r++) { + if (matrix_get_row(r)) return true; + } + return false; +} + +/** \brief run user level code immediately after wakeup + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_wakeup_init_user(void) {} + +/** \brief run keyboard level code immediately after wakeup + * + * FIXME: needs doc + */ +__attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); } + +/** \brief suspend wakeup condition + * + * run immediately after wakeup + * FIXME: needs doc + */ +void suspend_wakeup_init(void) { + // clear keyboard state + // need to do it manually, because we're running from ISR + // and clear_keyboard() calls print + // so only clear the variables in memory + // the reports will be sent from main.c afterwards + // or if the PC asks for GET_REPORT + clear_mods(); + clear_weak_mods(); + clear_keys(); +#ifdef MOUSEKEY_ENABLE + mousekey_clear(); +#endif /* MOUSEKEY_ENABLE */ +#ifdef PROGRAMMABLE_BUTTON_ENABLE + programmable_button_clear(); +#endif /* PROGRAMMABLE_BUTTON_ENABLE */ +#ifdef EXTRAKEY_ENABLE + host_system_send(0); + host_consumer_send(0); +#endif /* EXTRAKEY_ENABLE */ + + suspend_wakeup_init_quantum(); +} diff --git a/platforms/chibios/syscall-fallbacks.c b/platforms/chibios/syscall-fallbacks.c new file mode 100644 index 0000000000..4569879c7c --- /dev/null +++ b/platforms/chibios/syscall-fallbacks.c @@ -0,0 +1,110 @@ +/* Copyright 2021 Nick Brassel, QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +/* To compile the ChibiOS syscall stubs with picolibc + * the _reent struct has to be defined. */ +#if defined(USE_PICOLIBC) +struct _reent; +#endif + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +__attribute__((weak, used)) int _open_r(struct _reent *r, const char *path, int flag, int m) { + __errno_r(r) = ENOENT; + return -1; +} + +__attribute__((weak, used)) int _lseek_r(struct _reent *r, int file, int ptr, int dir) { + __errno_r(r) = EBADF; + return -1; +} + +__attribute__((weak, used)) int _read_r(struct _reent *r, int file, char *ptr, int len) { + __errno_r(r) = EBADF; + return -1; +} + +__attribute__((weak, used)) int _write_r(struct _reent *r, int file, char *ptr, int len) { + __errno_r(r) = EBADF; + return -1; +} + +__attribute__((weak, used)) int _close_r(struct _reent *r, int file) { + __errno_r(r) = EBADF; + return -1; +} + +__attribute__((weak, used)) int _link_r(struct _reent *r, const char *oldpath, const char *newpath) { + __errno_r(r) = EPERM; + return -1; +} + +__attribute__((weak, used)) int _unlink_r(struct _reent *r, const char *path) { + __errno_r(r) = EPERM; + return -1; +} + +__attribute__((weak, used)) clock_t _times_r(struct _reent *r, void *t) { + __errno_r(r) = EFAULT; + return -1; +} + +__attribute__((weak, used)) int _fstat_r(struct _reent *r, int file, struct stat *st) { + __errno_r(r) = EBADF; + return -1; +} + +__attribute__((weak, used)) int _isatty_r(struct _reent *r, int fd) { + __errno_r(r) = EBADF; + return 0; +} + +__attribute__((weak, used)) caddr_t _sbrk_r(struct _reent *r, int incr) { + __errno_r(r) = ENOMEM; + return (caddr_t)-1; +} + +__attribute__((weak, used)) int _kill(int pid, int sig) { + errno = EPERM; + return -1; +} + +__attribute__((weak, used)) pid_t _getpid(void) { return 1; } + +__attribute__((weak, used)) void _fini(void) { return; } + +__attribute__((weak, used, noreturn)) void _exit(int i) { + while (1) + ; +} + +__attribute__((weak, used)) int _gettimeofday_r(struct _reent *r, struct timeval *t, void *tzp) { + __errno_r(r) = EPERM; + return -1; +} + +__attribute__((weak, used)) void *__dso_handle; + +__attribute__((weak, used)) void __cxa_pure_virtual(void) { + while (1) + ; +} + +#pragma GCC diagnostic pop diff --git a/platforms/chibios/timer.c b/platforms/chibios/timer.c new file mode 100644 index 0000000000..9f664e1f79 --- /dev/null +++ b/platforms/chibios/timer.c @@ -0,0 +1,47 @@ +#include + +#include "timer.h" + +static uint32_t reset_point = 0; +#if CH_CFG_ST_RESOLUTION < 32 +static uint32_t last_systime = 0; +static uint32_t overflow = 0; +#endif + +void timer_init(void) { timer_clear(); } + +void timer_clear(void) { + reset_point = (uint32_t)chVTGetSystemTime(); +#if CH_CFG_ST_RESOLUTION < 32 + last_systime = reset_point; + overflow = 0; +#endif +} + +uint16_t timer_read(void) { return (uint16_t)timer_read32(); } + +uint32_t timer_read32(void) { + uint32_t systime = (uint32_t)chVTGetSystemTime(); + +#if CH_CFG_ST_RESOLUTION < 32 + // If/when we need to support 64-bit chips, this may need to be modified to match the native bit-ness of the MCU. + // At this point, the only SysTick resolution allowed other than 32 is 16 bit. + // In the 16-bit case, at: + // - CH_CFG_ST_FREQUENCY = 100000, overflow will occur every ~0.65 seconds + // - CH_CFG_ST_FREQUENCY = 10000, overflow will occur every ~6.5 seconds + // - CH_CFG_ST_FREQUENCY = 1000, overflow will occur every ~65 seconds + // With this implementation, as long as we ensure a timer read happens at least once during the overflow period, timing should be accurate. + if (systime < last_systime) { + overflow += ((uint32_t)1) << CH_CFG_ST_RESOLUTION; + } + + last_systime = systime; + return (uint32_t)TIME_I2MS(systime - reset_point + overflow); +#else + return (uint32_t)TIME_I2MS(systime - reset_point); +#endif +} + +uint16_t timer_elapsed(uint16_t last) { return TIMER_DIFF_16(timer_read(), last); } + +uint32_t timer_elapsed32(uint32_t last) { return TIMER_DIFF_32(timer_read32(), last); } diff --git a/platforms/chibios/wait.c b/platforms/chibios/wait.c new file mode 100644 index 0000000000..56fd6ffcec --- /dev/null +++ b/platforms/chibios/wait.c @@ -0,0 +1,41 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "_wait.h" + +#ifdef WAIT_US_TIMER +void wait_us(uint16_t duration) { + static const GPTConfig gpt_cfg = {1000000, NULL, 0, 0}; /* 1MHz timer, no callback */ + + if (duration == 0) { + duration = 1; + } + + /* + * Only use this timer on the main thread; + * other threads need to use their own timer. + */ + if (chThdGetSelfX() == &ch.mainthread && duration < (1ULL << (sizeof(gptcnt_t) * 8))) { + gptStart(&WAIT_US_TIMER, &gpt_cfg); + gptPolledDelay(&WAIT_US_TIMER, duration); + } else { + chThdSleepMicroseconds(duration); + } +} +#endif diff --git a/platforms/common.mk b/platforms/common.mk new file mode 100644 index 0000000000..f7a0fc7028 --- /dev/null +++ b/platforms/common.mk @@ -0,0 +1,12 @@ +PLATFORM_COMMON_DIR = $(PLATFORM_PATH)/$(PLATFORM_KEY) + +TMK_COMMON_SRC += \ + $(PLATFORM_COMMON_DIR)/platform.c \ + $(PLATFORM_COMMON_DIR)/suspend.c \ + $(PLATFORM_COMMON_DIR)/timer.c \ + $(PLATFORM_COMMON_DIR)/bootloader.c \ + +# Search Path +VPATH += $(PLATFORM_PATH) +VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY) +VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR) diff --git a/platforms/eeprom.h b/platforms/eeprom.h new file mode 100644 index 0000000000..f5b3f0ad53 --- /dev/null +++ b/platforms/eeprom.h @@ -0,0 +1,21 @@ +#pragma once + +#if defined(__AVR__) && !defined(EEPROM_DRIVER) +# include +#else +# include +# include + +uint8_t eeprom_read_byte(const uint8_t *__p); +uint16_t eeprom_read_word(const uint16_t *__p); +uint32_t eeprom_read_dword(const uint32_t *__p); +void eeprom_read_block(void *__dst, const void *__src, size_t __n); +void eeprom_write_byte(uint8_t *__p, uint8_t __value); +void eeprom_write_word(uint16_t *__p, uint16_t __value); +void eeprom_write_dword(uint32_t *__p, uint32_t __value); +void eeprom_write_block(const void *__src, void *__dst, size_t __n); +void eeprom_update_byte(uint8_t *__p, uint8_t __value); +void eeprom_update_word(uint16_t *__p, uint16_t __value); +void eeprom_update_dword(uint32_t *__p, uint32_t __value); +void eeprom_update_block(const void *__src, void *__dst, size_t __n); +#endif diff --git a/platforms/gpio.h b/platforms/gpio.h new file mode 100644 index 0000000000..b47f6f8e43 --- /dev/null +++ b/platforms/gpio.h @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "pin_defs.h" + +#if __has_include_next("gpio.h") +# include_next "gpio.h" /* Include the platforms gpio.h */ +#endif \ No newline at end of file diff --git a/platforms/pin_defs.h b/platforms/pin_defs.h new file mode 100644 index 0000000000..ea730138f2 --- /dev/null +++ b/platforms/pin_defs.h @@ -0,0 +1,23 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// useful for direct pin mapping +#define NO_PIN (pin_t)(~0) + +#if __has_include_next("pin_defs.h") +# include_next "pin_defs.h" /* Include the platforms pin_defs.h */ +#endif diff --git a/platforms/progmem.h b/platforms/progmem.h new file mode 100644 index 0000000000..a70d8e299f --- /dev/null +++ b/platforms/progmem.h @@ -0,0 +1,19 @@ +#pragma once + +#if defined(__AVR__) +# include +#else +# include +# define PROGMEM +# define __flash +# define PSTR(x) x +# define PGM_P const char* +# define memcpy_P(dest, src, n) memcpy(dest, src, n) +# define pgm_read_byte(address_short) *((uint8_t*)(address_short)) +# define pgm_read_word(address_short) *((uint16_t*)(address_short)) +# define pgm_read_dword(address_short) *((uint32_t*)(address_short)) +# define pgm_read_ptr(address_short) *((void**)(address_short)) +# define strcmp_P(s1, s2) strcmp(s1, s2) +# define strcpy_P(dest, src) strcpy(dest, src) +# define strlen_P(src) strlen(src) +#endif diff --git a/platforms/sleep_led.h b/platforms/sleep_led.h new file mode 100644 index 0000000000..38f80a660d --- /dev/null +++ b/platforms/sleep_led.h @@ -0,0 +1,17 @@ +#pragma once + +#ifdef SLEEP_LED_ENABLE + +void sleep_led_init(void); +void sleep_led_enable(void); +void sleep_led_disable(void); +void sleep_led_toggle(void); + +#else + +# define sleep_led_init() +# define sleep_led_enable() +# define sleep_led_disable() +# define sleep_led_toggle() + +#endif diff --git a/platforms/suspend.h b/platforms/suspend.h new file mode 100644 index 0000000000..081735f90e --- /dev/null +++ b/platforms/suspend.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +void suspend_idle(uint8_t timeout); +void suspend_power_down(void); +bool suspend_wakeup_condition(void); +void suspend_wakeup_init(void); + +void suspend_wakeup_init_user(void); +void suspend_wakeup_init_kb(void); +void suspend_wakeup_init_quantum(void); +void suspend_power_down_user(void); +void suspend_power_down_kb(void); +void suspend_power_down_quantum(void); + +#ifndef USB_SUSPEND_WAKEUP_DELAY +# define USB_SUSPEND_WAKEUP_DELAY 0 +#endif diff --git a/platforms/test/_wait.h b/platforms/test/_wait.h new file mode 100644 index 0000000000..4e22f593b7 --- /dev/null +++ b/platforms/test/_wait.h @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +void wait_ms(uint32_t ms); +#define wait_us(us) wait_ms(us / 1000) +#define waitInputPinDelay() diff --git a/platforms/test/bootloader.c b/platforms/test/bootloader.c new file mode 100644 index 0000000000..5155d9ff04 --- /dev/null +++ b/platforms/test/bootloader.c @@ -0,0 +1,19 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "bootloader.h" + +void bootloader_jump(void) {} diff --git a/platforms/test/eeprom.c b/platforms/test/eeprom.c new file mode 100644 index 0000000000..5c8e69dae3 --- /dev/null +++ b/platforms/test/eeprom.c @@ -0,0 +1,95 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "eeprom.h" + +#define EEPROM_SIZE 32 + +static uint8_t buffer[EEPROM_SIZE]; + +uint8_t eeprom_read_byte(const uint8_t *addr) { + uintptr_t offset = (uintptr_t)addr; + return buffer[offset]; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t value) { + uintptr_t offset = (uintptr_t)addr; + buffer[offset] = value; +} + +uint16_t eeprom_read_word(const uint16_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24); +} + +void eeprom_read_block(void *buf, const void *addr, size_t len) { + const uint8_t *p = (const uint8_t *)addr; + uint8_t * dest = (uint8_t *)buf; + while (len--) { + *dest++ = eeprom_read_byte(p++); + } +} + +void eeprom_write_word(uint16_t *addr, uint16_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_write_block(const void *buf, void *addr, size_t len) { + uint8_t * p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} + +void eeprom_update_byte(uint8_t *addr, uint8_t value) { eeprom_write_byte(addr, value); } + +void eeprom_update_word(uint16_t *addr, uint16_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_update_dword(uint32_t *addr, uint32_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_update_block(const void *buf, void *addr, size_t len) { + uint8_t * p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} diff --git a/platforms/test/eeprom_stm32_tests.cpp b/platforms/test/eeprom_stm32_tests.cpp new file mode 100644 index 0000000000..5bc8d87900 --- /dev/null +++ b/platforms/test/eeprom_stm32_tests.cpp @@ -0,0 +1,438 @@ +/* Copyright 2021 by Don Kjer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +extern "C" { +#include "flash_stm32.h" +#include "eeprom_stm32.h" +#include "eeprom.h" +} + +/* Mock Flash Parameters: + * + * === Large Layout === + * flash size: 65536 + * page size: 2048 + * density pages: 16 + * Simulated EEPROM size: 16384 + * + * FlashBuf Layout: + * [Unused | Compact | Write Log ] + * [0......|32768......|49152......65535] + * + * === Tiny Layout === + * flash size: 1024 + * page size: 512 + * density pages: 1 + * Simulated EEPROM size: 256 + * + * FlashBuf Layout: + * [Unused | Compact | Write Log ] + * [0......|512......|768......1023] + * + */ + +#define EEPROM_SIZE (FEE_PAGE_SIZE * FEE_PAGE_COUNT / 2) +#define LOG_SIZE EEPROM_SIZE +#define LOG_BASE (MOCK_FLASH_SIZE - LOG_SIZE) +#define EEPROM_BASE (LOG_BASE - EEPROM_SIZE) + +/* Log encoding helpers */ +#define BYTE_VALUE(addr, value) (((addr) << 8) | (value)) +#define WORD_ZERO(addr) (0x8000 | ((addr) >> 1)) +#define WORD_ONE(addr) (0xA000 | ((addr) >> 1)) +#define WORD_NEXT(addr) (0xE000 | (((addr)-0x80) >> 1)) + +class EepromStm32Test : public testing::Test { + public: + EepromStm32Test() {} + ~EepromStm32Test() {} + + protected: + void SetUp() override { EEPROM_Erase(); } + + void TearDown() override { +#ifdef EEPROM_DEBUG + dumpEepromDataBuf(); +#endif + } +}; + +TEST_F(EepromStm32Test, TestErase) { + EEPROM_WriteDataByte(0, 0x42); + EEPROM_Erase(); + EXPECT_EQ(EEPROM_ReadDataByte(0), 0); + EXPECT_EQ(EEPROM_ReadDataByte(1), 0); +} + +TEST_F(EepromStm32Test, TestReadGarbage) { + uint8_t garbage = 0x3c; + for (int i = 0; i < MOCK_FLASH_SIZE; ++i) { + garbage ^= 0xa3; + garbage += i; + FlashBuf[i] = garbage; + } + EEPROM_Init(); // Just verify we don't crash +} + +TEST_F(EepromStm32Test, TestWriteBadAddress) { + EXPECT_EQ(EEPROM_WriteDataByte(EEPROM_SIZE, 0x42), FLASH_BAD_ADDRESS); + EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE - 1, 0xbeef), FLASH_BAD_ADDRESS); + EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE, 0xbeef), FLASH_BAD_ADDRESS); +} + +TEST_F(EepromStm32Test, TestReadBadAddress) { + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE), 0xFF); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 1), 0xFFFF); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE), 0xFFFF); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 3)), 0xFF000000); + EXPECT_EQ(eeprom_read_dword((uint32_t*)EEPROM_SIZE), 0xFFFFFFFF); +} + +TEST_F(EepromStm32Test, TestReadByte) { + /* Direct compacted-area baseline: Address < 0x80 */ + FlashBuf[EEPROM_BASE + 2] = ~0xef; + FlashBuf[EEPROM_BASE + 3] = ~0xbe; + /* Direct compacted-area baseline: Address >= 0x80 */ + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56); + /* Write Log byte value */ + FlashBuf[LOG_BASE] = 0x65; + FlashBuf[LOG_BASE + 1] = 3; + /* Write Log word value */ + *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_NEXT(EEPROM_SIZE - 2); + *(uint16_t*)&FlashBuf[LOG_BASE + 4] = ~0x9abc; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0x65); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0xbc); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x9a); +} + +TEST_F(EepromStm32Test, TestWriteByte) { + /* Direct compacted-area baseline: Address < 0x80 */ + EEPROM_WriteDataByte(2, 0xef); + EEPROM_WriteDataByte(3, 0xbe); + /* Direct compacted-area baseline: Address >= 0x80 */ + EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78); + EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56); + /* Check values */ + /* First write in each aligned word should have been direct */ + EXPECT_EQ(FlashBuf[EEPROM_BASE + 2], (uint8_t)~0xef); + EXPECT_EQ(FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint8_t)~0x78); + + /* Second write per aligned word requires a log entry */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(3, 0xbe)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(EEPROM_SIZE - 1)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0x5678); +} + +TEST_F(EepromStm32Test, TestByteRoundTrip) { + /* Direct compacted-area: Address < 0x80 */ + EEPROM_WriteDataWord(0, 0xdead); + EEPROM_WriteDataByte(2, 0xef); + EEPROM_WriteDataByte(3, 0xbe); + /* Direct compacted-area: Address >= 0x80 */ + EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78); + EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(0), 0xad); + EXPECT_EQ(EEPROM_ReadDataByte(1), 0xde); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56); + /* Write log entries */ + EEPROM_WriteDataByte(2, 0x80); + EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x3c); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0x80); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x3c); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56); +} + +TEST_F(EepromStm32Test, TestReadWord) { + /* Direct compacted-area baseline: Address < 0x80 */ + FlashBuf[EEPROM_BASE + 0] = ~0xad; + FlashBuf[EEPROM_BASE + 1] = ~0xde; + /* Direct compacted-area baseline: Address >= 0x80 */ + FlashBuf[EEPROM_BASE + 200] = ~0xcd; + FlashBuf[EEPROM_BASE + 201] = ~0xab; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4] = ~0x34; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 3] = ~0x12; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678); + /* Write Log word zero-encoded */ + *(uint16_t*)&FlashBuf[LOG_BASE] = WORD_ZERO(200); + /* Write Log word one-encoded */ + *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_ONE(EEPROM_SIZE - 4); + /* Write Log word value */ + *(uint16_t*)&FlashBuf[LOG_BASE + 4] = WORD_NEXT(EEPROM_SIZE - 2); + *(uint16_t*)&FlashBuf[LOG_BASE + 6] = ~0x9abc; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 1); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x9abc); +} + +TEST_F(EepromStm32Test, TestWriteWord) { + /* Direct compacted-area: Address < 0x80 */ + EEPROM_WriteDataWord(0, 0xdead); // Aligned + EEPROM_WriteDataWord(3, 0xbeef); // Unaligned + /* Direct compacted-area: Address >= 0x80 */ + EEPROM_WriteDataWord(200, 0xabcd); // Aligned + EEPROM_WriteDataWord(203, 0x9876); // Unaligned + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234); + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678); + /* Write Log word zero-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0); + /* Write Log word one-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1); + /* Write Log word value aligned */ + EEPROM_WriteDataWord(200, 0x4321); // Aligned + /* Write Log word value unaligned */ + EEPROM_WriteDataByte(202, 0x3c); // Set neighboring byte + EEPROM_WriteDataWord(203, 0xcdef); // Unaligned + /* Check values */ + /* Direct compacted-area */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE], (uint16_t)~0xdead); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 3], (uint16_t)~0xbeef); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 200], (uint16_t)~0xabcd); + EXPECT_EQ(FlashBuf[EEPROM_BASE + 203], (uint8_t)~0x76); + EXPECT_EQ(FlashBuf[EEPROM_BASE + 204], (uint8_t)~0x98); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4], (uint16_t)~0x1234); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint16_t)~0x5678); + /* Write Log word zero-encoded */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], WORD_ZERO(EEPROM_SIZE - 4)); + /* Write Log word one-encoded */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_ONE(EEPROM_SIZE - 2)); + /* Write Log word value aligned */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], WORD_NEXT(200)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], (uint16_t)~0x4321); + /* Write Log word value unaligned */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], WORD_NEXT(202)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], (uint16_t)~0x763c); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(202)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xef3c); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(204)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0x00cd); +} + +TEST_F(EepromStm32Test, TestWordRoundTrip) { + /* Direct compacted-area: Address < 0x80 */ + EEPROM_WriteDataWord(0, 0xdead); // Aligned + EEPROM_WriteDataWord(3, 0xbeef); // Unaligned + /* Direct compacted-area: Address >= 0x80 */ + EEPROM_WriteDataWord(200, 0xabcd); // Aligned + EEPROM_WriteDataWord(203, 0x9876); // Unaligned + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234); + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead); + EXPECT_EQ(EEPROM_ReadDataWord(3), 0xbeef); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd); + EXPECT_EQ(EEPROM_ReadDataWord(203), 0x9876); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678); + + /* Write Log word zero-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0); + /* Write Log word one-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1); + /* Write Log word value aligned */ + EEPROM_WriteDataWord(200, 0x4321); // Aligned + /* Write Log word value unaligned */ + EEPROM_WriteDataByte(202, 0x3c); // Set neighboring byte + EEPROM_WriteDataWord(203, 0xcdef); // Unaligned + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0x4321); + EXPECT_EQ(EEPROM_ReadDataByte(202), 0x3c); + EXPECT_EQ(EEPROM_ReadDataWord(203), 0xcdef); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 1); +} + +TEST_F(EepromStm32Test, TestByteWordBoundary) { + /* Direct compacted-area write */ + EEPROM_WriteDataWord(0x7e, 0xdead); + EEPROM_WriteDataWord(0x80, 0xbeef); + /* Byte log entry */ + EEPROM_WriteDataByte(0x7f, 0x3c); + /* Word log entry */ + EEPROM_WriteDataByte(0x80, 0x18); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0x3cad); + EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xbe18); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(0x7f, 0x3c)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(0x80)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0xbe18); + /* Byte log entries */ + EEPROM_WriteDataWord(0x7e, 0xcafe); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0xcafe); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], BYTE_VALUE(0x7e, 0xfe)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], BYTE_VALUE(0x7f, 0xca)); + /* Byte and Word log entries */ + EEPROM_WriteDataWord(0x7f, 0xba5e); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x7f), 0xba5e); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], BYTE_VALUE(0x7f, 0x5e)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(0x80)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xbeba); + /* Word log entry */ + EEPROM_WriteDataWord(0x80, 0xf00d); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xf00d); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(0x80)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0xf00d); +} + +TEST_F(EepromStm32Test, TestDWordRoundTrip) { + /* Direct compacted-area: Address < 0x80 */ + eeprom_write_dword((uint32_t*)0, 0xdeadbeef); // Aligned + eeprom_write_dword((uint32_t*)9, 0x12345678); // Unaligned + /* Direct compacted-area: Address >= 0x80 */ + eeprom_write_dword((uint32_t*)200, 0xfacef00d); + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xba5eba11); // Aligned + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0xcafed00d); // Unaligned + /* Check direct values */ + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef); + EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x12345678); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 0xfacef00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xba5eba11); // Aligned + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0xcafed00d); // Unaligned + /* Write Log byte encoded */ + eeprom_write_dword((uint32_t*)0, 0xdecafbad); + eeprom_write_dword((uint32_t*)9, 0x87654321); + /* Write Log word encoded */ + eeprom_write_dword((uint32_t*)200, 1); + /* Write Log word value aligned */ + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xdeadc0de); // Aligned + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0x6789abcd); // Unaligned + /* Check log values */ + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdecafbad); + EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x87654321); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 1); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xdeadc0de); // Aligned + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0x6789abcd); // Unaligned +} + +TEST_F(EepromStm32Test, TestBlockRoundTrip) { + char src0[] = "0123456789abcdef"; + void* src1 = (void*)&src0[1]; + /* Various alignments of src & dst, Address < 0x80 */ + eeprom_write_block(src0, (void*)0, sizeof(src0)); + eeprom_write_block(src0, (void*)21, sizeof(src0)); + eeprom_write_block(src1, (void*)40, sizeof(src0) - 1); + eeprom_write_block(src1, (void*)61, sizeof(src0) - 1); + /* Various alignments of src & dst, Address >= 0x80 */ + eeprom_write_block(src0, (void*)140, sizeof(src0)); + eeprom_write_block(src0, (void*)161, sizeof(src0)); + eeprom_write_block(src1, (void*)180, sizeof(src0) - 1); + eeprom_write_block(src1, (void*)201, sizeof(src0) - 1); + + /* Check values */ + EEPROM_Init(); + + char dstBuf[256] = {0}; + char* dst0a = (char*)dstBuf; + char* dst0b = (char*)&dstBuf[20]; + char* dst1a = (char*)&dstBuf[41]; + char* dst1b = (char*)&dstBuf[61]; + char* dst0c = (char*)&dstBuf[80]; + char* dst0d = (char*)&dstBuf[100]; + char* dst1c = (char*)&dstBuf[121]; + char* dst1d = (char*)&dstBuf[141]; + eeprom_read_block((void*)dst0a, (void*)0, sizeof(src0)); + eeprom_read_block((void*)dst0b, (void*)21, sizeof(src0)); + eeprom_read_block((void*)dst1a, (void*)40, sizeof(src0) - 1); + eeprom_read_block((void*)dst1b, (void*)61, sizeof(src0) - 1); + eeprom_read_block((void*)dst0c, (void*)140, sizeof(src0)); + eeprom_read_block((void*)dst0d, (void*)161, sizeof(src0)); + eeprom_read_block((void*)dst1c, (void*)180, sizeof(src0) - 1); + eeprom_read_block((void*)dst1d, (void*)201, sizeof(src0) - 1); + EXPECT_EQ(strcmp((char*)src0, dst0a), 0); + EXPECT_EQ(strcmp((char*)src0, dst0b), 0); + EXPECT_EQ(strcmp((char*)src0, dst0c), 0); + EXPECT_EQ(strcmp((char*)src0, dst0d), 0); + EXPECT_EQ(strcmp((char*)src1, dst1a), 0); + EXPECT_EQ(strcmp((char*)src1, dst1b), 0); + EXPECT_EQ(strcmp((char*)src1, dst1c), 0); + EXPECT_EQ(strcmp((char*)src1, dst1d), 0); +} + +TEST_F(EepromStm32Test, TestCompaction) { + /* Direct writes */ + eeprom_write_dword((uint32_t*)0, 0xdeadbeef); + eeprom_write_byte((uint8_t*)4, 0x3c); + eeprom_write_word((uint16_t*)6, 0xd00d); + eeprom_write_dword((uint32_t*)150, 0xcafef00d); + eeprom_write_dword((uint32_t*)200, 0x12345678); + /* Fill write log entries */ + uint32_t i; + uint32_t val = 0xd8453c6b; + for (i = 0; i < (LOG_SIZE / (sizeof(uint32_t) * 2)); i++) { + val ^= 0x593ca5b3; + val += i; + eeprom_write_dword((uint32_t*)200, val); + } + /* Check values pre-compaction */ + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef); + EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x3c); + EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val); + EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF); + EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF); + /* Run compaction */ + eeprom_write_byte((uint8_t*)4, 0x1f); + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef); + EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x1f); + EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF); +} diff --git a/platforms/test/flash_stm32_mock.c b/platforms/test/flash_stm32_mock.c new file mode 100644 index 0000000000..222a004bc7 --- /dev/null +++ b/platforms/test/flash_stm32_mock.c @@ -0,0 +1,49 @@ +/* Copyright 2021 by Don Kjer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "flash_stm32.h" + +uint8_t FlashBuf[MOCK_FLASH_SIZE] = {0}; + +static bool flash_locked = true; + +FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { + if (flash_locked) return FLASH_ERROR_WRP; + Page_Address -= (uintptr_t)FlashBuf; + Page_Address -= (Page_Address % FEE_PAGE_SIZE); + if (Page_Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS; + memset(&FlashBuf[Page_Address], '\xff', FEE_PAGE_SIZE); + return FLASH_COMPLETE; +} + +FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) { + if (flash_locked) return FLASH_ERROR_WRP; + Address -= (uintptr_t)FlashBuf; + if (Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS; + uint16_t oldData = *(uint16_t*)&FlashBuf[Address]; + if (oldData == 0xFFFF || Data == 0) { + *(uint16_t*)&FlashBuf[Address] = Data; + return FLASH_COMPLETE; + } else { + return FLASH_ERROR_PG; + } +} + +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) { return FLASH_COMPLETE; } +void FLASH_Unlock(void) { flash_locked = false; } +void FLASH_Lock(void) { flash_locked = true; } diff --git a/platforms/test/hal.h b/platforms/test/hal.h new file mode 100644 index 0000000000..2d268ad54c --- /dev/null +++ b/platforms/test/hal.h @@ -0,0 +1,18 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// Just here to please eeprom tests diff --git a/platforms/test/platform.c b/platforms/test/platform.c new file mode 100644 index 0000000000..8ddceeda8f --- /dev/null +++ b/platforms/test/platform.c @@ -0,0 +1,21 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform_deps.h" + +void platform_setup(void) { + // do nothing +} \ No newline at end of file diff --git a/platforms/test/platform.h b/platforms/test/platform.h new file mode 100644 index 0000000000..f296d1d535 --- /dev/null +++ b/platforms/test/platform.h @@ -0,0 +1,18 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// here just to please the build diff --git a/platforms/test/platform.mk b/platforms/test/platform.mk new file mode 100644 index 0000000000..eb2424ec5c --- /dev/null +++ b/platforms/test/platform.mk @@ -0,0 +1,34 @@ +SYSTEM_TYPE := $(shell gcc -dumpmachine) +GCC_VERSION := $(shell gcc --version 2>/dev/null) + +CC = $(CC_PREFIX) gcc +OBJCOPY = +OBJDUMP = +SIZE = +AR = +NM = +HEX = +EEP = +BIN = + + +COMPILEFLAGS += -funsigned-char +ifeq ($(findstring clang, ${GCC_VERSION}),) +COMPILEFLAGS += -funsigned-bitfields +endif +COMPILEFLAGS += -ffunction-sections +COMPILEFLAGS += -fdata-sections +COMPILEFLAGS += -fshort-enums +ifneq ($(findstring mingw, ${SYSTEM_TYPE}),) +COMPILEFLAGS += -mno-ms-bitfields +endif + +CFLAGS += $(COMPILEFLAGS) +ifeq ($(findstring clang, ${GCC_VERSION}),) +CFLAGS += -fno-inline-small-functions +endif +CFLAGS += -fno-strict-aliasing + +CXXFLAGS += $(COMPILEFLAGS) +CXXFLAGS += -fno-exceptions +CXXFLAGS += -std=gnu++11 diff --git a/platforms/test/platform_deps.h b/platforms/test/platform_deps.h new file mode 100644 index 0000000000..f296d1d535 --- /dev/null +++ b/platforms/test/platform_deps.h @@ -0,0 +1,18 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// here just to please the build diff --git a/platforms/test/rules.mk b/platforms/test/rules.mk new file mode 100644 index 0000000000..66b853d8ee --- /dev/null +++ b/platforms/test/rules.mk @@ -0,0 +1,24 @@ +eeprom_stm32_DEFS := -DFLASH_STM32_MOCKED -DNO_PRINT -DFEE_FLASH_BASE=FlashBuf +eeprom_stm32_tiny_DEFS := $(eeprom_stm32_DEFS) \ + -DFEE_MCU_FLASH_SIZE=1 \ + -DMOCK_FLASH_SIZE=1024 \ + -DFEE_PAGE_SIZE=512 \ + -DFEE_PAGE_COUNT=1 +eeprom_stm32_large_DEFS := $(eeprom_stm32_DEFS) \ + -DFEE_MCU_FLASH_SIZE=64 \ + -DMOCK_FLASH_SIZE=65536 \ + -DFEE_PAGE_SIZE=2048 \ + -DFEE_PAGE_COUNT=16 + +eeprom_stm32_INC := \ + $(PLATFORM_PATH)/chibios/ +eeprom_stm32_tiny_INC := $(eeprom_stm32_INC) +eeprom_stm32_large_INC := $(eeprom_stm32_INC) + +eeprom_stm32_SRC := \ + $(TOP_DIR)/drivers/eeprom/eeprom_driver.c \ + $(PLATFORM_PATH)/$(PLATFORM_KEY)/eeprom_stm32_tests.cpp \ + $(PLATFORM_PATH)/$(PLATFORM_KEY)/flash_stm32_mock.c \ + $(PLATFORM_PATH)/chibios/eeprom_stm32.c +eeprom_stm32_tiny_SRC := $(eeprom_stm32_SRC) +eeprom_stm32_large_SRC := $(eeprom_stm32_SRC) diff --git a/platforms/test/suspend.c b/platforms/test/suspend.c new file mode 100644 index 0000000000..76b705967b --- /dev/null +++ b/platforms/test/suspend.c @@ -0,0 +1,15 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ diff --git a/platforms/test/testlist.mk b/platforms/test/testlist.mk new file mode 100644 index 0000000000..51a9638bb9 --- /dev/null +++ b/platforms/test/testlist.mk @@ -0,0 +1 @@ +TEST_LIST += eeprom_stm32_tiny eeprom_stm32_large diff --git a/platforms/test/timer.c b/platforms/test/timer.c new file mode 100644 index 0000000000..61c3a00201 --- /dev/null +++ b/platforms/test/timer.c @@ -0,0 +1,33 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "timer.h" + +static uint32_t current_time = 0; + +void timer_init(void) { current_time = 0; } + +void timer_clear(void) { current_time = 0; } + +uint16_t timer_read(void) { return current_time & 0xFFFF; } +uint32_t timer_read32(void) { return current_time; } +uint16_t timer_elapsed(uint16_t last) { return TIMER_DIFF_16(timer_read(), last); } +uint32_t timer_elapsed32(uint32_t last) { return TIMER_DIFF_32(timer_read32(), last); } + +void set_time(uint32_t t) { current_time = t; } +void advance_time(uint32_t ms) { current_time += ms; } + +void wait_ms(uint32_t ms) { advance_time(ms); } diff --git a/platforms/timer.h b/platforms/timer.h new file mode 100644 index 0000000000..02e39e79e7 --- /dev/null +++ b/platforms/timer.h @@ -0,0 +1,67 @@ +/* +Copyright 2011 Jun Wako +Copyright 2021 Simon Arlott + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#if __has_include_next("_timer.h") +# include_next "_timer.h" /* Include the platform's _timer.h */ +#endif + +#include + +#define TIMER_DIFF(a, b, max) ((max == UINT8_MAX) ? ((uint8_t)((a) - (b))) : ((max == UINT16_MAX) ? ((uint16_t)((a) - (b))) : ((max == UINT32_MAX) ? ((uint32_t)((a) - (b))) : ((a) >= (b) ? (a) - (b) : (max) + 1 - (b) + (a))))) +#define TIMER_DIFF_8(a, b) TIMER_DIFF(a, b, UINT8_MAX) +#define TIMER_DIFF_16(a, b) TIMER_DIFF(a, b, UINT16_MAX) +#define TIMER_DIFF_32(a, b) TIMER_DIFF(a, b, UINT32_MAX) +#define TIMER_DIFF_RAW(a, b) TIMER_DIFF_8(a, b) + +#ifdef __cplusplus +extern "C" { +#endif + +extern volatile uint32_t timer_count; + +void timer_init(void); +void timer_clear(void); +uint16_t timer_read(void); +uint32_t timer_read32(void); +uint16_t timer_elapsed(uint16_t last); +uint32_t timer_elapsed32(uint32_t last); + +// Utility functions to check if a future time has expired & autmatically handle time wrapping if checked / reset frequently (half of max value) +#define timer_expired(current, future) ((uint16_t)(current - future) < UINT16_MAX / 2) +#define timer_expired32(current, future) ((uint32_t)(current - future) < UINT32_MAX / 2) + +// Use an appropriate timer integer size based on architecture (16-bit will overflow sooner) +#if FAST_TIMER_T_SIZE < 32 +# define TIMER_DIFF_FAST(a, b) TIMER_DIFF_16(a, b) +# define timer_expired_fast(current, future) timer_expired(current, future) +typedef uint16_t fast_timer_t; +fast_timer_t inline timer_read_fast(void) { return timer_read(); } +fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed(last); } +#else +# define TIMER_DIFF_FAST(a, b) TIMER_DIFF_32(a, b) +# define timer_expired_fast(current, future) timer_expired32(current, future) +typedef uint32_t fast_timer_t; +fast_timer_t inline timer_read_fast(void) { return timer_read32(); } +fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed32(last); } +#endif + +#ifdef __cplusplus +} +#endif diff --git a/platforms/wait.h b/platforms/wait.h new file mode 100644 index 0000000000..cf7180fb07 --- /dev/null +++ b/platforms/wait.h @@ -0,0 +1,30 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if __has_include_next("_wait.h") +# include_next "_wait.h" /* Include the platforms _wait.h */ +#endif + +#ifdef __cplusplus +} +#endif -- cgit v1.2.3