aboutsummaryrefslogtreecommitdiff
path: root/platforms/chibios
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/chibios')
-rw-r--r--platforms/chibios/_timer.h19
-rw-r--r--platforms/chibios/_wait.c89
-rw-r--r--platforms/chibios/_wait.h60
-rw-r--r--platforms/chibios/atomic_util.h37
-rw-r--r--platforms/chibios/bootloader.c145
-rw-r--r--platforms/chibios/chibios_config.h78
-rw-r--r--platforms/chibios/eeprom_stm32.c687
-rw-r--r--platforms/chibios/eeprom_stm32.h33
-rw-r--r--platforms/chibios/eeprom_stm32_defs.h74
-rw-r--r--platforms/chibios/eeprom_teensy.c795
-rw-r--r--platforms/chibios/flash_stm32.c208
-rw-r--r--platforms/chibios/flash_stm32.h44
-rw-r--r--platforms/chibios/gd32v_compatibility.h120
-rw-r--r--platforms/chibios/gpio.h50
-rw-r--r--platforms/chibios/pin_defs.h323
-rw-r--r--platforms/chibios/platform.c22
-rw-r--r--platforms/chibios/platform.mk436
-rw-r--r--platforms/chibios/platform_deps.h19
-rw-r--r--platforms/chibios/sleep_led.c192
-rw-r--r--platforms/chibios/suspend.c92
-rw-r--r--platforms/chibios/syscall-fallbacks.c110
-rw-r--r--platforms/chibios/timer.c47
-rw-r--r--platforms/chibios/wait.c41
23 files changed, 3721 insertions, 0 deletions
diff --git a/platforms/chibios/_timer.h b/platforms/chibios/_timer.h
new file mode 100644
index 000000000..77402b612
--- /dev/null
+++ b/platforms/chibios/_timer.h
@@ -0,0 +1,19 @@
1/* Copyright 2021 Simon Arlott
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18// The platform is 32-bit, so prefer 32-bit timers to avoid overflow
19#define FAST_TIMER_T_SIZE 32
diff --git a/platforms/chibios/_wait.c b/platforms/chibios/_wait.c
new file mode 100644
index 000000000..1fbea2dd5
--- /dev/null
+++ b/platforms/chibios/_wait.c
@@ -0,0 +1,89 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef __OPTIMIZE__
18# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed"
19#endif
20
21#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"
22
23__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */
24 /* The argument n must be a constant expression.
25 * That way, compiler optimization will remove unnecessary code. */
26 if (n < 1) {
27 return;
28 }
29 if (n > 8) {
30 unsigned int n8 = n / 8;
31 n = n - n8 * 8;
32 switch (n8) {
33 case 16:
34 asm volatile(CLOCK_DELAY_NOP8::: "memory");
35 case 15:
36 asm volatile(CLOCK_DELAY_NOP8::: "memory");
37 case 14:
38 asm volatile(CLOCK_DELAY_NOP8::: "memory");
39 case 13:
40 asm volatile(CLOCK_DELAY_NOP8::: "memory");
41 case 12:
42 asm volatile(CLOCK_DELAY_NOP8::: "memory");
43 case 11:
44 asm volatile(CLOCK_DELAY_NOP8::: "memory");
45 case 10:
46 asm volatile(CLOCK_DELAY_NOP8::: "memory");
47 case 9:
48 asm volatile(CLOCK_DELAY_NOP8::: "memory");
49 case 8:
50 asm volatile(CLOCK_DELAY_NOP8::: "memory");
51 case 7:
52 asm volatile(CLOCK_DELAY_NOP8::: "memory");
53 case 6:
54 asm volatile(CLOCK_DELAY_NOP8::: "memory");
55 case 5:
56 asm volatile(CLOCK_DELAY_NOP8::: "memory");
57 case 4:
58 asm volatile(CLOCK_DELAY_NOP8::: "memory");
59 case 3:
60 asm volatile(CLOCK_DELAY_NOP8::: "memory");
61 case 2:
62 asm volatile(CLOCK_DELAY_NOP8::: "memory");
63 case 1:
64 asm volatile(CLOCK_DELAY_NOP8::: "memory");
65 case 0:
66 break;
67 }
68 }
69 switch (n) {
70 case 8:
71 asm volatile("nop" ::: "memory");
72 case 7:
73 asm volatile("nop" ::: "memory");
74 case 6:
75 asm volatile("nop" ::: "memory");
76 case 5:
77 asm volatile("nop" ::: "memory");
78 case 4:
79 asm volatile("nop" ::: "memory");
80 case 3:
81 asm volatile("nop" ::: "memory");
82 case 2:
83 asm volatile("nop" ::: "memory");
84 case 1:
85 asm volatile("nop" ::: "memory");
86 case 0:
87 break;
88 }
89}
diff --git a/platforms/chibios/_wait.h b/platforms/chibios/_wait.h
new file mode 100644
index 000000000..2f36c64a2
--- /dev/null
+++ b/platforms/chibios/_wait.h
@@ -0,0 +1,60 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <ch.h>
19#include <hal.h>
20
21/* chThdSleepX of zero maps to infinite - so we map to a tiny delay to still yield */
22#define wait_ms(ms) \
23 do { \
24 if (ms != 0) { \
25 chThdSleepMilliseconds(ms); \
26 } else { \
27 chThdSleepMicroseconds(1); \
28 } \
29 } while (0)
30
31#ifdef WAIT_US_TIMER
32void wait_us(uint16_t duration);
33#else
34# define wait_us(us) \
35 do { \
36 if (us != 0) { \
37 chThdSleepMicroseconds(us); \
38 } else { \
39 chThdSleepMicroseconds(1); \
40 } \
41 } while (0)
42#endif
43
44#include "_wait.c"
45
46/* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus
47 * to which the GPIO is connected.
48 * The connected buses differ depending on the various series of MCUs.
49 * And since the instruction execution clock of the CPU and the bus clock of GPIO are different,
50 * there is a delay of several clocks to read the change of the input signal.
51 *
52 * Define this delay with the GPIO_INPUT_PIN_DELAY macro.
53 * If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used.
54 * (A fairly large value of 0.25 microseconds is set.)
55 */
56#ifndef GPIO_INPUT_PIN_DELAY
57# define GPIO_INPUT_PIN_DELAY (CPU_CLOCK / 1000000L / 4)
58#endif
59
60#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 000000000..897504515
--- /dev/null
+++ b/platforms/chibios/atomic_util.h
@@ -0,0 +1,37 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <ch.h>
19
20static __inline__ uint8_t __interrupt_disable__(void) {
21 chSysLock();
22
23 return 1;
24}
25
26static __inline__ void __interrupt_enable__(const uint8_t *__s) {
27 chSysUnlock();
28
29 __asm__ volatile("" ::: "memory");
30 (void)__s;
31}
32
33#define ATOMIC_BLOCK(type) for (type, __ToDo = __interrupt_disable__(); __ToDo; __ToDo = 0)
34#define ATOMIC_FORCEON uint8_t sreg_save __attribute__((__cleanup__(__interrupt_enable__))) = 0
35
36#define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE not implemented")
37#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 000000000..5cadadeee
--- /dev/null
+++ b/platforms/chibios/bootloader.c
@@ -0,0 +1,145 @@
1#include "bootloader.h"
2
3#include <ch.h>
4#include <hal.h>
5#include "wait.h"
6
7/* This code should be checked whether it runs correctly on platforms */
8#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))
9#define BOOTLOADER_MAGIC 0xDEADBEEF
10#define MAGIC_ADDR (unsigned long *)(SYMVAL(__ram0_end__) - 4)
11
12#ifndef STM32_BOOTLOADER_DUAL_BANK
13# define STM32_BOOTLOADER_DUAL_BANK FALSE
14#endif
15
16#ifdef BOOTLOADER_TINYUF2
17
18# define DBL_TAP_MAGIC 0xf01669ef // From tinyuf2's board_api.h
19
20// defined by linker script
21extern uint32_t _board_dfu_dbl_tap[];
22# define DBL_TAP_REG _board_dfu_dbl_tap[0]
23
24void bootloader_jump(void) {
25 DBL_TAP_REG = DBL_TAP_MAGIC;
26 NVIC_SystemReset();
27}
28
29void enter_bootloader_mode_if_requested(void) { /* not needed, no two-stage reset */
30}
31
32#elif STM32_BOOTLOADER_DUAL_BANK
33
34// Need pin definitions
35# include "config_common.h"
36
37# ifndef STM32_BOOTLOADER_DUAL_BANK_GPIO
38# error "No STM32_BOOTLOADER_DUAL_BANK_GPIO defined, don't know which pin to toggle"
39# endif
40
41# ifndef STM32_BOOTLOADER_DUAL_BANK_POLARITY
42# define STM32_BOOTLOADER_DUAL_BANK_POLARITY 0
43# endif
44
45# ifndef STM32_BOOTLOADER_DUAL_BANK_DELAY
46# define STM32_BOOTLOADER_DUAL_BANK_DELAY 100000
47# endif
48
49extern uint32_t __ram0_end__;
50
51__attribute__((weak)) void bootloader_jump(void) {
52 // For STM32 MCUs with dual-bank flash, and we're incapable of jumping to the bootloader. The first valid flash
53 // bank is executed unconditionally after a reset, so it doesn't enter DFU unless BOOT0 is high. Instead, we do
54 // it with hardware...in this case, we pull a GPIO high/low depending on the configuration, connects 3.3V to
55 // BOOT0's RC charging circuit, lets it charge the capacitor, and issue a system reset. See the QMK discord
56 // #hardware channel pins for an example circuit.
57 palSetPadMode(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_MODE_OUTPUT_PUSHPULL);
58# if STM32_BOOTLOADER_DUAL_BANK_POLARITY
59 palSetPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));
60# else
61 palClearPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));
62# endif
63
64 // Wait for a while for the capacitor to charge
65 wait_ms(100);
66
67 // Issue a system reset to get the ROM bootloader to execute, with BOOT0 high
68 NVIC_SystemReset();
69}
70
71void enter_bootloader_mode_if_requested(void) {} // not needed at all, but if anybody attempts to invoke it....
72
73#elif defined(STM32_BOOTLOADER_ADDRESS) // STM32_BOOTLOADER_DUAL_BANK
74
75extern uint32_t __ram0_end__;
76
77__attribute__((weak)) void bootloader_jump(void) {
78 *MAGIC_ADDR = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader
79 NVIC_SystemReset();
80}
81
82void enter_bootloader_mode_if_requested(void) {
83 unsigned long *check = MAGIC_ADDR;
84 if (*check == BOOTLOADER_MAGIC) {
85 *check = 0;
86 __set_CONTROL(0);
87 __set_MSP(*(__IO uint32_t *)STM32_BOOTLOADER_ADDRESS);
88 __enable_irq();
89
90 typedef void (*BootJump_t)(void);
91 BootJump_t boot_jump = *(BootJump_t *)(STM32_BOOTLOADER_ADDRESS + 4);
92 boot_jump();
93 while (1)
94 ;
95 }
96}
97
98#elif defined(GD32VF103)
99
100# define DBGMCU_KEY_UNLOCK 0x4B5A6978
101# define DBGMCU_CMD_RESET 0x1
102
103__IO uint32_t *DBGMCU_KEY = (uint32_t *)DBGMCU_BASE + 0x0CU;
104__IO uint32_t *DBGMCU_CMD = (uint32_t *)DBGMCU_BASE + 0x08U;
105
106__attribute__((weak)) void bootloader_jump(void) {
107 /* The MTIMER unit of the GD32VF103 doesn't have the MSFRST
108 * register to generate a software reset request.
109 * BUT instead two undocumented registers in the debug peripheral
110 * that allow issueing a software reset. WHO would need the MSFRST
111 * register anyway? Source:
112 * https://github.com/esmil/gd32vf103inator/blob/master/include/gd32vf103/dbg.h */
113 *DBGMCU_KEY = DBGMCU_KEY_UNLOCK;
114 *DBGMCU_CMD = DBGMCU_CMD_RESET;
115}
116
117void enter_bootloader_mode_if_requested(void) { /* Jumping to bootloader is not possible from user code. */
118}
119
120#elif defined(KL2x) || defined(K20x) || defined(MK66F18) || defined(MIMXRT1062) // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS
121/* Kinetis */
122
123# if defined(BOOTLOADER_KIIBOHD)
124/* Kiibohd Bootloader (MCHCK and Infinity KB) */
125# define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000
126const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff";
127__attribute__((weak)) void bootloader_jump(void) {
128 void *volatile vbat = (void *)VBAT;
129 __builtin_memcpy(vbat, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic));
130 // request reset
131 SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk;
132}
133
134# else /* defined(BOOTLOADER_KIIBOHD) */
135/* Default for Kinetis - expecting an ARM Teensy */
136# include "wait.h"
137__attribute__((weak)) void bootloader_jump(void) {
138 wait_ms(100);
139 __BKPT(0);
140}
141# endif /* defined(BOOTLOADER_KIIBOHD) */
142
143#else /* neither STM32 nor KINETIS */
144__attribute__((weak)) void bootloader_jump(void) {}
145#endif
diff --git a/platforms/chibios/chibios_config.h b/platforms/chibios/chibios_config.h
new file mode 100644
index 000000000..ad2f808a9
--- /dev/null
+++ b/platforms/chibios/chibios_config.h
@@ -0,0 +1,78 @@
1/* Copyright 2019
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#ifndef USB_VBUS_PIN
19# define SPLIT_USB_DETECT // Force this on when dedicated pin is not used
20#endif
21
22// STM32 compatibility
23#if defined(MCU_STM32)
24# define CPU_CLOCK STM32_SYSCLK
25
26# if defined(STM32F1XX)
27# define USE_GPIOV1
28# define PAL_MODE_ALTERNATE_OPENDRAIN PAL_MODE_STM32_ALTERNATE_OPENDRAIN
29# define PAL_MODE_ALTERNATE_PUSHPULL PAL_MODE_STM32_ALTERNATE_PUSHPULL
30# else
31# define PAL_OUTPUT_TYPE_OPENDRAIN PAL_STM32_OTYPE_OPENDRAIN
32# define PAL_OUTPUT_TYPE_PUSHPULL PAL_STM32_OTYPE_PUSHPULL
33# define PAL_OUTPUT_SPEED_HIGHEST PAL_STM32_OSPEED_HIGHEST
34# define PAL_PUPDR_FLOATING PAL_STM32_PUPDR_FLOATING
35# endif
36
37# if defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32L1XX)
38# define USE_I2CV1
39# endif
40#endif
41
42// GD32 compatibility
43#if defined(MCU_GD32V)
44# define CPU_CLOCK GD32_SYSCLK
45
46# if defined(GD32VF103)
47# define USE_GPIOV1
48# define USE_I2CV1
49# define PAL_MODE_ALTERNATE_OPENDRAIN PAL_MODE_GD32_ALTERNATE_OPENDRAIN
50# define PAL_MODE_ALTERNATE_PUSHPULL PAL_MODE_GD32_ALTERNATE_PUSHPULL
51# endif
52#endif
53
54#if defined(GD32VF103)
55/* This chip has the same API as STM32F103, but uses different names for literally the same thing.
56 * As of 4.7.2021 QMK is tailored to use STM32 defines/names, for compatibility sake
57 * we just redefine the GD32 names. */
58# include "gd32v_compatibility.h"
59#endif
60
61// teensy compatibility
62#if defined(MCU_KINETIS)
63# define CPU_CLOCK KINETIS_SYSCLK_FREQUENCY
64
65# if defined(K20x) || defined(KL2x)
66# define USE_I2CV1
67# define USE_I2CV1_CONTRIB // for some reason a bunch of ChibiOS-Contrib boards only have clock_speed
68# define USE_GPIOV1
69# endif
70#endif
71
72#if defined(HT32)
73# define CPU_CLOCK HT32_CK_SYS_FREQUENCY
74# define PAL_MODE_ALTERNATE PAL_HT32_MODE_AF
75# define PAL_OUTPUT_TYPE_OPENDRAIN (PAL_HT32_MODE_OD | PAL_HT32_MODE_DIR)
76# define PAL_OUTPUT_TYPE_PUSHPULL PAL_HT32_MODE_DIR
77# define PAL_OUTPUT_SPEED_HIGHEST 0
78#endif
diff --git a/platforms/chibios/eeprom_stm32.c b/platforms/chibios/eeprom_stm32.c
new file mode 100644
index 000000000..acc6a4851
--- /dev/null
+++ b/platforms/chibios/eeprom_stm32.c
@@ -0,0 +1,687 @@
1/*
2 * This software is experimental and a work in progress.
3 * Under no circumstances should these files be used in relation to any critical system(s).
4 * Use of these files is at your own risk.
5 *
6 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
8 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
9 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
10 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
11 * DEALINGS IN THE SOFTWARE.
12 *
13 * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
14 * Artur F.
15 *
16 * Modifications for QMK and STM32F303 by Yiancar
17 * Modifications to add flash wear leveling by Ilya Zhuravlev
18 * Modifications to increase flash density by Don Kjer
19 */
20
21#include <stdio.h>
22#include <stdbool.h>
23#include "util.h"
24#include "debug.h"
25#include "eeprom_stm32.h"
26#include "flash_stm32.h"
27
28/*
29 * We emulate eeprom by writing a snapshot compacted view of eeprom contents,
30 * followed by a write log of any change since that snapshot:
31 *
32 * === SIMULATED EEPROM CONTENTS ===
33 *
34 * ┌─ Compacted ┬ Write Log ─┐
35 * │............│[BYTE][BYTE]│
36 * │FFFF....FFFF│[WRD0][WRD1]│
37 * │FFFFFFFFFFFF│[WORD][NEXT]│
38 * │....FFFFFFFF│[BYTE][WRD0]│
39 * ├────────────┼────────────┤
40 * └──PAGE_BASE │ │
41 * PAGE_LAST─┴─WRITE_BASE │
42 * WRITE_LAST ┘
43 *
44 * Compacted contents are the 1's complement of the actual EEPROM contents.
45 * e.g. An 'FFFF' represents a '0000' value.
46 *
47 * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom.
48 * The size of the compacted-area and write log are configurable, and the combined
49 * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent.
50 * Simulated Eeprom contents are located at the end of available flash space.
51 *
52 * The following configuration defines can be set:
53 *
54 * FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log)
55 * FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT)
56 * NOTE: The current implementation does not include page swapping,
57 * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents.
58 *
59 * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals
60 * FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES.
61 * The larger the write log, the less frequently the compacted area needs to be rewritten.
62 *
63 *
64 * *** General Algorithm ***
65 *
66 * During initialization:
67 * The contents of the Compacted-flash area are loaded and the 1's complement value
68 * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache).
69 * Write log entries are processed until a 0xFFFF is reached.
70 * Each log entry updates a byte or word in the cache.
71 *
72 * During reads:
73 * EEPROM contents are given back directly from the cache in memory.
74 *
75 * During writes:
76 * The contents of the cache is updated first.
77 * 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
78 * Otherwise:
79 * 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.
80 * Otherwise a Write log entry is constructed and appended to the next free position in the Write log.
81 *
82 *
83 * *** Write Log Structure ***
84 *
85 * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned.
86 *
87 * === WRITE LOG ENTRY FORMATS ===
88 *
89 * ╔═══ Byte-Entry ══╗
90 * ║0XXXXXXX║YYYYYYYY║
91 * ║ └──┬──┘║└──┬───┘║
92 * ║ Address║ Value ║
93 * ╚════════╩════════╝
94 * 0 <= Address < 0x80 (128)
95 *
96 * ╔ Word-Encoded 0 ╗
97 * ║100XXXXXXXXXXXXX║
98 * ║ │└─────┬─────┘║
99 * ║ │Address >> 1 ║
100 * ║ └── Value: 0 ║
101 * ╚════════════════╝
102 * 0 <= Address <= 0x3FFE (16382)
103 *
104 * ╔ Word-Encoded 1 ╗
105 * ║101XXXXXXXXXXXXX║
106 * ║ │└─────┬─────┘║
107 * ║ │Address >> 1 ║
108 * ║ └── Value: 1 ║
109 * ╚════════════════╝
110 * 0 <= Address <= 0x3FFE (16382)
111 *
112 * ╔═══ Reserved ═══╗
113 * ║110XXXXXXXXXXXXX║
114 * ╚════════════════╝
115 *
116 * ╔═══════════ Word-Next ═══════════╗
117 * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║
118 * ║ └─────┬─────┘║└───────┬──────┘║
119 * ║(Address-128)>>1║ ~Value ║
120 * ╚════════════════╩════════════════╝
121 * ( 0 <= Address < 0x0080 (128): Reserved)
122 * 0x80 <= Address <= 0x3FFE (16382)
123 *
124 * Write Log entry ranges:
125 * 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF)
126 * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0
127 * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1
128 * 0xC000 ... 0xDFFF - Reserved
129 * 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry)
130 * 0xFFC0 ... 0xFFFE - Reserved
131 * 0xFFFF - Unprogrammed
132 *
133 */
134
135#include "eeprom_stm32_defs.h"
136#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS)
137# error "not implemented."
138#endif
139
140/* These bits are used for optimizing encoding of bytes, 0 and 1 */
141#define FEE_WORD_ENCODING 0x8000
142#define FEE_VALUE_NEXT 0x6000
143#define FEE_VALUE_RESERVED 0x4000
144#define FEE_VALUE_ENCODED 0x2000
145#define FEE_BYTE_RANGE 0x80
146
147/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */
148#define FEE_ADDRESS_MAX_SIZE 0x4000
149
150/* Flash word value after erase */
151#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
152
153/* Size of combined compacted eeprom and write log pages */
154#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE)
155
156#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */
157# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024)
158# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024)
159# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size
160# endif
161#endif
162
163/* Size of emulated eeprom */
164#ifdef FEE_DENSITY_BYTES
165# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE)
166# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
167# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE
168# endif
169# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE)
170# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE)
171# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate!
172# endif
173# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE
174# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE)
175# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows
176# endif
177# if ((FEE_DENSITY_BYTES) % 2) == 1
178# error emulated eeprom: FEE_DENSITY_BYTES must be even
179# endif
180#else
181/* Default to half of allocated space used for emulated eeprom, half for write log */
182# define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2)
183#endif
184
185/* Size of write log */
186#ifdef FEE_WRITE_LOG_BYTES
187# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE)
188# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
189# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE
190# endif
191# if ((FEE_WRITE_LOG_BYTES) % 2) == 1
192# error emulated eeprom: FEE_WRITE_LOG_BYTES must be even
193# endif
194#else
195/* Default to use all remaining space */
196# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES)
197#endif
198
199/* Start of the emulated eeprom compacted flash area */
200#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS
201/* End of the emulated eeprom compacted flash area */
202#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES)
203/* Start of the emulated eeprom write log */
204#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS
205/* End of the emulated eeprom write log */
206#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES)
207
208#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES)
209# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available
210#endif
211
212/* In-memory contents of emulated eeprom for faster access */
213/* *TODO: Implement page swapping */
214static uint16_t WordBuf[FEE_DENSITY_BYTES / 2];
215static uint8_t *DataBuf = (uint8_t *)WordBuf;
216
217/* Pointer to the first available slot within the write log */
218static uint16_t *empty_slot;
219
220// #define DEBUG_EEPROM_OUTPUT
221
222/*
223 * Debug print utils
224 */
225
226#if defined(DEBUG_EEPROM_OUTPUT)
227
228# define debug_eeprom debug_enable
229# define eeprom_println(s) println(s)
230# define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__);
231
232#else /* NO_DEBUG */
233
234# define debug_eeprom false
235# define eeprom_println(s)
236# define eeprom_printf(fmt, ...)
237
238#endif /* NO_DEBUG */
239
240void print_eeprom(void) {
241#ifndef NO_DEBUG
242 int empty_rows = 0;
243 for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) {
244 if (i % 16 == 0) {
245 if (i >= FEE_DENSITY_BYTES - 16) {
246 /* Make sure we display the last row */
247 empty_rows = 0;
248 }
249 /* Check if this row is uninitialized */
250 ++empty_rows;
251 for (uint16_t j = 0; j < 16; j++) {
252 if (DataBuf[i + j]) {
253 empty_rows = 0;
254 break;
255 }
256 }
257 if (empty_rows > 1) {
258 /* Repeat empty row */
259 if (empty_rows == 2) {
260 /* Only display the first repeat empty row */
261 println("*");
262 }
263 i += 15;
264 continue;
265 }
266 xprintf("%04x", i);
267 }
268 if (i % 8 == 0) print(" ");
269
270 xprintf(" %02x", DataBuf[i]);
271 if ((i + 1) % 16 == 0) {
272 println("");
273 }
274 }
275#endif
276}
277
278uint16_t EEPROM_Init(void) {
279 /* Load emulated eeprom contents from compacted flash into memory */
280 uint16_t *src = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS;
281 uint16_t *dest = (uint16_t *)DataBuf;
282 for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) {
283 *dest = ~*src;
284 }
285
286 if (debug_eeprom) {
287 println("EEPROM_Init Compacted Pages:");
288 print_eeprom();
289 println("EEPROM_Init Write Log:");
290 }
291
292 /* Replay write log */
293 uint16_t *log_addr;
294 for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) {
295 uint16_t address = *log_addr;
296 if (address == FEE_EMPTY_WORD) {
297 break;
298 }
299 /* Check for lowest 128-bytes optimization */
300 if (!(address & FEE_WORD_ENCODING)) {
301 uint8_t bvalue = (uint8_t)address;
302 address >>= 8;
303 DataBuf[address] = bvalue;
304 eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue);
305 } else {
306 uint16_t wvalue;
307 /* Check if value is in next word */
308 if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) {
309 /* Read value from next word */
310 if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
311 break;
312 }
313 wvalue = ~*log_addr;
314 if (!wvalue) {
315 eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr);
316 /* Possibly incomplete write. Ignore and continue */
317 continue;
318 }
319 address &= 0x1FFF;
320 address <<= 1;
321 /* Writes to addresses less than 128 are byte log entries */
322 address += FEE_BYTE_RANGE;
323 } else {
324 /* Reserved for future use */
325 if (address & FEE_VALUE_RESERVED) {
326 eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr);
327 continue;
328 }
329 /* Optimization for 0 or 1 values. */
330 wvalue = (address & FEE_VALUE_ENCODED) >> 13;
331 address &= 0x1FFF;
332 address <<= 1;
333 }
334 if (address < FEE_DENSITY_BYTES) {
335 eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue);
336 *(uint16_t *)(&DataBuf[address]) = wvalue;
337 } else {
338 eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue);
339 }
340 }
341 }
342
343 empty_slot = log_addr;
344
345 if (debug_eeprom) {
346 println("EEPROM_Init Final DataBuf:");
347 print_eeprom();
348 }
349
350 return FEE_DENSITY_BYTES;
351}
352
353/* Clear flash contents (doesn't touch in-memory DataBuf) */
354static void eeprom_clear(void) {
355 FLASH_Unlock();
356
357 for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) {
358 eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)));
359 FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
360 }
361
362 FLASH_Lock();
363
364 empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS;
365 eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot);
366}
367
368/* Erase emulated eeprom */
369void EEPROM_Erase(void) {
370 eeprom_println("EEPROM_Erase");
371 /* Erase compacted pages and write log */
372 eeprom_clear();
373 /* re-initialize to reset DataBuf */
374 EEPROM_Init();
375}
376
377/* Compact write log */
378static uint8_t eeprom_compact(void) {
379 /* Erase compacted pages and write log */
380 eeprom_clear();
381
382 FLASH_Unlock();
383
384 FLASH_Status final_status = FLASH_COMPLETE;
385
386 /* Write emulated eeprom contents from memory to compacted flash */
387 uint16_t *src = (uint16_t *)DataBuf;
388 uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS;
389 uint16_t value;
390 for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) {
391 value = *src;
392 if (value) {
393 eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value);
394 FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value);
395 if (status != FLASH_COMPLETE) final_status = status;
396 }
397 }
398
399 FLASH_Lock();
400
401 if (debug_eeprom) {
402 println("eeprom_compacted:");
403 print_eeprom();
404 }
405
406 return final_status;
407}
408
409static uint8_t eeprom_write_direct_entry(uint16_t Address) {
410 /* Check if we can just write this directly to the compacted flash area */
411 uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE);
412 if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) {
413 /* Write the value directly to the compacted area without a log entry */
414 uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]);
415 /* Early exit if a write isn't needed */
416 if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE;
417
418 FLASH_Unlock();
419
420 eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value);
421 FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value);
422
423 FLASH_Lock();
424 return status;
425 }
426 return 0;
427}
428
429static uint8_t eeprom_write_log_word_entry(uint16_t Address) {
430 FLASH_Status final_status = FLASH_COMPLETE;
431
432 uint16_t value = *(uint16_t *)(&DataBuf[Address]);
433 eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value);
434
435 /* MSB signifies the lowest 128-byte optimization is not in effect */
436 uint16_t encoding = FEE_WORD_ENCODING;
437 uint8_t entry_size;
438 if (value <= 1) {
439 encoding |= value << 13;
440 entry_size = 2;
441 } else {
442 encoding |= FEE_VALUE_NEXT;
443 entry_size = 4;
444 /* Writes to addresses less than 128 are byte log entries */
445 Address -= FEE_BYTE_RANGE;
446 }
447
448 /* if we can't find an empty spot, we must compact emulated eeprom */
449 if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) {
450 /* compact the write log into the compacted flash area */
451 return eeprom_compact();
452 }
453
454 /* Word log writes should be word-aligned. Take back a bit */
455 Address >>= 1;
456 Address |= encoding;
457
458 /* ok we found a place let's write our data */
459 FLASH_Unlock();
460
461 /* address */
462 eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address);
463 final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address);
464
465 /* value */
466 if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) {
467 eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value);
468 FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value);
469 if (status != FLASH_COMPLETE) final_status = status;
470 }
471
472 FLASH_Lock();
473
474 return final_status;
475}
476
477static uint8_t eeprom_write_log_byte_entry(uint16_t Address) {
478 eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]);
479
480 /* if couldn't find an empty spot, we must compact emulated eeprom */
481 if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
482 /* compact the write log into the compacted flash area */
483 return eeprom_compact();
484 }
485
486 /* ok we found a place let's write our data */
487 FLASH_Unlock();
488
489 /* Pack address and value into the same word */
490 uint16_t value = (Address << 8) | DataBuf[Address];
491
492 /* write to flash */
493 eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value);
494 FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value);
495
496 FLASH_Lock();
497
498 return status;
499}
500
501uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
502 /* if the address is out-of-bounds, do nothing */
503 if (Address >= FEE_DENSITY_BYTES) {
504 eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte);
505 return FLASH_BAD_ADDRESS;
506 }
507
508 /* if the value is the same, don't bother writing it */
509 if (DataBuf[Address] == DataByte) {
510 eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte);
511 return 0;
512 }
513
514 /* keep DataBuf cache in sync */
515 DataBuf[Address] = DataByte;
516 eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]);
517
518 /* perform the write into flash memory */
519 /* First, attempt to write directly into the compacted flash area */
520 FLASH_Status status = eeprom_write_direct_entry(Address);
521 if (!status) {
522 /* Otherwise append to the write log */
523 if (Address < FEE_BYTE_RANGE) {
524 status = eeprom_write_log_byte_entry(Address);
525 } else {
526 status = eeprom_write_log_word_entry(Address & 0xFFFE);
527 }
528 }
529 if (status != 0 && status != FLASH_COMPLETE) {
530 eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status);
531 }
532 return status;
533}
534
535uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) {
536 /* if the address is out-of-bounds, do nothing */
537 if (Address >= FEE_DENSITY_BYTES) {
538 eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord);
539 return FLASH_BAD_ADDRESS;
540 }
541
542 /* Check for word alignment */
543 FLASH_Status final_status = FLASH_COMPLETE;
544 if (Address % 2) {
545 final_status = EEPROM_WriteDataByte(Address, DataWord);
546 FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8);
547 if (status != FLASH_COMPLETE) final_status = status;
548 if (final_status != 0 && final_status != FLASH_COMPLETE) {
549 eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
550 }
551 return final_status;
552 }
553
554 /* if the value is the same, don't bother writing it */
555 uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]);
556 if (oldValue == DataWord) {
557 eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord);
558 return 0;
559 }
560
561 /* keep DataBuf cache in sync */
562 *(uint16_t *)(&DataBuf[Address]) = DataWord;
563 eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address]));
564
565 /* perform the write into flash memory */
566 /* First, attempt to write directly into the compacted flash area */
567 final_status = eeprom_write_direct_entry(Address);
568 if (!final_status) {
569 /* Otherwise append to the write log */
570 /* Check if we need to fall back to byte write */
571 if (Address < FEE_BYTE_RANGE) {
572 final_status = FLASH_COMPLETE;
573 /* Only write a byte if it has changed */
574 if ((uint8_t)oldValue != (uint8_t)DataWord) {
575 final_status = eeprom_write_log_byte_entry(Address);
576 }
577 FLASH_Status status = FLASH_COMPLETE;
578 /* Only write a byte if it has changed */
579 if ((oldValue >> 8) != (DataWord >> 8)) {
580 status = eeprom_write_log_byte_entry(Address + 1);
581 }
582 if (status != FLASH_COMPLETE) final_status = status;
583 } else {
584 final_status = eeprom_write_log_word_entry(Address);
585 }
586 }
587 if (final_status != 0 && final_status != FLASH_COMPLETE) {
588 eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
589 }
590 return final_status;
591}
592
593uint8_t EEPROM_ReadDataByte(uint16_t Address) {
594 uint8_t DataByte = 0xFF;
595
596 if (Address < FEE_DENSITY_BYTES) {
597 DataByte = DataBuf[Address];
598 }
599
600 eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte);
601
602 return DataByte;
603}
604
605uint16_t EEPROM_ReadDataWord(uint16_t Address) {
606 uint16_t DataWord = 0xFFFF;
607
608 if (Address < FEE_DENSITY_BYTES - 1) {
609 /* Check word alignment */
610 if (Address % 2) {
611 DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8);
612 } else {
613 DataWord = *(uint16_t *)(&DataBuf[Address]);
614 }
615 }
616
617 eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord);
618
619 return DataWord;
620}
621
622/*****************************************************************************
623 * Bind to eeprom_driver.c
624 *******************************************************************************/
625void eeprom_driver_init(void) { EEPROM_Init(); }
626
627void eeprom_driver_erase(void) { EEPROM_Erase(); }
628
629void eeprom_read_block(void *buf, const void *addr, size_t len) {
630 const uint8_t *src = (const uint8_t *)addr;
631 uint8_t * dest = (uint8_t *)buf;
632
633 /* Check word alignment */
634 if (len && (uintptr_t)src % 2) {
635 /* Read the unaligned first byte */
636 *dest++ = EEPROM_ReadDataByte((const uintptr_t)src++);
637 --len;
638 }
639
640 uint16_t value;
641 bool aligned = ((uintptr_t)dest % 2 == 0);
642 while (len > 1) {
643 value = EEPROM_ReadDataWord((const uintptr_t)((uint16_t *)src));
644 if (aligned) {
645 *(uint16_t *)dest = value;
646 dest += 2;
647 } else {
648 *dest++ = value;
649 *dest++ = value >> 8;
650 }
651 src += 2;
652 len -= 2;
653 }
654 if (len) {
655 *dest = EEPROM_ReadDataByte((const uintptr_t)src);
656 }
657}
658
659void eeprom_write_block(const void *buf, void *addr, size_t len) {
660 uint8_t * dest = (uint8_t *)addr;
661 const uint8_t *src = (const uint8_t *)buf;
662
663 /* Check word alignment */
664 if (len && (uintptr_t)dest % 2) {
665 /* Write the unaligned first byte */
666 EEPROM_WriteDataByte((uintptr_t)dest++, *src++);
667 --len;
668 }
669
670 uint16_t value;
671 bool aligned = ((uintptr_t)src % 2 == 0);
672 while (len > 1) {
673 if (aligned) {
674 value = *(uint16_t *)src;
675 } else {
676 value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8);
677 }
678 EEPROM_WriteDataWord((uintptr_t)((uint16_t *)dest), value);
679 dest += 2;
680 src += 2;
681 len -= 2;
682 }
683
684 if (len) {
685 EEPROM_WriteDataByte((uintptr_t)dest, *src);
686 }
687}
diff --git a/platforms/chibios/eeprom_stm32.h b/platforms/chibios/eeprom_stm32.h
new file mode 100644
index 000000000..8fcfb556b
--- /dev/null
+++ b/platforms/chibios/eeprom_stm32.h
@@ -0,0 +1,33 @@
1/*
2 * This software is experimental and a work in progress.
3 * Under no circumstances should these files be used in relation to any critical system(s).
4 * Use of these files is at your own risk.
5 *
6 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
8 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
9 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
10 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
11 * DEALINGS IN THE SOFTWARE.
12 *
13 * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
14 * Artur F.
15 *
16 * Modifications for QMK and STM32F303 by Yiancar
17 *
18 * This library assumes 8-bit data locations. To add a new MCU, please provide the flash
19 * page size and the total flash size in Kb. The number of available pages must be a multiple
20 * of 2. Only half of the pages account for the total EEPROM size.
21 * This library also assumes that the pages are not used by the firmware.
22 */
23
24#pragma once
25
26uint16_t EEPROM_Init(void);
27void EEPROM_Erase(void);
28uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);
29uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord);
30uint8_t EEPROM_ReadDataByte(uint16_t Address);
31uint16_t EEPROM_ReadDataWord(uint16_t Address);
32
33void print_eeprom(void);
diff --git a/platforms/chibios/eeprom_stm32_defs.h b/platforms/chibios/eeprom_stm32_defs.h
new file mode 100644
index 000000000..66904f247
--- /dev/null
+++ b/platforms/chibios/eeprom_stm32_defs.h
@@ -0,0 +1,74 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <hal.h>
19
20#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT)
21# if defined(STM32F103xB) || defined(STM32F042x6) || defined(GD32VF103C8) || defined(GD32VF103CB)
22# ifndef FEE_PAGE_SIZE
23# define FEE_PAGE_SIZE 0x400 // Page size = 1KByte
24# endif
25# ifndef FEE_PAGE_COUNT
26# define FEE_PAGE_COUNT 2 // How many pages are used
27# endif
28# elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F072xB) || defined(STM32F070xB)
29# ifndef FEE_PAGE_SIZE
30# define FEE_PAGE_SIZE 0x800 // Page size = 2KByte
31# endif
32# ifndef FEE_PAGE_COUNT
33# define FEE_PAGE_COUNT 4 // How many pages are used
34# endif
35# elif defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE)
36# ifndef FEE_PAGE_SIZE
37# define FEE_PAGE_SIZE 0x4000 // Page size = 16KByte
38# endif
39# ifndef FEE_PAGE_COUNT
40# define FEE_PAGE_COUNT 1 // How many pages are used
41# endif
42# endif
43#endif
44
45#if !defined(FEE_MCU_FLASH_SIZE)
46# if defined(STM32F042x6)
47# define FEE_MCU_FLASH_SIZE 32 // Size in Kb
48# elif defined(GD32VF103C8)
49# define FEE_MCU_FLASH_SIZE 64 // Size in Kb
50# elif defined(STM32F103xB) || defined(STM32F072xB) || defined(STM32F070xB) || defined(GD32VF103CB)
51# define FEE_MCU_FLASH_SIZE 128 // Size in Kb
52# elif defined(STM32F303xC) || defined(STM32F401xC)
53# define FEE_MCU_FLASH_SIZE 256 // Size in Kb
54# elif defined(STM32F103xE) || defined(STM32F401xE) || defined(STM32F411xE)
55# define FEE_MCU_FLASH_SIZE 512 // Size in Kb
56# elif defined(STM32F405xG)
57# define FEE_MCU_FLASH_SIZE 1024 // Size in Kb
58# endif
59#endif
60
61/* Start of the emulated eeprom */
62#if !defined(FEE_PAGE_BASE_ADDRESS)
63# if defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE)
64# ifndef FEE_PAGE_BASE_ADDRESS
65# define FEE_PAGE_BASE_ADDRESS 0x08004000 // bodge to force 2nd 16k page
66# endif
67# else
68# ifndef FEE_FLASH_BASE
69# define FEE_FLASH_BASE 0x8000000
70# endif
71/* Default to end of flash */
72# define FEE_PAGE_BASE_ADDRESS ((uintptr_t)(FEE_FLASH_BASE) + FEE_MCU_FLASH_SIZE * 1024 - (FEE_PAGE_COUNT * FEE_PAGE_SIZE))
73# endif
74#endif
diff --git a/platforms/chibios/eeprom_teensy.c b/platforms/chibios/eeprom_teensy.c
new file mode 100644
index 000000000..97da6f9e1
--- /dev/null
+++ b/platforms/chibios/eeprom_teensy.c
@@ -0,0 +1,795 @@
1#include <ch.h>
2#include <hal.h>
3
4#include "eeconfig.h"
5
6/*************************************/
7/* Hardware backend */
8/* */
9/* Code from PJRC/Teensyduino */
10/*************************************/
11
12/* Teensyduino Core Library
13 * http://www.pjrc.com/teensy/
14 * Copyright (c) 2013 PJRC.COM, LLC.
15 *
16 * Permission is hereby granted, free of charge, to any person obtaining
17 * a copy of this software and associated documentation files (the
18 * "Software"), to deal in the Software without restriction, including
19 * without limitation the rights to use, copy, modify, merge, publish,
20 * distribute, sublicense, and/or sell copies of the Software, and to
21 * permit persons to whom the Software is furnished to do so, subject to
22 * the following conditions:
23 *
24 * 1. The above copyright notice and this permission notice shall be
25 * included in all copies or substantial portions of the Software.
26 *
27 * 2. If the Software is incorporated into a build system that allows
28 * selection among a list of target devices, then similar target
29 * devices manufactured by PJRC.COM must be included in the list of
30 * target devices and selectable in the same manner.
31 *
32 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
36 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
37 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
38 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39 * SOFTWARE.
40 */
41
42#define SMC_PMSTAT_RUN ((uint8_t)0x01)
43#define SMC_PMSTAT_HSRUN ((uint8_t)0x80)
44
45#define F_CPU KINETIS_SYSCLK_FREQUENCY
46
47static inline int kinetis_hsrun_disable(void) {
48#if defined(MK66F18)
49 if (SMC->PMSTAT == SMC_PMSTAT_HSRUN) {
50// First, reduce the CPU clock speed, but do not change
51// the peripheral speed (F_BUS). Serial1 & Serial2 baud
52// rates will be impacted, but most other peripherals
53// will continue functioning at the same speed.
54# if F_CPU == 256000000 && F_BUS == 64000000
55 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // TODO: TEST
56# elif F_CPU == 256000000 && F_BUS == 128000000
57 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // TODO: TEST
58# elif F_CPU == 240000000 && F_BUS == 60000000
59 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // ok
60# elif F_CPU == 240000000 && F_BUS == 80000000
61 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok
62# elif F_CPU == 240000000 && F_BUS == 120000000
63 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok
64# elif F_CPU == 216000000 && F_BUS == 54000000
65 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // ok
66# elif F_CPU == 216000000 && F_BUS == 72000000
67 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok
68# elif F_CPU == 216000000 && F_BUS == 108000000
69 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok
70# elif F_CPU == 192000000 && F_BUS == 48000000
71 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // ok
72# elif F_CPU == 192000000 && F_BUS == 64000000
73 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok
74# elif F_CPU == 192000000 && F_BUS == 96000000
75 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok
76# elif F_CPU == 180000000 && F_BUS == 60000000
77 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok
78# elif F_CPU == 180000000 && F_BUS == 90000000
79 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok
80# elif F_CPU == 168000000 && F_BUS == 56000000
81 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 5); // ok
82# elif F_CPU == 144000000 && F_BUS == 48000000
83 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 5); // ok
84# elif F_CPU == 144000000 && F_BUS == 72000000
85 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 5); // ok
86# elif F_CPU == 120000000 && F_BUS == 60000000
87 SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV1(KINETIS_CLKDIV1_OUTDIV1 - 1) | SIM_CLKDIV1_OUTDIV2(KINETIS_CLKDIV1_OUTDIV2 - 1) |
88# if defined(MK66F18)
89 SIM_CLKDIV1_OUTDIV3(KINETIS_CLKDIV1_OUTDIV3 - 1) |
90# endif
91 SIM_CLKDIV1_OUTDIV4(KINETIS_CLKDIV1_OUTDIV4 - 1);
92# else
93 return 0;
94# endif
95 // Then turn off HSRUN mode
96 SMC->PMCTRL = SMC_PMCTRL_RUNM_SET(0);
97 while (SMC->PMSTAT == SMC_PMSTAT_HSRUN)
98 ; // wait
99 return 1;
100 }
101#endif
102 return 0;
103}
104
105static inline int kinetis_hsrun_enable(void) {
106#if defined(MK66F18)
107 if (SMC->PMSTAT == SMC_PMSTAT_RUN) {
108 // Turn HSRUN mode on
109 SMC->PMCTRL = SMC_PMCTRL_RUNM_SET(3);
110 while (SMC->PMSTAT != SMC_PMSTAT_HSRUN) {
111 ;
112 } // wait
113// Then configure clock for full speed
114# if F_CPU == 256000000 && F_BUS == 64000000
115 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7);
116# elif F_CPU == 256000000 && F_BUS == 128000000
117 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7);
118# elif F_CPU == 240000000 && F_BUS == 60000000
119 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7);
120# elif F_CPU == 240000000 && F_BUS == 80000000
121 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 7);
122# elif F_CPU == 240000000 && F_BUS == 120000000
123 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7);
124# elif F_CPU == 216000000 && F_BUS == 54000000
125 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7);
126# elif F_CPU == 216000000 && F_BUS == 72000000
127 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 7);
128# elif F_CPU == 216000000 && F_BUS == 108000000
129 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7);
130# elif F_CPU == 192000000 && F_BUS == 48000000
131 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 6);
132# elif F_CPU == 192000000 && F_BUS == 64000000
133 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 6);
134# elif F_CPU == 192000000 && F_BUS == 96000000
135 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 6);
136# elif F_CPU == 180000000 && F_BUS == 60000000
137 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 6);
138# elif F_CPU == 180000000 && F_BUS == 90000000
139 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 6);
140# elif F_CPU == 168000000 && F_BUS == 56000000
141 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 5);
142# elif F_CPU == 144000000 && F_BUS == 48000000
143 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 4);
144# elif F_CPU == 144000000 && F_BUS == 72000000
145 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 4);
146# elif F_CPU == 120000000 && F_BUS == 60000000
147 SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV1(KINETIS_CLKDIV1_OUTDIV1 - 1) | SIM_CLKDIV1_OUTDIV2(KINETIS_CLKDIV1_OUTDIV2 - 1) |
148# if defined(MK66F18)
149 SIM_CLKDIV1_OUTDIV3(KINETIS_CLKDIV1_OUTDIV3 - 1) |
150# endif
151 SIM_CLKDIV1_OUTDIV4(KINETIS_CLKDIV1_OUTDIV4 - 1);
152# else
153 return 0;
154# endif
155 return 1;
156 }
157#endif
158 return 0;
159}
160
161#if defined(K20x) || defined(MK66F18) /* chip selection */
162/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */
163
164// The EEPROM is really RAM with a hardware-based backup system to
165// flash memory. Selecting a smaller size EEPROM allows more wear
166// leveling, for higher write endurance. If you edit this file,
167// set this to the smallest size your application can use. Also,
168// due to Freescale's implementation, writing 16 or 32 bit words
169// (aligned to 2 or 4 byte boundaries) has twice the endurance
170// compared to writing 8 bit bytes.
171//
172# ifndef EEPROM_SIZE
173# define EEPROM_SIZE 32
174# endif
175
176/*
177 ^^^ Here be dragons:
178 NXP AppNote AN4282 section 3.1 states that partitioning must only be done once.
179 Once EEPROM partitioning is done, the size is locked to this initial configuration.
180 Attempts to modify the EEPROM_SIZE setting may brick your board.
181*/
182
183// Writing unaligned 16 or 32 bit data is handled automatically when
184// this is defined, but at a cost of extra code size. Without this,
185// any unaligned write will cause a hard fault exception! If you're
186// absolutely sure all 16 and 32 bit writes will be aligned, you can
187// remove the extra unnecessary code.
188//
189# define HANDLE_UNALIGNED_WRITES
190
191# if defined(K20x)
192# define EEPROM_MAX 2048
193# define EEPARTITION 0x03 // all 32K dataflash for EEPROM, none for Data
194# define EEESPLIT 0x30 // must be 0x30 on these chips
195# elif defined(MK66F18)
196# define EEPROM_MAX 4096
197# define EEPARTITION 0x05 // 128K dataflash for EEPROM, 128K for Data
198# define EEESPLIT 0x10 // best endurance: 0x00 = first 12%, 0x10 = first 25%, 0x30 = all equal
199# endif
200
201// Minimum EEPROM Endurance
202// ------------------------
203# if (EEPROM_SIZE == 4096)
204# define EEESIZE 0x02
205# elif (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word
206# define EEESIZE 0x03
207# elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word
208# define EEESIZE 0x04
209# elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word
210# define EEESIZE 0x05
211# elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word
212# define EEESIZE 0x06
213# elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word
214# define EEESIZE 0x07
215# elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word
216# define EEESIZE 0x08
217# elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word
218# define EEESIZE 0x09
219# endif
220
221/** \brief eeprom initialization
222 *
223 * FIXME: needs doc
224 */
225void eeprom_initialize(void) {
226 uint32_t count = 0;
227 uint16_t do_flash_cmd[] = {0xf06f, 0x037f, 0x7003, 0x7803, 0xf013, 0x0f80, 0xd0fb, 0x4770};
228 uint8_t status;
229
230 if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) {
231 uint8_t stat = FTFL->FSTAT & 0x70;
232 if (stat) FTFL->FSTAT = stat;
233
234 // FlexRAM is configured as traditional RAM
235 // We need to reconfigure for EEPROM usage
236 kinetis_hsrun_disable();
237 FTFL->FCCOB0 = 0x80; // PGMPART = Program Partition Command
238 FTFL->FCCOB3 = 0;
239 FTFL->FCCOB4 = EEESPLIT | EEESIZE;
240 FTFL->FCCOB5 = EEPARTITION;
241 __disable_irq();
242 // do_flash_cmd() must execute from RAM. Luckily the C syntax is simple...
243 (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT));
244 __enable_irq();
245 kinetis_hsrun_enable();
246 status = FTFL->FSTAT;
247 if (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)) {
248 FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL));
249 return; // error
250 }
251 }
252 // wait for eeprom to become ready (is this really necessary?)
253 while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) {
254 if (++count > 200000) break;
255 }
256}
257
258# define FlexRAM ((volatile uint8_t *)0x14000000)
259
260/** \brief eeprom read byte
261 *
262 * FIXME: needs doc
263 */
264uint8_t eeprom_read_byte(const uint8_t *addr) {
265 uint32_t offset = (uint32_t)addr;
266 if (offset >= EEPROM_SIZE) return 0;
267 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
268 return FlexRAM[offset];
269}
270
271/** \brief eeprom read word
272 *
273 * FIXME: needs doc
274 */
275uint16_t eeprom_read_word(const uint16_t *addr) {
276 uint32_t offset = (uint32_t)addr;
277 if (offset >= EEPROM_SIZE - 1) return 0;
278 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
279 return *(uint16_t *)(&FlexRAM[offset]);
280}
281
282/** \brief eeprom read dword
283 *
284 * FIXME: needs doc
285 */
286uint32_t eeprom_read_dword(const uint32_t *addr) {
287 uint32_t offset = (uint32_t)addr;
288 if (offset >= EEPROM_SIZE - 3) return 0;
289 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
290 return *(uint32_t *)(&FlexRAM[offset]);
291}
292
293/** \brief eeprom read block
294 *
295 * FIXME: needs doc
296 */
297void eeprom_read_block(void *buf, const void *addr, uint32_t len) {
298 uint32_t offset = (uint32_t)addr;
299 uint8_t *dest = (uint8_t *)buf;
300 uint32_t end = offset + len;
301
302 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
303 if (end > EEPROM_SIZE) end = EEPROM_SIZE;
304 while (offset < end) {
305 *dest++ = FlexRAM[offset++];
306 }
307}
308
309/** \brief eeprom is ready
310 *
311 * FIXME: needs doc
312 */
313int eeprom_is_ready(void) { return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; }
314
315/** \brief flexram wait
316 *
317 * FIXME: needs doc
318 */
319static void flexram_wait(void) {
320 while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) {
321 // TODO: timeout
322 }
323}
324
325/** \brief eeprom_write_byte
326 *
327 * FIXME: needs doc
328 */
329void eeprom_write_byte(uint8_t *addr, uint8_t value) {
330 uint32_t offset = (uint32_t)addr;
331
332 if (offset >= EEPROM_SIZE) return;
333 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
334 if (FlexRAM[offset] != value) {
335 kinetis_hsrun_disable();
336 uint8_t stat = FTFL->FSTAT & 0x70;
337 if (stat) FTFL->FSTAT = stat;
338 FlexRAM[offset] = value;
339 flexram_wait();
340 kinetis_hsrun_enable();
341 }
342}
343
344/** \brief eeprom write word
345 *
346 * FIXME: needs doc
347 */
348void eeprom_write_word(uint16_t *addr, uint16_t value) {
349 uint32_t offset = (uint32_t)addr;
350
351 if (offset >= EEPROM_SIZE - 1) return;
352 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
353# ifdef HANDLE_UNALIGNED_WRITES
354 if ((offset & 1) == 0) {
355# endif
356 if (*(uint16_t *)(&FlexRAM[offset]) != value) {
357 kinetis_hsrun_disable();
358 uint8_t stat = FTFL->FSTAT & 0x70;
359 if (stat) FTFL->FSTAT = stat;
360 *(uint16_t *)(&FlexRAM[offset]) = value;
361 flexram_wait();
362 kinetis_hsrun_enable();
363 }
364# ifdef HANDLE_UNALIGNED_WRITES
365 } else {
366 if (FlexRAM[offset] != value) {
367 kinetis_hsrun_disable();
368 uint8_t stat = FTFL->FSTAT & 0x70;
369 if (stat) FTFL->FSTAT = stat;
370 FlexRAM[offset] = value;
371 flexram_wait();
372 kinetis_hsrun_enable();
373 }
374 if (FlexRAM[offset + 1] != (value >> 8)) {
375 kinetis_hsrun_disable();
376 uint8_t stat = FTFL->FSTAT & 0x70;
377 if (stat) FTFL->FSTAT = stat;
378 FlexRAM[offset + 1] = value >> 8;
379 flexram_wait();
380 kinetis_hsrun_enable();
381 }
382 }
383# endif
384}
385
386/** \brief eeprom write dword
387 *
388 * FIXME: needs doc
389 */
390void eeprom_write_dword(uint32_t *addr, uint32_t value) {
391 uint32_t offset = (uint32_t)addr;
392
393 if (offset >= EEPROM_SIZE - 3) return;
394 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
395# ifdef HANDLE_UNALIGNED_WRITES
396 switch (offset & 3) {
397 case 0:
398# endif
399 if (*(uint32_t *)(&FlexRAM[offset]) != value) {
400 kinetis_hsrun_disable();
401 uint8_t stat = FTFL->FSTAT & 0x70;
402 if (stat) FTFL->FSTAT = stat;
403 *(uint32_t *)(&FlexRAM[offset]) = value;
404 flexram_wait();
405 kinetis_hsrun_enable();
406 }
407 return;
408# ifdef HANDLE_UNALIGNED_WRITES
409 case 2:
410 if (*(uint16_t *)(&FlexRAM[offset]) != value) {
411 kinetis_hsrun_disable();
412 uint8_t stat = FTFL->FSTAT & 0x70;
413 if (stat) FTFL->FSTAT = stat;
414 *(uint16_t *)(&FlexRAM[offset]) = value;
415 flexram_wait();
416 kinetis_hsrun_enable();
417 }
418 if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) {
419 kinetis_hsrun_disable();
420 uint8_t stat = FTFL->FSTAT & 0x70;
421 if (stat) FTFL->FSTAT = stat;
422 *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16;
423 flexram_wait();
424 kinetis_hsrun_enable();
425 }
426 return;
427 default:
428 if (FlexRAM[offset] != value) {
429 kinetis_hsrun_disable();
430 uint8_t stat = FTFL->FSTAT & 0x70;
431 if (stat) FTFL->FSTAT = stat;
432 FlexRAM[offset] = value;
433 flexram_wait();
434 kinetis_hsrun_enable();
435 }
436 if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) {
437 kinetis_hsrun_disable();
438 uint8_t stat = FTFL->FSTAT & 0x70;
439 if (stat) FTFL->FSTAT = stat;
440 *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8;
441 flexram_wait();
442 kinetis_hsrun_enable();
443 }
444 if (FlexRAM[offset + 3] != (value >> 24)) {
445 kinetis_hsrun_disable();
446 uint8_t stat = FTFL->FSTAT & 0x70;
447 if (stat) FTFL->FSTAT = stat;
448 FlexRAM[offset + 3] = value >> 24;
449 flexram_wait();
450 kinetis_hsrun_enable();
451 }
452 }
453# endif
454}
455
456/** \brief eeprom write block
457 *
458 * FIXME: needs doc
459 */
460void eeprom_write_block(const void *buf, void *addr, uint32_t len) {
461 uint32_t offset = (uint32_t)addr;
462 const uint8_t *src = (const uint8_t *)buf;
463
464 if (offset >= EEPROM_SIZE) return;
465 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
466 if (len >= EEPROM_SIZE) len = EEPROM_SIZE;
467 if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset;
468 kinetis_hsrun_disable();
469 while (len > 0) {
470 uint32_t lsb = offset & 3;
471 if (lsb == 0 && len >= 4) {
472 // write aligned 32 bits
473 uint32_t val32;
474 val32 = *src++;
475 val32 |= (*src++ << 8);
476 val32 |= (*src++ << 16);
477 val32 |= (*src++ << 24);
478 if (*(uint32_t *)(&FlexRAM[offset]) != val32) {
479 uint8_t stat = FTFL->FSTAT & 0x70;
480 if (stat) FTFL->FSTAT = stat;
481 *(uint32_t *)(&FlexRAM[offset]) = val32;
482 flexram_wait();
483 }
484 offset += 4;
485 len -= 4;
486 } else if ((lsb == 0 || lsb == 2) && len >= 2) {
487 // write aligned 16 bits
488 uint16_t val16;
489 val16 = *src++;
490 val16 |= (*src++ << 8);
491 if (*(uint16_t *)(&FlexRAM[offset]) != val16) {
492 uint8_t stat = FTFL->FSTAT & 0x70;
493 if (stat) FTFL->FSTAT = stat;
494 *(uint16_t *)(&FlexRAM[offset]) = val16;
495 flexram_wait();
496 }
497 offset += 2;
498 len -= 2;
499 } else {
500 // write 8 bits
501 uint8_t val8 = *src++;
502 if (FlexRAM[offset] != val8) {
503 uint8_t stat = FTFL->FSTAT & 0x70;
504 if (stat) FTFL->FSTAT = stat;
505 FlexRAM[offset] = val8;
506 flexram_wait();
507 }
508 offset++;
509 len--;
510 }
511 }
512 kinetis_hsrun_enable();
513}
514
515/*
516void do_flash_cmd(volatile uint8_t *fstat)
517{
518 *fstat = 0x80;
519 while ((*fstat & 0x80) == 0) ; // wait
520}
52100000000 <do_flash_cmd>:
522 0: f06f 037f mvn.w r3, #127 ; 0x7f
523 4: 7003 strb r3, [r0, #0]
524 6: 7803 ldrb r3, [r0, #0]
525 8: f013 0f80 tst.w r3, #128 ; 0x80
526 c: d0fb beq.n 6 <do_flash_cmd+0x6>
527 e: 4770 bx lr
528*/
529
530#elif defined(KL2x) /* chip selection */
531/* Teensy LC (emulated) */
532
533# define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))
534
535extern uint32_t __eeprom_workarea_start__;
536extern uint32_t __eeprom_workarea_end__;
537
538# define EEPROM_SIZE 128
539
540static uint32_t flashend = 0;
541
542void eeprom_initialize(void) {
543 const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__);
544
545 do {
546 if (*p++ == 0xFFFF) {
547 flashend = (uint32_t)(p - 2);
548 return;
549 }
550 } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__));
551 flashend = (uint32_t)(p - 1);
552}
553
554uint8_t eeprom_read_byte(const uint8_t *addr) {
555 uint32_t offset = (uint32_t)addr;
556 const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__);
557 const uint16_t *end = (const uint16_t *)((uint32_t)flashend);
558 uint16_t val;
559 uint8_t data = 0xFF;
560
561 if (!end) {
562 eeprom_initialize();
563 end = (const uint16_t *)((uint32_t)flashend);
564 }
565 if (offset < EEPROM_SIZE) {
566 while (p <= end) {
567 val = *p++;
568 if ((val & 255) == offset) data = val >> 8;
569 }
570 }
571 return data;
572}
573
574static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) {
575 // with great power comes great responsibility....
576 uint32_t stat;
577 *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC);
578 *(uint32_t *)&(FTFA->FCCOB7) = data;
579 __disable_irq();
580 (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT));
581 __enable_irq();
582 stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL);
583 if (stat) {
584 FTFA->FSTAT = stat;
585 }
586 MCM->PLACR |= MCM_PLACR_CFCC;
587}
588
589void eeprom_write_byte(uint8_t *addr, uint8_t data) {
590 uint32_t offset = (uint32_t)addr;
591 const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend);
592 uint32_t i, val, flashaddr;
593 uint16_t do_flash_cmd[] = {0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770};
594 uint8_t buf[EEPROM_SIZE];
595
596 if (offset >= EEPROM_SIZE) return;
597 if (!end) {
598 eeprom_initialize();
599 end = (const uint16_t *)((uint32_t)flashend);
600 }
601 if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) {
602 val = (data << 8) | offset;
603 flashaddr = (uint32_t)end;
604 flashend = flashaddr;
605 if ((flashaddr & 2) == 0) {
606 val |= 0xFFFF0000;
607 } else {
608 val <<= 16;
609 val |= 0x0000FFFF;
610 }
611 flash_write(do_flash_cmd, flashaddr, val);
612 } else {
613 for (i = 0; i < EEPROM_SIZE; i++) {
614 buf[i] = 0xFF;
615 }
616 val = 0;
617 for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) {
618 val = *p;
619 if ((val & 255) < EEPROM_SIZE) {
620 buf[val & 255] = val >> 8;
621 }
622 }
623 buf[offset] = data;
624 for (flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) {
625 *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr;
626 __disable_irq();
627 (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT));
628 __enable_irq();
629 val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL);
630 ;
631 if (val) FTFA->FSTAT = val;
632 MCM->PLACR |= MCM_PLACR_CFCC;
633 }
634 flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__);
635 for (i = 0; i < EEPROM_SIZE; i++) {
636 if (buf[i] == 0xFF) continue;
637 if ((flashaddr & 2) == 0) {
638 val = (buf[i] << 8) | i;
639 } else {
640 val = val | (buf[i] << 24) | (i << 16);
641 flash_write(do_flash_cmd, flashaddr, val);
642 }
643 flashaddr += 2;
644 }
645 flashend = flashaddr;
646 if ((flashaddr & 2)) {
647 val |= 0xFFFF0000;
648 flash_write(do_flash_cmd, flashaddr, val);
649 }
650 }
651}
652
653/*
654void do_flash_cmd(volatile uint8_t *fstat)
655{
656 *fstat = 0x80;
657 while ((*fstat & 0x80) == 0) ; // wait
658}
65900000000 <do_flash_cmd>:
660 0: 2380 movs r3, #128 ; 0x80
661 2: 7003 strb r3, [r0, #0]
662 4: 7803 ldrb r3, [r0, #0]
663 6: b25b sxtb r3, r3
664 8: 2b00 cmp r3, #0
665 a: dafb bge.n 4 <do_flash_cmd+0x4>
666 c: 4770 bx lr
667*/
668
669uint16_t eeprom_read_word(const uint16_t *addr) {
670 const uint8_t *p = (const uint8_t *)addr;
671 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8);
672}
673
674uint32_t eeprom_read_dword(const uint32_t *addr) {
675 const uint8_t *p = (const uint8_t *)addr;
676 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24);
677}
678
679void eeprom_read_block(void *buf, const void *addr, uint32_t len) {
680 const uint8_t *p = (const uint8_t *)addr;
681 uint8_t * dest = (uint8_t *)buf;
682 while (len--) {
683 *dest++ = eeprom_read_byte(p++);
684 }
685}
686
687int eeprom_is_ready(void) { return 1; }
688
689void eeprom_write_word(uint16_t *addr, uint16_t value) {
690 uint8_t *p = (uint8_t *)addr;
691 eeprom_write_byte(p++, value);
692 eeprom_write_byte(p, value >> 8);
693}
694
695void eeprom_write_dword(uint32_t *addr, uint32_t value) {
696 uint8_t *p = (uint8_t *)addr;
697 eeprom_write_byte(p++, value);
698 eeprom_write_byte(p++, value >> 8);
699 eeprom_write_byte(p++, value >> 16);
700 eeprom_write_byte(p, value >> 24);
701}
702
703void eeprom_write_block(const void *buf, void *addr, uint32_t len) {
704 uint8_t * p = (uint8_t *)addr;
705 const uint8_t *src = (const uint8_t *)buf;
706 while (len--) {
707 eeprom_write_byte(p++, *src++);
708 }
709}
710
711#else
712// No EEPROM supported, so emulate it
713
714# ifndef EEPROM_SIZE
715# include "eeconfig.h"
716# define EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO
717# endif
718__attribute__((aligned(4))) static uint8_t buffer[EEPROM_SIZE];
719
720uint8_t eeprom_read_byte(const uint8_t *addr) {
721 uint32_t offset = (uint32_t)addr;
722 return buffer[offset];
723}
724
725void eeprom_write_byte(uint8_t *addr, uint8_t value) {
726 uint32_t offset = (uint32_t)addr;
727 buffer[offset] = value;
728}
729
730uint16_t eeprom_read_word(const uint16_t *addr) {
731 const uint8_t *p = (const uint8_t *)addr;
732 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8);
733}
734
735uint32_t eeprom_read_dword(const uint32_t *addr) {
736 const uint8_t *p = (const uint8_t *)addr;
737 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24);
738}
739
740void eeprom_read_block(void *buf, const void *addr, size_t len) {
741 const uint8_t *p = (const uint8_t *)addr;
742 uint8_t * dest = (uint8_t *)buf;
743 while (len--) {
744 *dest++ = eeprom_read_byte(p++);
745 }
746}
747
748void eeprom_write_word(uint16_t *addr, uint16_t value) {
749 uint8_t *p = (uint8_t *)addr;
750 eeprom_write_byte(p++, value);
751 eeprom_write_byte(p, value >> 8);
752}
753
754void eeprom_write_dword(uint32_t *addr, uint32_t value) {
755 uint8_t *p = (uint8_t *)addr;
756 eeprom_write_byte(p++, value);
757 eeprom_write_byte(p++, value >> 8);
758 eeprom_write_byte(p++, value >> 16);
759 eeprom_write_byte(p, value >> 24);
760}
761
762void eeprom_write_block(const void *buf, void *addr, size_t len) {
763 uint8_t * p = (uint8_t *)addr;
764 const uint8_t *src = (const uint8_t *)buf;
765 while (len--) {
766 eeprom_write_byte(p++, *src++);
767 }
768}
769
770#endif /* chip selection */
771// The update functions just calls write for now, but could probably be optimized
772
773void eeprom_update_byte(uint8_t *addr, uint8_t value) { eeprom_write_byte(addr, value); }
774
775void eeprom_update_word(uint16_t *addr, uint16_t value) {
776 uint8_t *p = (uint8_t *)addr;
777 eeprom_write_byte(p++, value);
778 eeprom_write_byte(p, value >> 8);
779}
780
781void eeprom_update_dword(uint32_t *addr, uint32_t value) {
782 uint8_t *p = (uint8_t *)addr;
783 eeprom_write_byte(p++, value);
784 eeprom_write_byte(p++, value >> 8);
785 eeprom_write_byte(p++, value >> 16);
786 eeprom_write_byte(p, value >> 24);
787}
788
789void eeprom_update_block(const void *buf, void *addr, size_t len) {
790 uint8_t * p = (uint8_t *)addr;
791 const uint8_t *src = (const uint8_t *)buf;
792 while (len--) {
793 eeprom_write_byte(p++, *src++);
794 }
795}
diff --git a/platforms/chibios/flash_stm32.c b/platforms/chibios/flash_stm32.c
new file mode 100644
index 000000000..72c41b8b7
--- /dev/null
+++ b/platforms/chibios/flash_stm32.c
@@ -0,0 +1,208 @@
1/*
2 * This software is experimental and a work in progress.
3 * Under no circumstances should these files be used in relation to any critical system(s).
4 * Use of these files is at your own risk.
5 *
6 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
8 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
9 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
10 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
11 * DEALINGS IN THE SOFTWARE.
12 *
13 * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and
14 * https://github.com/leaflabs/libmaple
15 *
16 * Modifications for QMK and STM32F303 by Yiancar
17 */
18
19#include <hal.h>
20#include "flash_stm32.h"
21
22#if defined(STM32F1XX)
23# define FLASH_SR_WRPERR FLASH_SR_WRPRTERR
24#endif
25
26#if defined(MCU_GD32V)
27/* GigaDevice GD32VF103 is a STM32F103 clone at heart. */
28# include "gd32v_compatibility.h"
29#endif
30
31#if defined(STM32F4XX)
32# define FLASH_SR_PGERR (FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR)
33
34# define FLASH_KEY1 0x45670123U
35# define FLASH_KEY2 0xCDEF89ABU
36
37static uint8_t ADDR2PAGE(uint32_t Page_Address) {
38 switch (Page_Address) {
39 case 0x08000000 ... 0x08003FFF:
40 return 0;
41 case 0x08004000 ... 0x08007FFF:
42 return 1;
43 case 0x08008000 ... 0x0800BFFF:
44 return 2;
45 case 0x0800C000 ... 0x0800FFFF:
46 return 3;
47 }
48
49 // TODO: bad times...
50 return 7;
51}
52#endif
53
54/* Delay definition */
55#define EraseTimeout ((uint32_t)0x00000FFF)
56#define ProgramTimeout ((uint32_t)0x0000001F)
57
58#define ASSERT(exp) (void)((0))
59
60/**
61 * @brief Inserts a time delay.
62 * @param None
63 * @retval None
64 */
65static void delay(void) {
66 __IO uint32_t i = 0;
67 for (i = 0xFF; i != 0; i--) {
68 }
69}
70
71/**
72 * @brief Returns the FLASH Status.
73 * @param None
74 * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG,
75 * FLASH_ERROR_WRP or FLASH_COMPLETE
76 */
77FLASH_Status FLASH_GetStatus(void) {
78 if ((FLASH->SR & FLASH_SR_BSY) == FLASH_SR_BSY) return FLASH_BUSY;
79
80 if ((FLASH->SR & FLASH_SR_PGERR) != 0) return FLASH_ERROR_PG;
81
82 if ((FLASH->SR & FLASH_SR_WRPERR) != 0) return FLASH_ERROR_WRP;
83
84#if defined(FLASH_OBR_OPTERR)
85 if ((FLASH->SR & FLASH_OBR_OPTERR) != 0) return FLASH_ERROR_OPT;
86#endif
87
88 return FLASH_COMPLETE;
89}
90
91/**
92 * @brief Waits for a Flash operation to complete or a TIMEOUT to occur.
93 * @param Timeout: FLASH progamming Timeout
94 * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
95 * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
96 */
97FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) {
98 FLASH_Status status;
99
100 /* Check for the Flash Status */
101 status = FLASH_GetStatus();
102 /* Wait for a Flash operation to complete or a TIMEOUT to occur */
103 while ((status == FLASH_BUSY) && (Timeout != 0x00)) {
104 delay();
105 status = FLASH_GetStatus();
106 Timeout--;
107 }
108 if (Timeout == 0) status = FLASH_TIMEOUT;
109 /* Return the operation status */
110 return status;
111}
112
113/**
114 * @brief Erases a specified FLASH page.
115 * @param Page_Address: The page address to be erased.
116 * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG,
117 * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
118 */
119FLASH_Status FLASH_ErasePage(uint32_t Page_Address) {
120 FLASH_Status status = FLASH_COMPLETE;
121 /* Check the parameters */
122 ASSERT(IS_FLASH_ADDRESS(Page_Address));
123 /* Wait for last operation to be completed */
124 status = FLASH_WaitForLastOperation(EraseTimeout);
125
126 if (status == FLASH_COMPLETE) {
127 /* if the previous operation is completed, proceed to erase the page */
128#if defined(FLASH_CR_SNB)
129 FLASH->CR &= ~FLASH_CR_SNB;
130 FLASH->CR |= FLASH_CR_SER | (ADDR2PAGE(Page_Address) << FLASH_CR_SNB_Pos);
131#else
132 FLASH->CR |= FLASH_CR_PER;
133 FLASH->AR = Page_Address;
134#endif
135 FLASH->CR |= FLASH_CR_STRT;
136
137 /* Wait for last operation to be completed */
138 status = FLASH_WaitForLastOperation(EraseTimeout);
139 if (status != FLASH_TIMEOUT) {
140 /* if the erase operation is completed, disable the configured Bits */
141#if defined(FLASH_CR_SNB)
142 FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB);
143#else
144 FLASH->CR &= ~FLASH_CR_PER;
145#endif
146 }
147 FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR);
148 }
149 /* Return the Erase Status */
150 return status;
151}
152
153/**
154 * @brief Programs a half word at a specified address.
155 * @param Address: specifies the address to be programmed.
156 * @param Data: specifies the data to be programmed.
157 * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
158 * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
159 */
160FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
161 FLASH_Status status = FLASH_BAD_ADDRESS;
162
163 if (IS_FLASH_ADDRESS(Address)) {
164 /* Wait for last operation to be completed */
165 status = FLASH_WaitForLastOperation(ProgramTimeout);
166 if (status == FLASH_COMPLETE) {
167 /* if the previous operation is completed, proceed to program the new data */
168
169#if defined(FLASH_CR_PSIZE)
170 FLASH->CR &= ~FLASH_CR_PSIZE;
171 FLASH->CR |= FLASH_CR_PSIZE_0;
172#endif
173 FLASH->CR |= FLASH_CR_PG;
174 *(__IO uint16_t*)Address = Data;
175 /* Wait for last operation to be completed */
176 status = FLASH_WaitForLastOperation(ProgramTimeout);
177 if (status != FLASH_TIMEOUT) {
178 /* if the program operation is completed, disable the PG Bit */
179 FLASH->CR &= ~FLASH_CR_PG;
180 }
181 FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR);
182 }
183 }
184 return status;
185}
186
187/**
188 * @brief Unlocks the FLASH Program Erase Controller.
189 * @param None
190 * @retval None
191 */
192void FLASH_Unlock(void) {
193 if (FLASH->CR & FLASH_CR_LOCK) {
194 /* Authorize the FPEC Access */
195 FLASH->KEYR = FLASH_KEY1;
196 FLASH->KEYR = FLASH_KEY2;
197 }
198}
199
200/**
201 * @brief Locks the FLASH Program Erase Controller.
202 * @param None
203 * @retval None
204 */
205void FLASH_Lock(void) {
206 /* Set the Lock Bit to lock the FPEC and the FCR */
207 FLASH->CR |= FLASH_CR_LOCK;
208}
diff --git a/platforms/chibios/flash_stm32.h b/platforms/chibios/flash_stm32.h
new file mode 100644
index 000000000..6c66642ec
--- /dev/null
+++ b/platforms/chibios/flash_stm32.h
@@ -0,0 +1,44 @@
1/*
2 * This software is experimental and a work in progress.
3 * Under no circumstances should these files be used in relation to any critical system(s).
4 * Use of these files is at your own risk.
5 *
6 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
8 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
9 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
10 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
11 * DEALINGS IN THE SOFTWARE.
12 *
13 * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and
14 * https://github.com/leaflabs/libmaple
15 *
16 * Modifications for QMK and STM32F303 by Yiancar
17 */
18
19#pragma once
20
21#ifdef __cplusplus
22extern "C" {
23#endif
24
25#include <stdint.h>
26
27#ifdef FLASH_STM32_MOCKED
28extern uint8_t FlashBuf[MOCK_FLASH_SIZE];
29#endif
30
31typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status;
32
33#define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x0807FFFF))
34
35FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
36FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
37FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
38
39void FLASH_Unlock(void);
40void FLASH_Lock(void);
41
42#ifdef __cplusplus
43}
44#endif
diff --git a/platforms/chibios/gd32v_compatibility.h b/platforms/chibios/gd32v_compatibility.h
new file mode 100644
index 000000000..f4dcfd8c5
--- /dev/null
+++ b/platforms/chibios/gd32v_compatibility.h
@@ -0,0 +1,120 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19/* GD32VF103 has the same API as STM32F103, but uses different names for literally the same thing.
20 * As of 23.7.2021 QMK is tailored to use STM32 defines/names, for compatibility sake
21 * we just redefine the GD32 names. */
22
23/* Close your eyes kids. */
24#define MCU_STM32
25
26/* AFIO redefines */
27#define MAPR PCF0
28#define AFIO_MAPR_USART1_REMAP AFIO_PCF0_USART0_REMAP
29#define AFIO_MAPR_USART2_REMAP AFIO_PCF0_USART1_REMAP
30#define AFIO_MAPR_USART3_REMAP_PARTIALREMAP AFIO_PCF0_USART2_REMAP_PARTIALREMAP
31#define AFIO_MAPR_USART3_REMAP_FULLREMAP AFIO_PCF0_USART2_REMAP_FULLREMAP
32
33/* DMA redefines. */
34#define STM32_DMA_STREAM(stream) GD32_DMA_STREAM(stream)
35#define STM32_DMA_STREAM_ID(peripheral, channel) GD32_DMA_STREAM_ID(peripheral - 1, channel - 1)
36#define STM32_DMA_CR_DIR_M2P GD32_DMA_CTL_DIR_M2P
37#define STM32_DMA_CR_PSIZE_WORD GD32_DMA_CTL_PWIDTH_WORD
38#define STM32_DMA_CR_MSIZE_WORD GD32_DMA_CTL_MWIDTH_WORD
39#define STM32_DMA_CR_MINC GD32_DMA_CTL_MNAGA
40#define STM32_DMA_CR_CIRC GD32_DMA_CTL_CMEN
41#define STM32_DMA_CR_PL GD32_DMA_CTL_PRIO
42#define STM32_DMA_CR_CHSEL GD32_DMA_CTL_CHSEL
43#define cr1 ctl0
44#define cr2 ctl1
45#define cr3 ctl2
46#define dier dmainten
47
48/* ADC redefines */
49#if HAL_USE_ADC
50# define STM32_ADC_USE_ADC1 GD32_ADC_USE_ADC0
51
52# define smpr1 sampt0
53# define smpr2 sampt1
54# define sqr1 rsq0
55# define sqr2 rsq1
56# define sqr3 rsq2
57
58# define ADC_SMPR2_SMP_AN0 ADC_SAMPT1_SMP_SPT0
59# define ADC_SMPR2_SMP_AN1 ADC_SAMPT1_SMP_SPT1
60# define ADC_SMPR2_SMP_AN2 ADC_SAMPT1_SMP_SPT2
61# define ADC_SMPR2_SMP_AN3 ADC_SAMPT1_SMP_SPT3
62# define ADC_SMPR2_SMP_AN4 ADC_SAMPT1_SMP_SPT4
63# define ADC_SMPR2_SMP_AN5 ADC_SAMPT1_SMP_SPT5
64# define ADC_SMPR2_SMP_AN6 ADC_SAMPT1_SMP_SPT6
65# define ADC_SMPR2_SMP_AN7 ADC_SAMPT1_SMP_SPT7
66# define ADC_SMPR2_SMP_AN8 ADC_SAMPT1_SMP_SPT8
67# define ADC_SMPR2_SMP_AN9 ADC_SAMPT1_SMP_SPT9
68
69# define ADC_SMPR1_SMP_AN10 ADC_SAMPT0_SMP_SPT10
70# define ADC_SMPR1_SMP_AN11 ADC_SAMPT0_SMP_SPT11
71# define ADC_SMPR1_SMP_AN12 ADC_SAMPT0_SMP_SPT12
72# define ADC_SMPR1_SMP_AN13 ADC_SAMPT0_SMP_SPT13
73# define ADC_SMPR1_SMP_AN14 ADC_SAMPT0_SMP_SPT14
74# define ADC_SMPR1_SMP_AN15 ADC_SAMPT0_SMP_SPT15
75
76# define ADC_SQR3_SQ1_N ADC_RSQ2_RSQ1_N
77#endif
78
79/* FLASH redefines */
80#if defined(EEPROM_ENABLE)
81# define SR STAT
82# define FLASH_SR_BSY FLASH_STAT_BUSY
83# define FLASH_SR_PGERR FLASH_STAT_PGERR
84# define FLASH_SR_EOP FLASH_STAT_ENDF
85# define FLASH_SR_WRPRTERR FLASH_STAT_WPERR
86# define FLASH_SR_WRPERR FLASH_SR_WRPRTERR
87# define FLASH_OBR_OPTERR FLASH_OBSTAT_OBERR
88# define AR ADDR
89# define CR CTL
90# define FLASH_CR_PER FLASH_CTL_PER
91# define FLASH_CR_STRT FLASH_CTL_START
92# define FLASH_CR_LOCK FLASH_CTL_LK
93# define FLASH_CR_PG FLASH_CTL_PG
94# define KEYR KEY
95#endif
96
97/* Serial USART redefines. */
98#if HAL_USE_SERIAL
99# if !defined(SERIAL_USART_CR1)
100# define SERIAL_USART_CR1 (USART_CTL0_PCEN | USART_CTL0_PM | USART_CTL0_WL) // parity enable, odd parity, 9 bit length
101# endif
102# if !defined(SERIAL_USART_CR2)
103# define SERIAL_USART_CR2 (USART_CTL1_STB_1) // 2 stop bits
104# endif
105# if !defined(SERIAL_USART_CR3)
106# define SERIAL_USART_CR3 0x0
107# endif
108# define USART_CR3_HDSEL USART_CTL2_HDEN
109# define CCR CHCV
110#endif
111
112/* SPI redefines. */
113#if HAL_USE_SPI
114# define SPI_CR1_LSBFIRST SPI_CTL0_LF
115# define SPI_CR1_CPHA SPI_CTL0_CKPH
116# define SPI_CR1_CPOL SPI_CTL0_CKPL
117# define SPI_CR1_BR_0 SPI_CTL0_PSC_0
118# define SPI_CR1_BR_1 SPI_CTL0_PSC_1
119# define SPI_CR1_BR_2 SPI_CTL0_PSC_2
120#endif
diff --git a/platforms/chibios/gpio.h b/platforms/chibios/gpio.h
new file mode 100644
index 000000000..4d057f1ca
--- /dev/null
+++ b/platforms/chibios/gpio.h
@@ -0,0 +1,50 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <hal.h>
19#include "pin_defs.h"
20
21typedef ioline_t pin_t;
22
23/* Operation of GPIO by pin. */
24
25#define setPinInput(pin) palSetLineMode(pin, PAL_MODE_INPUT)
26#define setPinInputHigh(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLUP)
27#define setPinInputLow(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN)
28#define setPinOutput(pin) palSetLineMode(pin, PAL_MODE_OUTPUT_PUSHPULL)
29
30#define writePinHigh(pin) palSetLine(pin)
31#define writePinLow(pin) palClearLine(pin)
32#define writePin(pin, level) ((level) ? (writePinHigh(pin)) : (writePinLow(pin)))
33
34#define readPin(pin) palReadLine(pin)
35
36#define togglePin(pin) palToggleLine(pin)
37
38/* Operation of GPIO by port. */
39
40typedef uint16_t port_data_t;
41
42#define readPort(pin) palReadPort(PAL_PORT(pin))
43
44#define setPortBitInput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT)
45#define setPortBitInputHigh(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLUP)
46#define setPortBitInputLow(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLDOWN)
47#define setPortBitOutput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_OUTPUT_PUSHPULL)
48
49#define writePortBitLow(pin, bit) palClearLine(PAL_LINE(PAL_PORT(pin), bit))
50#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 000000000..c03f8de0c
--- /dev/null
+++ b/platforms/chibios/pin_defs.h
@@ -0,0 +1,323 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18// Defines mapping for Proton C replacement
19#ifdef CONVERT_TO_PROTON_C
20// Left side (front)
21# define D3 PAL_LINE(GPIOA, 9)
22# define D2 PAL_LINE(GPIOA, 10)
23// GND
24// GND
25# define D1 PAL_LINE(GPIOB, 7)
26# define D0 PAL_LINE(GPIOB, 6)
27# define D4 PAL_LINE(GPIOB, 5)
28# define C6 PAL_LINE(GPIOB, 4)
29# define D7 PAL_LINE(GPIOB, 3)
30# define E6 PAL_LINE(GPIOB, 2)
31# define B4 PAL_LINE(GPIOB, 1)
32# define B5 PAL_LINE(GPIOB, 0)
33
34// Right side (front)
35// RAW
36// GND
37// RESET
38// VCC
39# define F4 PAL_LINE(GPIOA, 2)
40# define F5 PAL_LINE(GPIOA, 1)
41# define F6 PAL_LINE(GPIOA, 0)
42# define F7 PAL_LINE(GPIOB, 8)
43# define B1 PAL_LINE(GPIOB, 13)
44# define B3 PAL_LINE(GPIOB, 14)
45# define B2 PAL_LINE(GPIOB, 15)
46# define B6 PAL_LINE(GPIOB, 9)
47
48// LEDs (only D5/C13 uses an actual LED)
49# ifdef CONVERT_TO_PROTON_C_RXLED
50# define D5 PAL_LINE(GPIOC, 14)
51# define B0 PAL_LINE(GPIOC, 13)
52# else
53# define D5 PAL_LINE(GPIOC, 13)
54# define B0 PAL_LINE(GPIOC, 14)
55# endif
56#else
57# define A0 PAL_LINE(GPIOA, 0)
58# define A1 PAL_LINE(GPIOA, 1)
59# define A2 PAL_LINE(GPIOA, 2)
60# define A3 PAL_LINE(GPIOA, 3)
61# define A4 PAL_LINE(GPIOA, 4)
62# define A5 PAL_LINE(GPIOA, 5)
63# define A6 PAL_LINE(GPIOA, 6)
64# define A7 PAL_LINE(GPIOA, 7)
65# define A8 PAL_LINE(GPIOA, 8)
66# define A9 PAL_LINE(GPIOA, 9)
67# define A10 PAL_LINE(GPIOA, 10)
68# define A11 PAL_LINE(GPIOA, 11)
69# define A12 PAL_LINE(GPIOA, 12)
70# define A13 PAL_LINE(GPIOA, 13)
71# define A14 PAL_LINE(GPIOA, 14)
72# define A15 PAL_LINE(GPIOA, 15)
73# define A16 PAL_LINE(GPIOA, 16)
74# define A17 PAL_LINE(GPIOA, 17)
75# define A18 PAL_LINE(GPIOA, 18)
76# define A19 PAL_LINE(GPIOA, 19)
77# define A20 PAL_LINE(GPIOA, 20)
78# define A21 PAL_LINE(GPIOA, 21)
79# define A22 PAL_LINE(GPIOA, 22)
80# define A23 PAL_LINE(GPIOA, 23)
81# define A24 PAL_LINE(GPIOA, 24)
82# define A25 PAL_LINE(GPIOA, 25)
83# define A26 PAL_LINE(GPIOA, 26)
84# define A27 PAL_LINE(GPIOA, 27)
85# define A28 PAL_LINE(GPIOA, 28)
86# define A29 PAL_LINE(GPIOA, 29)
87# define A30 PAL_LINE(GPIOA, 30)
88# define A31 PAL_LINE(GPIOA, 31)
89# define A32 PAL_LINE(GPIOA, 32)
90# define B0 PAL_LINE(GPIOB, 0)
91# define B1 PAL_LINE(GPIOB, 1)
92# define B2 PAL_LINE(GPIOB, 2)
93# define B3 PAL_LINE(GPIOB, 3)
94# define B4 PAL_LINE(GPIOB, 4)
95# define B5 PAL_LINE(GPIOB, 5)
96# define B6 PAL_LINE(GPIOB, 6)
97# define B7 PAL_LINE(GPIOB, 7)
98# define B8 PAL_LINE(GPIOB, 8)
99# define B9 PAL_LINE(GPIOB, 9)
100# define B10 PAL_LINE(GPIOB, 10)
101# define B11 PAL_LINE(GPIOB, 11)
102# define B12 PAL_LINE(GPIOB, 12)
103# define B13 PAL_LINE(GPIOB, 13)
104# define B14 PAL_LINE(GPIOB, 14)
105# define B15 PAL_LINE(GPIOB, 15)
106# define B16 PAL_LINE(GPIOB, 16)
107# define B17 PAL_LINE(GPIOB, 17)
108# define B18 PAL_LINE(GPIOB, 18)
109# define B19 PAL_LINE(GPIOB, 19)
110# define B20 PAL_LINE(GPIOB, 20)
111# define B21 PAL_LINE(GPIOB, 21)
112# define B22 PAL_LINE(GPIOB, 22)
113# define B23 PAL_LINE(GPIOB, 23)
114# define B24 PAL_LINE(GPIOB, 24)
115# define B25 PAL_LINE(GPIOB, 25)
116# define B26 PAL_LINE(GPIOB, 26)
117# define B27 PAL_LINE(GPIOB, 27)
118# define B28 PAL_LINE(GPIOB, 28)
119# define B29 PAL_LINE(GPIOB, 29)
120# define B30 PAL_LINE(GPIOB, 30)
121# define B31 PAL_LINE(GPIOB, 31)
122# define B32 PAL_LINE(GPIOB, 32)
123# define C0 PAL_LINE(GPIOC, 0)
124# define C1 PAL_LINE(GPIOC, 1)
125# define C2 PAL_LINE(GPIOC, 2)
126# define C3 PAL_LINE(GPIOC, 3)
127# define C4 PAL_LINE(GPIOC, 4)
128# define C5 PAL_LINE(GPIOC, 5)
129# define C6 PAL_LINE(GPIOC, 6)
130# define C7 PAL_LINE(GPIOC, 7)
131# define C8 PAL_LINE(GPIOC, 8)
132# define C9 PAL_LINE(GPIOC, 9)
133# define C10 PAL_LINE(GPIOC, 10)
134# define C11 PAL_LINE(GPIOC, 11)
135# define C12 PAL_LINE(GPIOC, 12)
136# define C13 PAL_LINE(GPIOC, 13)
137# define C14 PAL_LINE(GPIOC, 14)
138# define C15 PAL_LINE(GPIOC, 15)
139# define C16 PAL_LINE(GPIOC, 16)
140# define C17 PAL_LINE(GPIOC, 17)
141# define C18 PAL_LINE(GPIOC, 18)
142# define C19 PAL_LINE(GPIOC, 19)
143# define C20 PAL_LINE(GPIOC, 20)
144# define C21 PAL_LINE(GPIOC, 21)
145# define C22 PAL_LINE(GPIOC, 22)
146# define C23 PAL_LINE(GPIOC, 23)
147# define C24 PAL_LINE(GPIOC, 24)
148# define C25 PAL_LINE(GPIOC, 25)
149# define C26 PAL_LINE(GPIOC, 26)
150# define C27 PAL_LINE(GPIOC, 27)
151# define C28 PAL_LINE(GPIOC, 28)
152# define C29 PAL_LINE(GPIOC, 29)
153# define C30 PAL_LINE(GPIOC, 30)
154# define C31 PAL_LINE(GPIOC, 31)
155# define C32 PAL_LINE(GPIOC, 32)
156# define D0 PAL_LINE(GPIOD, 0)
157# define D1 PAL_LINE(GPIOD, 1)
158# define D2 PAL_LINE(GPIOD, 2)
159# define D3 PAL_LINE(GPIOD, 3)
160# define D4 PAL_LINE(GPIOD, 4)
161# define D5 PAL_LINE(GPIOD, 5)
162# define D6 PAL_LINE(GPIOD, 6)
163# define D7 PAL_LINE(GPIOD, 7)
164# define D8 PAL_LINE(GPIOD, 8)
165# define D9 PAL_LINE(GPIOD, 9)
166# define D10 PAL_LINE(GPIOD, 10)
167# define D11 PAL_LINE(GPIOD, 11)
168# define D12 PAL_LINE(GPIOD, 12)
169# define D13 PAL_LINE(GPIOD, 13)
170# define D14 PAL_LINE(GPIOD, 14)
171# define D15 PAL_LINE(GPIOD, 15)
172# define D16 PAL_LINE(GPIOD, 16)
173# define D17 PAL_LINE(GPIOD, 17)
174# define D18 PAL_LINE(GPIOD, 18)
175# define D19 PAL_LINE(GPIOD, 19)
176# define D20 PAL_LINE(GPIOD, 20)
177# define D21 PAL_LINE(GPIOD, 21)
178# define D22 PAL_LINE(GPIOD, 22)
179# define D23 PAL_LINE(GPIOD, 23)
180# define D24 PAL_LINE(GPIOD, 24)
181# define D25 PAL_LINE(GPIOD, 25)
182# define D26 PAL_LINE(GPIOD, 26)
183# define D27 PAL_LINE(GPIOD, 27)
184# define D28 PAL_LINE(GPIOD, 28)
185# define D29 PAL_LINE(GPIOD, 29)
186# define D30 PAL_LINE(GPIOD, 30)
187# define D31 PAL_LINE(GPIOD, 31)
188# define D32 PAL_LINE(GPIOD, 32)
189# define E0 PAL_LINE(GPIOE, 0)
190# define E1 PAL_LINE(GPIOE, 1)
191# define E2 PAL_LINE(GPIOE, 2)
192# define E3 PAL_LINE(GPIOE, 3)
193# define E4 PAL_LINE(GPIOE, 4)
194# define E5 PAL_LINE(GPIOE, 5)
195# define E6 PAL_LINE(GPIOE, 6)
196# define E7 PAL_LINE(GPIOE, 7)
197# define E8 PAL_LINE(GPIOE, 8)
198# define E9 PAL_LINE(GPIOE, 9)
199# define E10 PAL_LINE(GPIOE, 10)
200# define E11 PAL_LINE(GPIOE, 11)
201# define E12 PAL_LINE(GPIOE, 12)
202# define E13 PAL_LINE(GPIOE, 13)
203# define E14 PAL_LINE(GPIOE, 14)
204# define E15 PAL_LINE(GPIOE, 15)
205# define E16 PAL_LINE(GPIOE, 16)
206# define E17 PAL_LINE(GPIOE, 17)
207# define E18 PAL_LINE(GPIOE, 18)
208# define E19 PAL_LINE(GPIOE, 19)
209# define E20 PAL_LINE(GPIOE, 20)
210# define E21 PAL_LINE(GPIOE, 21)
211# define E22 PAL_LINE(GPIOE, 22)
212# define E23 PAL_LINE(GPIOE, 23)
213# define E24 PAL_LINE(GPIOE, 24)
214# define E25 PAL_LINE(GPIOE, 25)
215# define E26 PAL_LINE(GPIOE, 26)
216# define E27 PAL_LINE(GPIOE, 27)
217# define E28 PAL_LINE(GPIOE, 28)
218# define E29 PAL_LINE(GPIOE, 29)
219# define E30 PAL_LINE(GPIOE, 30)
220# define E31 PAL_LINE(GPIOE, 31)
221# define E32 PAL_LINE(GPIOE, 32)
222# define F0 PAL_LINE(GPIOF, 0)
223# define F1 PAL_LINE(GPIOF, 1)
224# define F2 PAL_LINE(GPIOF, 2)
225# define F3 PAL_LINE(GPIOF, 3)
226# define F4 PAL_LINE(GPIOF, 4)
227# define F5 PAL_LINE(GPIOF, 5)
228# define F6 PAL_LINE(GPIOF, 6)
229# define F7 PAL_LINE(GPIOF, 7)
230# define F8 PAL_LINE(GPIOF, 8)
231# define F9 PAL_LINE(GPIOF, 9)
232# define F10 PAL_LINE(GPIOF, 10)
233# define F11 PAL_LINE(GPIOF, 11)
234# define F12 PAL_LINE(GPIOF, 12)
235# define F13 PAL_LINE(GPIOF, 13)
236# define F14 PAL_LINE(GPIOF, 14)
237# define F15 PAL_LINE(GPIOF, 15)
238# define G0 PAL_LINE(GPIOG, 0)
239# define G1 PAL_LINE(GPIOG, 1)
240# define G2 PAL_LINE(GPIOG, 2)
241# define G3 PAL_LINE(GPIOG, 3)
242# define G4 PAL_LINE(GPIOG, 4)
243# define G5 PAL_LINE(GPIOG, 5)
244# define G6 PAL_LINE(GPIOG, 6)
245# define G7 PAL_LINE(GPIOG, 7)
246# define G8 PAL_LINE(GPIOG, 8)
247# define G9 PAL_LINE(GPIOG, 9)
248# define G10 PAL_LINE(GPIOG, 10)
249# define G11 PAL_LINE(GPIOG, 11)
250# define G12 PAL_LINE(GPIOG, 12)
251# define G13 PAL_LINE(GPIOG, 13)
252# define G14 PAL_LINE(GPIOG, 14)
253# define G15 PAL_LINE(GPIOG, 15)
254# define H0 PAL_LINE(GPIOH, 0)
255# define H1 PAL_LINE(GPIOH, 1)
256# define H2 PAL_LINE(GPIOH, 2)
257# define H3 PAL_LINE(GPIOH, 3)
258# define H4 PAL_LINE(GPIOH, 4)
259# define H5 PAL_LINE(GPIOH, 5)
260# define H6 PAL_LINE(GPIOH, 6)
261# define H7 PAL_LINE(GPIOH, 7)
262# define H8 PAL_LINE(GPIOH, 8)
263# define H9 PAL_LINE(GPIOH, 9)
264# define H10 PAL_LINE(GPIOH, 10)
265# define H11 PAL_LINE(GPIOH, 11)
266# define H12 PAL_LINE(GPIOH, 12)
267# define H13 PAL_LINE(GPIOH, 13)
268# define H14 PAL_LINE(GPIOH, 14)
269# define H15 PAL_LINE(GPIOH, 15)
270# define I0 PAL_LINE(GPIOI, 0)
271# define I1 PAL_LINE(GPIOI, 1)
272# define I2 PAL_LINE(GPIOI, 2)
273# define I3 PAL_LINE(GPIOI, 3)
274# define I4 PAL_LINE(GPIOI, 4)
275# define I5 PAL_LINE(GPIOI, 5)
276# define I6 PAL_LINE(GPIOI, 6)
277# define I7 PAL_LINE(GPIOI, 7)
278# define I8 PAL_LINE(GPIOI, 8)
279# define I9 PAL_LINE(GPIOI, 9)
280# define I10 PAL_LINE(GPIOI, 10)
281# define I11 PAL_LINE(GPIOI, 11)
282# define I12 PAL_LINE(GPIOI, 12)
283# define I13 PAL_LINE(GPIOI, 13)
284# define I14 PAL_LINE(GPIOI, 14)
285# define I15 PAL_LINE(GPIOI, 15)
286# define J0 PAL_LINE(GPIOJ, 0)
287# define J1 PAL_LINE(GPIOJ, 1)
288# define J2 PAL_LINE(GPIOJ, 2)
289# define J3 PAL_LINE(GPIOJ, 3)
290# define J4 PAL_LINE(GPIOJ, 4)
291# define J5 PAL_LINE(GPIOJ, 5)
292# define J6 PAL_LINE(GPIOJ, 6)
293# define J7 PAL_LINE(GPIOJ, 7)
294# define J8 PAL_LINE(GPIOJ, 8)
295# define J9 PAL_LINE(GPIOJ, 9)
296# define J10 PAL_LINE(GPIOJ, 10)
297# define J11 PAL_LINE(GPIOJ, 11)
298# define J12 PAL_LINE(GPIOJ, 12)
299# define J13 PAL_LINE(GPIOJ, 13)
300# define J14 PAL_LINE(GPIOJ, 14)
301# define J15 PAL_LINE(GPIOJ, 15)
302// Keyboards can `#define KEYBOARD_REQUIRES_GPIOK` if they need to access GPIO-K pins. These conflict with a whole
303// bunch of layout definitions, so it's intentionally left out unless absolutely required -- in that case, the
304// keyboard designer should use a different symbol when defining their layout macros.
305# ifdef KEYBOARD_REQUIRES_GPIOK
306# define K0 PAL_LINE(GPIOK, 0)
307# define K1 PAL_LINE(GPIOK, 1)
308# define K2 PAL_LINE(GPIOK, 2)
309# define K3 PAL_LINE(GPIOK, 3)
310# define K4 PAL_LINE(GPIOK, 4)
311# define K5 PAL_LINE(GPIOK, 5)
312# define K6 PAL_LINE(GPIOK, 6)
313# define K7 PAL_LINE(GPIOK, 7)
314# define K8 PAL_LINE(GPIOK, 8)
315# define K9 PAL_LINE(GPIOK, 9)
316# define K10 PAL_LINE(GPIOK, 10)
317# define K11 PAL_LINE(GPIOK, 11)
318# define K12 PAL_LINE(GPIOK, 12)
319# define K13 PAL_LINE(GPIOK, 13)
320# define K14 PAL_LINE(GPIOK, 14)
321# define K15 PAL_LINE(GPIOK, 15)
322# endif
323#endif
diff --git a/platforms/chibios/platform.c b/platforms/chibios/platform.c
new file mode 100644
index 000000000..d4a229f27
--- /dev/null
+++ b/platforms/chibios/platform.c
@@ -0,0 +1,22 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "platform_deps.h"
18
19void platform_setup(void) {
20 halInit();
21 chSysInit();
22} \ No newline at end of file
diff --git a/platforms/chibios/platform.mk b/platforms/chibios/platform.mk
new file mode 100644
index 000000000..6b298732c
--- /dev/null
+++ b/platforms/chibios/platform.mk
@@ -0,0 +1,436 @@
1# Hey Emacs, this is a -*- makefile -*-
2##############################################################################
3# Architecture or project specific options
4#
5
6# Stack size to be allocated to the Cortex-M process stack. This stack is
7# the stack used by the main() thread.
8ifeq ($(USE_PROCESS_STACKSIZE),)
9 USE_PROCESS_STACKSIZE = 0x800
10endif
11
12# Stack size to the allocated to the Cortex-M main/exceptions stack. This
13# stack is used for processing interrupts and exceptions.
14ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
15 USE_EXCEPTIONS_STACKSIZE = 0x400
16endif
17
18#
19# Architecture or project specific options
20##############################################################################
21
22##############################################################################
23# Project, sources and paths
24#
25
26# Imported source files and paths
27OPT_OS = chibios
28CHIBIOS = $(TOP_DIR)/lib/chibios
29CHIBIOS_CONTRIB = $(TOP_DIR)/lib/chibios-contrib
30
31#
32# Startup, Port and Platform support selection
33##############################################################################
34
35ifeq ($(strip $(MCU)), risc-v)
36 # RISC-V Support
37 # As of 7.4.2021 there is only one supported RISC-V platform in Chibios-Contrib,
38 # therefore all required settings are hard-coded
39 STARTUP_MK = $(CHIBIOS_CONTRIB)/os/common/startup/RISCV-ECLIC/compilers/GCC/mk/startup_$(MCU_STARTUP).mk
40 PORT_V = $(CHIBIOS_CONTRIB)/os/common/ports/RISCV-ECLIC/compilers/GCC/mk/port.mk
41 RULESPATH = $(CHIBIOS_CONTRIB)/os/common/startup/RISCV-ECLIC/compilers/GCC
42 PLATFORM_MK = $(CHIBIOS_CONTRIB)/os/hal/ports/GD/GD32VF103/platform.mk
43else
44 # ARM Support
45 CHIBIOS_PORT ?=
46 ifeq ("$(CHIBIOS_PORT)","")
47 CHIBIOS_PORT = ARMv$(ARMV)-M
48 endif
49
50 # Startup files. Try a few different locations, for compability with old versions and
51 # for things hardware in the contrib repository
52 STARTUP_MK = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk
53 ifeq ("$(wildcard $(STARTUP_MK))","")
54 STARTUP_MK = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk
55 ifeq ("$(wildcard $(STARTUP_MK))","")
56 STARTUP_MK = $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk
57 endif
58 endif
59
60 # Port files. Try a few different locations, for compability with old versions and
61 # for things hardware in the contrib repository
62 PORT_V = $(CHIBIOS)/os/common/ports/$(CHIBIOS_PORT)/compilers/GCC/mk/port.mk
63 ifeq ("$(wildcard $(PORT_V))","")
64 PORT_V = $(CHIBIOS)/os/rt/ports/ARMCMx/compilers/GCC/mk/port_v$(ARMV)m.mk
65 ifeq ("$(wildcard $(PORT_V))","")
66 PORT_V = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v$(ARMV)m.mk
67 endif
68 endif
69
70 # Rules location. Try a few different locations, for compability with old versions and
71 # for things hardware in the contrib repository
72 RULESPATH = $(CHIBIOS)/os/common/ports/$(CHIBIOS_PORT)/compilers/GCC
73 ifeq ("$(wildcard $(RULESPATH)/rules.mk)","")
74 RULESPATH = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC
75 ifeq ("$(wildcard $(RULESPATH)/rules.mk)","")
76 RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC
77 endif
78 endif
79endif
80
81ifeq ("$(PLATFORM_NAME)","")
82 PLATFORM_NAME = platform
83endif
84
85ifeq ("$(wildcard $(PLATFORM_MK))","")
86 PLATFORM_MK = $(CHIBIOS)/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)/$(PLATFORM_NAME).mk
87 ifeq ("$(wildcard $(PLATFORM_MK))","")
88 PLATFORM_MK = $(CHIBIOS_CONTRIB)/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)/$(PLATFORM_NAME).mk
89 endif
90endif
91
92include $(STARTUP_MK)
93include $(PORT_V)
94include $(PLATFORM_MK)
95
96#
97# Board support selection.
98##############################################################################
99
100BOARD_MK :=
101
102ifneq ("$(wildcard $(KEYBOARD_PATH_5)/boards/$(BOARD)/board.mk)","")
103 BOARD_PATH = $(KEYBOARD_PATH_5)
104 BOARD_MK += $(KEYBOARD_PATH_5)/boards/$(BOARD)/board.mk
105else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/boards/$(BOARD)/board.mk)","")
106 BOARD_PATH = $(KEYBOARD_PATH_4)
107 BOARD_MK += $(KEYBOARD_PATH_4)/boards/$(BOARD)/board.mk
108else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/boards/$(BOARD)/board.mk)","")
109 BOARD_PATH = $(KEYBOARD_PATH_3)
110 BOARD_MK += $(KEYBOARD_PATH_3)/boards/$(BOARD)/board.mk
111else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/boards/$(BOARD)/board.mk)","")
112 BOARD_PATH = $(KEYBOARD_PATH_2)
113 BOARD_MK += $(KEYBOARD_PATH_2)/boards/$(BOARD)/board.mk
114else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/boards/$(BOARD)/board.mk)","")
115 BOARD_PATH = $(KEYBOARD_PATH_1)
116 BOARD_MK += $(KEYBOARD_PATH_1)/boards/$(BOARD)/board.mk
117else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/board/board.mk)","")
118 BOARD_PATH = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)
119 BOARD_MK += $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/board/board.mk
120 KEYBOARD_PATHS += $(BOARD_PATH)/configs
121 ifneq ("$(wildcard $(BOARD_PATH)/rules.mk)","")
122 include $(BOARD_PATH)/rules.mk
123 endif
124endif
125
126ifeq ("$(wildcard $(BOARD_MK))","")
127 BOARD_MK = $(CHIBIOS)/os/hal/boards/$(BOARD)/board.mk
128 ifeq ("$(wildcard $(BOARD_MK))","")
129 BOARD_MK = $(CHIBIOS_CONTRIB)/os/hal/boards/$(BOARD)/board.mk
130 endif
131endif
132
133include $(BOARD_MK)
134
135#
136# Bootloader selection.
137##############################################################################
138
139# Set bootloader address if supplied.
140ifdef STM32_BOOTLOADER_ADDRESS
141 OPT_DEFS += -DSTM32_BOOTLOADER_ADDRESS=$(STM32_BOOTLOADER_ADDRESS)
142endif
143
144# Work out if we need to set up the include for the bootloader definitions
145ifneq ("$(wildcard $(KEYBOARD_PATH_5)/bootloader_defs.h)","")
146 OPT_DEFS += -include $(KEYBOARD_PATH_5)/bootloader_defs.h
147else ifneq ("$(wildcard $(KEYBOARD_PATH_5)/boards/$(BOARD)/bootloader_defs.h)","")
148 OPT_DEFS += -include $(KEYBOARD_PATH_5)/boards/$(BOARD)/bootloader_defs.h
149else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/bootloader_defs.h)","")
150 OPT_DEFS += -include $(KEYBOARD_PATH_4)/bootloader_defs.h
151else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/boards/$(BOARD)/bootloader_defs.h)","")
152 OPT_DEFS += -include $(KEYBOARD_PATH_4)/boards/$(BOARD)/bootloader_defs.h
153else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/bootloader_defs.h)","")
154 OPT_DEFS += -include $(KEYBOARD_PATH_3)/bootloader_defs.h
155else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/boards/$(BOARD)/bootloader_defs.h)","")
156 OPT_DEFS += -include $(KEYBOARD_PATH_3)/boards/$(BOARD)/bootloader_defs.h
157else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/bootloader_defs.h)","")
158 OPT_DEFS += -include $(KEYBOARD_PATH_2)/bootloader_defs.h
159else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/boards/$(BOARD)/bootloader_defs.h)","")
160 OPT_DEFS += -include $(KEYBOARD_PATH_2)/boards/$(BOARD)/bootloader_defs.h
161else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/bootloader_defs.h)","")
162 OPT_DEFS += -include $(KEYBOARD_PATH_1)/bootloader_defs.h
163else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/boards/$(BOARD)/bootloader_defs.h)","")
164 OPT_DEFS += -include $(KEYBOARD_PATH_1)/boards/$(BOARD)/bootloader_defs.h
165else ifneq ("$(wildcard $(BOARD_PATH)/configs/bootloader_defs.h)","")
166 OPT_DEFS += -include $(BOARD_PATH)/configs/bootloader_defs.h
167endif
168
169#
170# ChibiOS config selection.
171##############################################################################
172
173# Work out the config file directories
174ifneq ("$(wildcard $(KEYBOARD_PATH_5)/chconf.h)","")
175 CHCONFDIR = $(KEYBOARD_PATH_5)
176else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/chconf.h)","")
177 CHCONFDIR = $(KEYBOARD_PATH_4)
178else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/chconf.h)","")
179 CHCONFDIR = $(KEYBOARD_PATH_3)
180else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/chconf.h)","")
181 CHCONFDIR = $(KEYBOARD_PATH_2)
182else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/chconf.h)","")
183 CHCONFDIR = $(KEYBOARD_PATH_1)
184else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/chconf.h)","")
185 CHCONFDIR = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs
186else ifneq ("$(wildcard $(TOP_DIR)/platforms/boards/chibios/common/configs/chconf.h)","")
187 CHCONFDIR = $(TOP_DIR)/platforms/chibios/boards/common/configs
188endif
189
190#
191# HAL config selection.
192##############################################################################
193
194ifneq ("$(wildcard $(KEYBOARD_PATH_5)/halconf.h)","")
195 HALCONFDIR = $(KEYBOARD_PATH_5)
196else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/halconf.h)","")
197 HALCONFDIR = $(KEYBOARD_PATH_4)
198else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/halconf.h)","")
199 HALCONFDIR = $(KEYBOARD_PATH_3)
200else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/halconf.h)","")
201 HALCONFDIR = $(KEYBOARD_PATH_2)
202else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/halconf.h)","")
203 HALCONFDIR = $(KEYBOARD_PATH_1)
204else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/halconf.h)","")
205 HALCONFDIR = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs
206else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/common/configs/halconf.h)","")
207 HALCONFDIR = $(TOP_DIR)/platforms/chibios/boards/common/configs
208endif
209
210#
211# Linker script selection.
212##############################################################################
213
214ifneq ("$(wildcard $(KEYBOARD_PATH_5)/ld/$(MCU_LDSCRIPT).ld)","")
215 LDSCRIPT = $(KEYBOARD_PATH_5)/ld/$(MCU_LDSCRIPT).ld
216else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/ld/$(MCU_LDSCRIPT).ld)","")
217 LDSCRIPT = $(KEYBOARD_PATH_4)/ld/$(MCU_LDSCRIPT).ld
218else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/ld/$(MCU_LDSCRIPT).ld)","")
219 LDSCRIPT = $(KEYBOARD_PATH_3)/ld/$(MCU_LDSCRIPT).ld
220else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld)","")
221 LDSCRIPT = $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld
222else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld)","")
223 LDSCRIPT = $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld
224else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT).ld)","")
225 LDFLAGS += -L$(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld
226 LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT).ld
227else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT).ld)","")
228 LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT).ld
229else ifneq ("$(wildcard $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld)","")
230 LDSCRIPT = $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld
231 USE_CHIBIOS_CONTRIB = yes
232else
233 LDSCRIPT = $(STARTUPLD)/$(MCU_LDSCRIPT).ld
234endif
235
236#
237# Include ChibiOS makefiles.
238##############################################################################
239
240# HAL-OSAL files (optional).
241include $(CHIBIOS)/os/hal/hal.mk
242-include $(CHIBIOS)/os/hal/osal/rt/osal.mk # ChibiOS <= 19.x
243-include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk # ChibiOS >= 20.x
244# RTOS files (optional).
245include $(CHIBIOS)/os/rt/rt.mk
246# Other files (optional).
247include $(CHIBIOS)/os/hal/lib/streams/streams.mk
248
249PLATFORM_SRC = \
250 $(STARTUPSRC) \
251 $(KERNSRC) \
252 $(PORTSRC) \
253 $(OSALSRC) \
254 $(HALSRC) \
255 $(PLATFORMSRC) \
256 $(BOARDSRC) \
257 $(STREAMSSRC) \
258 $(CHIBIOS)/os/various/syscalls.c \
259 $(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \
260 $(PLATFORM_COMMON_DIR)/wait.c
261
262# Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise.
263QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM)
264
265PLATFORM_SRC := $(patsubst $(TOP_DIR)/%,%,$(PLATFORM_SRC))
266
267EXTRAINCDIRS += $(CHIBIOS)/os/license $(CHIBIOS)/os/oslib/include \
268 $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs \
269 $(TOP_DIR)/platforms/chibios/boards/common/configs \
270 $(HALCONFDIR) $(CHCONFDIR) \
271 $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \
272 $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \
273 $(STREAMSINC) $(CHIBIOS)/os/various $(COMMON_VPATH)
274
275#
276# ChibiOS-Contrib
277##############################################################################
278
279# Work out if we're using ChibiOS-Contrib by checking if halconf_community.h exists
280ifneq ("$(wildcard $(KEYBOARD_PATH_5)/halconf_community.h)","")
281 USE_CHIBIOS_CONTRIB = yes
282else ifneq ("$(wildcard $(KEYBOARD_PATH_4)/halconf_community.h)","")
283 USE_CHIBIOS_CONTRIB = yes
284else ifneq ("$(wildcard $(KEYBOARD_PATH_3)/halconf_community.h)","")
285 USE_CHIBIOS_CONTRIB = yes
286else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/halconf_community.h)","")
287 USE_CHIBIOS_CONTRIB = yes
288else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/halconf_community.h)","")
289 USE_CHIBIOS_CONTRIB = yes
290else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/halconf_community.h)","")
291 USE_CHIBIOS_CONTRIB = yes
292endif
293
294ifeq ($(strip $(USE_CHIBIOS_CONTRIB)),yes)
295 include $(CHIBIOS_CONTRIB)/os/hal/hal.mk
296 PLATFORM_SRC += $(PLATFORMSRC_CONTRIB) $(HALSRC_CONTRIB)
297 EXTRAINCDIRS += $(PLATFORMINC_CONTRIB) $(HALINC_CONTRIB) $(CHIBIOS_CONTRIB)/os/various
298endif
299
300#
301# Project, sources and paths
302##############################################################################
303
304##############################################################################
305# Injected configs
306#
307ifneq ("$(wildcard $(BOARD_PATH)/configs/config.h)","")
308 CONFIG_H += $(BOARD_PATH)/configs/config.h
309endif
310ifneq ("$(wildcard $(BOARD_PATH)/configs/post_config.h)","")
311 POST_CONFIG_H += $(BOARD_PATH)/configs/post_config.h
312endif
313
314##############################################################################
315# Compiler and Linker configuration
316#
317
318# Use defined stack sizes of the main thread in linker scripts
319LDSYMBOLS =--defsym=__process_stack_size__=$(USE_PROCESS_STACKSIZE),--defsym=__main_stack_size__=$(USE_EXCEPTIONS_STACKSIZE)
320
321# Shared Compiler flags for all toolchains
322SHARED_CFLAGS = -fomit-frame-pointer \
323 -ffunction-sections \
324 -fdata-sections \
325 -fno-common \
326 -fshort-wchar
327
328# Shared Linker flags for all toolchains
329SHARED_LDFLAGS = -T $(LDSCRIPT) \
330 -Wl,$(LDSYMBOLS) \
331 -Wl,--gc-sections \
332 -nostartfiles
333
334ifeq ($(strip $(MCU)), risc-v)
335 # RISC-V toolchain specific configuration
336 # Find suitable GCC compiler
337 ifeq ($(strip $(TOOLCHAIN)),)
338 ifneq ($(shell which riscv32-unknown-elf-gcc 2>/dev/null),)
339 TOOLCHAIN = riscv32-unknown-elf-
340 else
341 ifneq ($(shell which riscv64-unknown-elf-gcc 2>/dev/null),)
342 TOOLCHAIN = riscv64-unknown-elf-
343 else
344 $(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!")
345 endif
346 endif
347 endif
348
349 # Default to compiling with picolibc for RISC-V targets if available,
350 # which is available by default on current (bullseye) debian based systems.
351 ifeq ($(shell $(TOOLCHAIN)gcc --specs=picolibc.specs -E - 2>/dev/null >/dev/null </dev/null ; echo $$?),0)
352 # Toolchain specific Compiler flags
353 # Note that we still link with our own linker script
354 # by providing it via the -T flag above.
355 TOOLCHAIN_CFLAGS = --specs=picolibc.specs
356
357 # Tell QMK that we are compiling with picolibc.
358 OPT_DEFS += -DUSE_PICOLIBC
359 endif
360
361 # MCU architecture flags
362 MCUFLAGS = -march=$(MCU_ARCH) \
363 -mabi=$(MCU_ABI) \
364 -mcmodel=$(MCU_CMODEL) \
365 -mstrict-align
366else
367 # ARM toolchain specific configuration
368 TOOLCHAIN ?= arm-none-eabi-
369
370 # Toolchain specific Linker flags
371 TOOLCHAIN_LDFLAGS = -Wl,--no-wchar-size-warning \
372 --specs=nano.specs
373
374 # MCU architecture flags
375 MCUFLAGS = -mcpu=$(MCU) \
376 -mthumb -DTHUMB_PRESENT \
377 -mno-thumb-interwork -DTHUMB_NO_INTERWORKING \
378 -mno-unaligned-access
379
380 # Some ARM cores like the M4 and M7 have floating point units which can be enabled
381 USE_FPU ?= no
382
383 ifneq ($(USE_FPU),no)
384 OPT_DEFS += -DCORTEX_USE_FPU=TRUE
385
386 # Default is single precision floats
387 USE_FPU_OPT ?= -mfloat-abi=hard \
388 -mfpu=fpv4-sp-d16 \
389 -fsingle-precision-constant
390
391 MCUFLAGS += $(USE_FPU_OPT)
392 else
393 OPT_DEFS += -DCORTEX_USE_FPU=FALSE
394 endif
395endif
396
397# Assembler flags
398ASFLAGS += $(SHARED_ASFLAGS) $(TOOLCHAIN_ASFLAGS)
399
400# C Compiler flags
401CFLAGS += $(SHARED_CFLAGS) $(TOOLCHAIN_CFLAGS)
402
403# C++ Compiler flags
404CXXFLAGS += $(CFLAGS) $(SHARED_CXXFLAGS) $(TOOLCHAIN_CXXFLAGS) -fno-rtti
405
406# Linker flags
407LDFLAGS += $(SHARED_LDFLAGS) $(TOOLCHAIN_LDFLAGS) $(MCUFLAGS)
408
409# Tell QMK that we are hosting it on ChibiOS.
410OPT_DEFS += -DPROTOCOL_CHIBIOS
411
412# Workaround to stop ChibiOS from complaining about new GCC -- it's been fixed for 7/8/9 already
413OPT_DEFS += -DPORT_IGNORE_GCC_VERSION_CHECK=1
414
415# Construct GCC toolchain
416CC = $(CC_PREFIX) $(TOOLCHAIN)gcc
417OBJCOPY = $(TOOLCHAIN)objcopy
418OBJDUMP = $(TOOLCHAIN)objdump
419SIZE = $(TOOLCHAIN)size
420AR = $(TOOLCHAIN)ar
421NM = $(TOOLCHAIN)nm
422HEX = $(OBJCOPY) -O $(FORMAT)
423EEP =
424BIN = $(OBJCOPY) -O binary
425
426##############################################################################
427# Make targets
428#
429
430DEBUG = gdb
431
432# List any extra directories to look for libraries here.
433EXTRALIBDIRS = $(RULESPATH)/ld
434
435bin: $(BUILD_DIR)/$(TARGET).bin sizeafter
436 $(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin;
diff --git a/platforms/chibios/platform_deps.h b/platforms/chibios/platform_deps.h
new file mode 100644
index 000000000..8243dcec5
--- /dev/null
+++ b/platforms/chibios/platform_deps.h
@@ -0,0 +1,19 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <hal.h>
19#include "chibios_config.h"
diff --git a/platforms/chibios/sleep_led.c b/platforms/chibios/sleep_led.c
new file mode 100644
index 000000000..477056a45
--- /dev/null
+++ b/platforms/chibios/sleep_led.c
@@ -0,0 +1,192 @@
1#include <ch.h>
2#include <hal.h>
3
4#include "led.h"
5#include "sleep_led.h"
6
7/* All right, we go the "software" way: timer, toggle LED in interrupt.
8 * Based on hasu's code for AVRs.
9 * Use LP timer on Kinetises, TIM14 on STM32F0.
10 */
11
12#ifndef SLEEP_LED_GPT_DRIVER
13# if defined(STM32F0XX)
14# define SLEEP_LED_GPT_DRIVER GPTD14
15# endif
16#endif
17
18#if defined(KL2x) || defined(K20x) || defined(SLEEP_LED_GPT_DRIVER) /* common parts for timers/interrupts */
19
20/* Breathing Sleep LED brighness(PWM On period) table
21 * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle
22 *
23 * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63
24 * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i }
25 */
26static 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};
27
28void sleep_led_timer_callback(void) {
29 /* Software PWM
30 * timer:1111 1111 1111 1111
31 * \_____/\/ \_______/____ count(0-255)
32 * \ \______________ duration of step(4)
33 * \__________________ index of step table(0-63)
34 */
35
36 // this works for cca 65536 irqs/sec
37 static union {
38 uint16_t row;
39 struct {
40 uint8_t count : 8;
41 uint8_t duration : 2;
42 uint8_t index : 6;
43 } pwm;
44 } timer = {.row = 0};
45
46 timer.row++;
47
48 // LED on
49 if (timer.pwm.count == 0) {
50 led_set(1 << USB_LED_CAPS_LOCK);
51 }
52 // LED off
53 if (timer.pwm.count == breathing_table[timer.pwm.index]) {
54 led_set(0);
55 }
56}
57
58#endif /* common parts for known platforms */
59
60#if defined(KL2x) || defined(K20x) /* platform selection: familiar Kinetis chips */
61
62/* Use Low Power Timer (LPTMR) */
63# define TIMER_INTERRUPT_VECTOR KINETIS_LPTMR0_IRQ_VECTOR
64# define RESET_COUNTER LPTMR0->CSR |= LPTMRx_CSR_TCF
65
66/* LPTMR clock options */
67# define LPTMR_CLOCK_MCGIRCLK 0 /* 4MHz clock */
68# define LPTMR_CLOCK_LPO 1 /* 1kHz clock */
69# define LPTMR_CLOCK_ERCLK32K 2 /* external 32kHz crystal */
70# define LPTMR_CLOCK_OSCERCLK 3 /* output from OSC */
71
72/* Work around inconsistencies in Freescale naming */
73# if !defined(SIM_SCGC5_LPTMR)
74# define SIM_SCGC5_LPTMR SIM_SCGC5_LPTIMER
75# endif
76
77/* interrupt handler */
78OSAL_IRQ_HANDLER(TIMER_INTERRUPT_VECTOR) {
79 OSAL_IRQ_PROLOGUE();
80
81 sleep_led_timer_callback();
82
83 /* Reset the counter */
84 RESET_COUNTER;
85
86 OSAL_IRQ_EPILOGUE();
87}
88
89/* Initialise the timer */
90void sleep_led_init(void) {
91 /* Make sure the clock to the LPTMR is enabled */
92 SIM->SCGC5 |= SIM_SCGC5_LPTMR;
93 /* Reset LPTMR settings */
94 LPTMR0->CSR = 0;
95 /* Set the compare value */
96 LPTMR0->CMR = 0; // trigger on counter value (i.e. every time)
97
98/* Set up clock source and prescaler */
99/* Software PWM
100 * ______ ______ __
101 * | ON |___OFF___| ON |___OFF___| ....
102 * |<-------------->|<-------------->|<- ....
103 * PWM period PWM period
104 *
105 * R interrupts/period[resolution]
106 * F periods/second[frequency]
107 * R * F interrupts/second
108 */
109
110/* === OPTION 1 === */
111# if 0
112 // 1kHz LPO
113 // No prescaler => 1024 irqs/sec
114 // Note: this is too slow for a smooth breathe
115 LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_LPO)|LPTMRx_PSR_PBYP;
116# endif /* OPTION 1 */
117
118/* === OPTION 2 === */
119# if 1
120 // nMHz IRC (n=4 on KL25Z, KL26Z and K20x; n=2 or 8 on KL27Z)
121 MCG->C2 |= MCG_C2_IRCS; // fast (4MHz) internal ref clock
122# if defined(KL27) // divide the 8MHz IRC by 2, to have the same MCGIRCLK speed as others
123 MCG->MC |= MCG_MC_LIRC_DIV2_DIV2;
124# endif /* KL27 */
125 MCG->C1 |= MCG_C1_IRCLKEN; // enable internal ref clock
126 // to work in stop mode, also MCG_C1_IREFSTEN
127 // Divide 4MHz by 2^N (N=6) => 62500 irqs/sec =>
128 // => approx F=61, R=256, duration = 4
129 LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_MCGIRCLK) | LPTMRx_PSR_PRESCALE(6);
130# endif /* OPTION 2 */
131
132/* === OPTION 3 === */
133# if 0
134 // OSC output (external crystal), usually 8MHz or 16MHz
135 OSC0->CR |= OSC_CR_ERCLKEN; // enable ext ref clock
136 // to work in stop mode, also OSC_CR_EREFSTEN
137 // Divide by 2^N
138 LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_OSCERCLK)|LPTMRx_PSR_PRESCALE(7);
139# endif /* OPTION 3 */
140 /* === END OPTIONS === */
141
142 /* Interrupt on TCF set (compare flag) */
143 nvicEnableVector(LPTMR0_IRQn, 2); // vector, priority
144 LPTMR0->CSR |= LPTMRx_CSR_TIE;
145}
146
147void sleep_led_enable(void) {
148 /* Enable the timer */
149 LPTMR0->CSR |= LPTMRx_CSR_TEN;
150}
151
152void sleep_led_disable(void) {
153 /* Disable the timer */
154 LPTMR0->CSR &= ~LPTMRx_CSR_TEN;
155}
156
157void sleep_led_toggle(void) {
158 /* Toggle the timer */
159 LPTMR0->CSR ^= LPTMRx_CSR_TEN;
160}
161
162#elif defined(SLEEP_LED_GPT_DRIVER)
163
164static void gptTimerCallback(GPTDriver *gptp) {
165 (void)gptp;
166 sleep_led_timer_callback();
167}
168
169static const GPTConfig gptcfg = {1000000, gptTimerCallback, 0, 0};
170
171/* Initialise the timer */
172void sleep_led_init(void) { gptStart(&SLEEP_LED_GPT_DRIVER, &gptcfg); }
173
174void sleep_led_enable(void) { gptStartContinuous(&SLEEP_LED_GPT_DRIVER, gptcfg.frequency / 0xFFFF); }
175
176void sleep_led_disable(void) { gptStopTimer(&SLEEP_LED_GPT_DRIVER); }
177
178void sleep_led_toggle(void) { (SLEEP_LED_GPT_DRIVER.state == GPT_READY) ? sleep_led_enable() : sleep_led_disable(); }
179
180#else /* platform selection: not on familiar chips */
181
182void sleep_led_init(void) {}
183
184void sleep_led_enable(void) { led_set(1 << USB_LED_CAPS_LOCK); }
185
186void sleep_led_disable(void) { led_set(0); }
187
188void sleep_led_toggle(void) {
189 // not implemented
190}
191
192#endif /* platform selection */
diff --git a/platforms/chibios/suspend.c b/platforms/chibios/suspend.c
new file mode 100644
index 000000000..9310a9992
--- /dev/null
+++ b/platforms/chibios/suspend.c
@@ -0,0 +1,92 @@
1/* TODO */
2
3#include <ch.h>
4#include <hal.h>
5
6#include "matrix.h"
7#include "action.h"
8#include "action_util.h"
9#include "mousekey.h"
10#include "programmable_button.h"
11#include "host.h"
12#include "suspend.h"
13#include "led.h"
14#include "wait.h"
15
16/** \brief suspend idle
17 *
18 * FIXME: needs doc
19 */
20void suspend_idle(uint8_t time) {
21 // TODO: this is not used anywhere - what units is 'time' in?
22 wait_ms(time);
23}
24
25/** \brief suspend power down
26 *
27 * FIXME: needs doc
28 */
29void suspend_power_down(void) {
30 suspend_power_down_quantum();
31 // on AVR, this enables the watchdog for 15ms (max), and goes to
32 // SLEEP_MODE_PWR_DOWN
33
34 wait_ms(17);
35}
36
37/** \brief suspend wakeup condition
38 *
39 * FIXME: needs doc
40 */
41__attribute__((weak)) void matrix_power_up(void) {}
42__attribute__((weak)) void matrix_power_down(void) {}
43bool suspend_wakeup_condition(void) {
44 matrix_power_up();
45 matrix_scan();
46 matrix_power_down();
47 for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
48 if (matrix_get_row(r)) return true;
49 }
50 return false;
51}
52
53/** \brief run user level code immediately after wakeup
54 *
55 * FIXME: needs doc
56 */
57__attribute__((weak)) void suspend_wakeup_init_user(void) {}
58
59/** \brief run keyboard level code immediately after wakeup
60 *
61 * FIXME: needs doc
62 */
63__attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); }
64
65/** \brief suspend wakeup condition
66 *
67 * run immediately after wakeup
68 * FIXME: needs doc
69 */
70void suspend_wakeup_init(void) {
71 // clear keyboard state
72 // need to do it manually, because we're running from ISR
73 // and clear_keyboard() calls print
74 // so only clear the variables in memory
75 // the reports will be sent from main.c afterwards
76 // or if the PC asks for GET_REPORT
77 clear_mods();
78 clear_weak_mods();
79 clear_keys();
80#ifdef MOUSEKEY_ENABLE
81 mousekey_clear();
82#endif /* MOUSEKEY_ENABLE */
83#ifdef PROGRAMMABLE_BUTTON_ENABLE
84 programmable_button_clear();
85#endif /* PROGRAMMABLE_BUTTON_ENABLE */
86#ifdef EXTRAKEY_ENABLE
87 host_system_send(0);
88 host_consumer_send(0);
89#endif /* EXTRAKEY_ENABLE */
90
91 suspend_wakeup_init_quantum();
92}
diff --git a/platforms/chibios/syscall-fallbacks.c b/platforms/chibios/syscall-fallbacks.c
new file mode 100644
index 000000000..4569879c7
--- /dev/null
+++ b/platforms/chibios/syscall-fallbacks.c
@@ -0,0 +1,110 @@
1/* Copyright 2021 Nick Brassel, QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <errno.h>
18#include <sys/stat.h>
19#include <sys/types.h>
20
21/* To compile the ChibiOS syscall stubs with picolibc
22 * the _reent struct has to be defined. */
23#if defined(USE_PICOLIBC)
24struct _reent;
25#endif
26
27#pragma GCC diagnostic ignored "-Wmissing-prototypes"
28
29__attribute__((weak, used)) int _open_r(struct _reent *r, const char *path, int flag, int m) {
30 __errno_r(r) = ENOENT;
31 return -1;
32}
33
34__attribute__((weak, used)) int _lseek_r(struct _reent *r, int file, int ptr, int dir) {
35 __errno_r(r) = EBADF;
36 return -1;
37}
38
39__attribute__((weak, used)) int _read_r(struct _reent *r, int file, char *ptr, int len) {
40 __errno_r(r) = EBADF;
41 return -1;
42}
43
44__attribute__((weak, used)) int _write_r(struct _reent *r, int file, char *ptr, int len) {
45 __errno_r(r) = EBADF;
46 return -1;
47}
48
49__attribute__((weak, used)) int _close_r(struct _reent *r, int file) {
50 __errno_r(r) = EBADF;
51 return -1;
52}
53
54__attribute__((weak, used)) int _link_r(struct _reent *r, const char *oldpath, const char *newpath) {
55 __errno_r(r) = EPERM;
56 return -1;
57}
58
59__attribute__((weak, used)) int _unlink_r(struct _reent *r, const char *path) {
60 __errno_r(r) = EPERM;
61 return -1;
62}
63
64__attribute__((weak, used)) clock_t _times_r(struct _reent *r, void *t) {
65 __errno_r(r) = EFAULT;
66 return -1;
67}
68
69__attribute__((weak, used)) int _fstat_r(struct _reent *r, int file, struct stat *st) {
70 __errno_r(r) = EBADF;
71 return -1;
72}
73
74__attribute__((weak, used)) int _isatty_r(struct _reent *r, int fd) {
75 __errno_r(r) = EBADF;
76 return 0;
77}
78
79__attribute__((weak, used)) caddr_t _sbrk_r(struct _reent *r, int incr) {
80 __errno_r(r) = ENOMEM;
81 return (caddr_t)-1;
82}
83
84__attribute__((weak, used)) int _kill(int pid, int sig) {
85 errno = EPERM;
86 return -1;
87}
88
89__attribute__((weak, used)) pid_t _getpid(void) { return 1; }
90
91__attribute__((weak, used)) void _fini(void) { return; }
92
93__attribute__((weak, used, noreturn)) void _exit(int i) {
94 while (1)
95 ;
96}
97
98__attribute__((weak, used)) int _gettimeofday_r(struct _reent *r, struct timeval *t, void *tzp) {
99 __errno_r(r) = EPERM;
100 return -1;
101}
102
103__attribute__((weak, used)) void *__dso_handle;
104
105__attribute__((weak, used)) void __cxa_pure_virtual(void) {
106 while (1)
107 ;
108}
109
110#pragma GCC diagnostic pop
diff --git a/platforms/chibios/timer.c b/platforms/chibios/timer.c
new file mode 100644
index 000000000..9f664e1f7
--- /dev/null
+++ b/platforms/chibios/timer.c
@@ -0,0 +1,47 @@
1#include <ch.h>
2
3#include "timer.h"
4
5static uint32_t reset_point = 0;
6#if CH_CFG_ST_RESOLUTION < 32
7static uint32_t last_systime = 0;
8static uint32_t overflow = 0;
9#endif
10
11void timer_init(void) { timer_clear(); }
12
13void timer_clear(void) {
14 reset_point = (uint32_t)chVTGetSystemTime();
15#if CH_CFG_ST_RESOLUTION < 32
16 last_systime = reset_point;
17 overflow = 0;
18#endif
19}
20
21uint16_t timer_read(void) { return (uint16_t)timer_read32(); }
22
23uint32_t timer_read32(void) {
24 uint32_t systime = (uint32_t)chVTGetSystemTime();
25
26#if CH_CFG_ST_RESOLUTION < 32
27 // If/when we need to support 64-bit chips, this may need to be modified to match the native bit-ness of the MCU.
28 // At this point, the only SysTick resolution allowed other than 32 is 16 bit.
29 // In the 16-bit case, at:
30 // - CH_CFG_ST_FREQUENCY = 100000, overflow will occur every ~0.65 seconds
31 // - CH_CFG_ST_FREQUENCY = 10000, overflow will occur every ~6.5 seconds
32 // - CH_CFG_ST_FREQUENCY = 1000, overflow will occur every ~65 seconds
33 // With this implementation, as long as we ensure a timer read happens at least once during the overflow period, timing should be accurate.
34 if (systime < last_systime) {
35 overflow += ((uint32_t)1) << CH_CFG_ST_RESOLUTION;
36 }
37
38 last_systime = systime;
39 return (uint32_t)TIME_I2MS(systime - reset_point + overflow);
40#else
41 return (uint32_t)TIME_I2MS(systime - reset_point);
42#endif
43}
44
45uint16_t timer_elapsed(uint16_t last) { return TIMER_DIFF_16(timer_read(), last); }
46
47uint32_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 000000000..56fd6ffce
--- /dev/null
+++ b/platforms/chibios/wait.c
@@ -0,0 +1,41 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <ch.h>
18#include <hal.h>
19
20#include "_wait.h"
21
22#ifdef WAIT_US_TIMER
23void wait_us(uint16_t duration) {
24 static const GPTConfig gpt_cfg = {1000000, NULL, 0, 0}; /* 1MHz timer, no callback */
25
26 if (duration == 0) {
27 duration = 1;
28 }
29
30 /*
31 * Only use this timer on the main thread;
32 * other threads need to use their own timer.
33 */
34 if (chThdGetSelfX() == &ch.mainthread && duration < (1ULL << (sizeof(gptcnt_t) * 8))) {
35 gptStart(&WAIT_US_TIMER, &gpt_cfg);
36 gptPolledDelay(&WAIT_US_TIMER, duration);
37 } else {
38 chThdSleepMicroseconds(duration);
39 }
40}
41#endif