summary refs log tree commit diff
path: root/tmk_core/common
diff options
context:
space:
mode:
authorNick Brassel <nick@tzarc.org>2021-08-29 08:20:25 +1000
committerNick Brassel <nick@tzarc.org>2021-08-29 08:20:25 +1000
commitf061ca497464fe85284906fb163a33eaee7a91ef (patch)
tree33ef1bfb529aed382e8526c607c4e18717f92571 /tmk_core/common
parentff65185dec6f97be1eb49f17cea526a0d0bbf3d6 (diff)
parent4bad375d7c09d949a9dcdd4feba147c9c7a67ec6 (diff)
Breaking changes develop merge to master, 2021Q3 edition. (#14196)
Diffstat (limited to 'tmk_core/common')
-rw-r--r--tmk_core/common/action.c1093
-rw-r--r--tmk_core/common/action.h127
-rw-r--r--tmk_core/common/action_code.h308
-rw-r--r--tmk_core/common/action_layer.c279
-rw-r--r--tmk_core/common/action_layer.h122
-rw-r--r--tmk_core/common/action_macro.c93
-rw-r--r--tmk_core/common/action_macro.h123
-rw-r--r--tmk_core/common/action_tapping.c426
-rw-r--r--tmk_core/common/action_tapping.h41
-rw-r--r--tmk_core/common/action_util.c427
-rw-r--r--tmk_core/common/action_util.h105
-rw-r--r--tmk_core/common/arm_atsam/_timer.h19
-rw-r--r--tmk_core/common/arm_atsam/platform.c21
-rw-r--r--tmk_core/common/avr/_timer.h19
-rw-r--r--tmk_core/common/avr/_wait.h26
-rw-r--r--tmk_core/common/avr/gpio.h15
-rw-r--r--tmk_core/common/avr/platform.c21
-rw-r--r--tmk_core/common/chibios/_timer.h19
-rw-r--r--tmk_core/common/chibios/_wait.c89
-rw-r--r--tmk_core/common/chibios/_wait.h31
-rw-r--r--tmk_core/common/chibios/bootloader.c5
-rw-r--r--tmk_core/common/chibios/eeprom_stm32.c802
-rw-r--r--tmk_core/common/chibios/eeprom_stm32.h61
-rw-r--r--tmk_core/common/chibios/flash_stm32.h7
-rw-r--r--tmk_core/common/chibios/gpio.h16
-rw-r--r--tmk_core/common/chibios/platform.c22
-rw-r--r--tmk_core/common/chibios/wait.c90
-rw-r--r--tmk_core/common/debug.c25
-rw-r--r--tmk_core/common/debug.h169
-rw-r--r--tmk_core/common/eeconfig.c211
-rw-r--r--tmk_core/common/eeconfig.h113
-rw-r--r--tmk_core/common/host.c33
-rw-r--r--tmk_core/common/host_driver.h2
-rw-r--r--tmk_core/common/keyboard.c534
-rw-r--r--tmk_core/common/keyboard.h90
-rw-r--r--tmk_core/common/keycode.h560
-rw-r--r--tmk_core/common/lib_printf.mk9
-rw-r--r--tmk_core/common/nodebug.h26
-rw-r--r--tmk_core/common/print.h135
-rw-r--r--tmk_core/common/printf.c27
-rw-r--r--tmk_core/common/progmem.h2
-rw-r--r--tmk_core/common/report.h14
-rw-r--r--tmk_core/common/sendchar.h33
-rw-r--r--tmk_core/common/sendchar_null.c19
-rw-r--r--tmk_core/common/sendchar_uart.c23
-rw-r--r--tmk_core/common/sync_timer.c2
-rw-r--r--tmk_core/common/sync_timer.h2
-rw-r--r--tmk_core/common/test/eeprom_stm32_tests.cpp438
-rw-r--r--tmk_core/common/test/flash_stm32_mock.c50
-rw-r--r--tmk_core/common/test/platform.c21
-rw-r--r--tmk_core/common/test/rules.mk23
-rw-r--r--tmk_core/common/test/testlist.mk1
-rw-r--r--tmk_core/common/timer.h20
-rw-r--r--tmk_core/common/usb_util.c2
-rw-r--r--tmk_core/common/usb_util.h2
55 files changed, 1604 insertions, 5389 deletions
diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c
deleted file mode 100644
index bd41d28b66..0000000000
--- a/tmk_core/common/action.c
+++ /dev/null
@@ -1,1093 +0,0 @@
-/*
-Copyright 2012,2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-#include "host.h"
-#include "keycode.h"
-#include "keyboard.h"
-#include "mousekey.h"
-#include "command.h"
-#include "led.h"
-#include "action_layer.h"
-#include "action_tapping.h"
-#include "action_macro.h"
-#include "action_util.h"
-#include "action.h"
-#include "wait.h"
-
-#ifdef BACKLIGHT_ENABLE
-#    include "backlight.h"
-#endif
-
-#ifdef DEBUG_ACTION
-#    include "debug.h"
-#else
-#    include "nodebug.h"
-#endif
-
-#ifdef POINTING_DEVICE_ENABLE
-#    include "pointing_device.h"
-#endif
-
-int tp_buttons;
-
-#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
-int retro_tapping_counter = 0;
-#endif
-
-#ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
-__attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; }
-#endif
-
-#ifdef RETRO_TAPPING_PER_KEY
-__attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; }
-#endif
-
-#ifndef TAP_CODE_DELAY
-#    define TAP_CODE_DELAY 0
-#endif
-#ifndef TAP_HOLD_CAPS_DELAY
-#    define TAP_HOLD_CAPS_DELAY 80
-#endif
-/** \brief Called to execute an action.
- *
- * FIXME: Needs documentation.
- */
-void action_exec(keyevent_t event) {
-    if (!IS_NOEVENT(event)) {
-        dprint("\n---- action_exec: start -----\n");
-        dprint("EVENT: ");
-        debug_event(event);
-        dprintln();
-#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
-        retro_tapping_counter++;
-#endif
-    }
-
-    if (event.pressed) {
-        // clear the potential weak mods left by previously pressed keys
-        clear_weak_mods();
-    }
-
-#ifdef SWAP_HANDS_ENABLE
-    if (!IS_NOEVENT(event)) {
-        process_hand_swap(&event);
-    }
-#endif
-
-    keyrecord_t record = {.event = event};
-
-#ifndef NO_ACTION_ONESHOT
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-    if (has_oneshot_layer_timed_out()) {
-        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
-    }
-    if (has_oneshot_mods_timed_out()) {
-        clear_oneshot_mods();
-    }
-#        ifdef SWAP_HANDS_ENABLE
-    if (has_oneshot_swaphands_timed_out()) {
-        clear_oneshot_swaphands();
-    }
-#        endif
-#    endif
-#endif
-
-#ifndef NO_ACTION_TAPPING
-    action_tapping_process(record);
-#else
-    process_record(&record);
-    if (!IS_NOEVENT(record.event)) {
-        dprint("processed: ");
-        debug_record(record);
-        dprintln();
-    }
-#endif
-}
-
-#ifdef SWAP_HANDS_ENABLE
-bool swap_hands = false;
-bool swap_held  = false;
-
-/** \brief Process Hand Swap
- *
- * FIXME: Needs documentation.
- */
-void process_hand_swap(keyevent_t *event) {
-    static swap_state_row_t swap_state[MATRIX_ROWS];
-
-    keypos_t         pos     = event->key;
-    swap_state_row_t col_bit = (swap_state_row_t)1 << pos.col;
-    bool             do_swap = event->pressed ? swap_hands : swap_state[pos.row] & (col_bit);
-
-    if (do_swap) {
-        event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row);
-        event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col);
-        swap_state[pos.row] |= col_bit;
-    } else {
-        swap_state[pos.row] &= ~(col_bit);
-    }
-}
-#endif
-
-#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
-bool disable_action_cache = false;
-
-void process_record_nocache(keyrecord_t *record) {
-    disable_action_cache = true;
-    process_record(record);
-    disable_action_cache = false;
-}
-#else
-void process_record_nocache(keyrecord_t *record) { process_record(record); }
-#endif
-
-__attribute__((weak)) bool process_record_quantum(keyrecord_t *record) { return true; }
-
-__attribute__((weak)) void post_process_record_quantum(keyrecord_t *record) {}
-
-#ifndef NO_ACTION_TAPPING
-/** \brief Allows for handling tap-hold actions immediately instead of waiting for TAPPING_TERM or another keypress.
- *
- * FIXME: Needs documentation.
- */
-void process_record_tap_hint(keyrecord_t *record) {
-    action_t action = layer_switch_get_action(record->event.key);
-
-    switch (action.kind.id) {
-#    ifdef SWAP_HANDS_ENABLE
-        case ACT_SWAP_HANDS:
-            switch (action.swap.code) {
-                case OP_SH_ONESHOT:
-                    break;
-                case OP_SH_TAP_TOGGLE:
-                default:
-                    swap_hands = !swap_hands;
-                    swap_held  = true;
-            }
-            break;
-#    endif
-    }
-}
-#endif
-
-/** \brief Take a key event (key press or key release) and processes it.
- *
- * FIXME: Needs documentation.
- */
-void process_record(keyrecord_t *record) {
-    if (IS_NOEVENT(record->event)) {
-        return;
-    }
-
-    if (!process_record_quantum(record)) {
-#ifndef NO_ACTION_ONESHOT
-        if (is_oneshot_layer_active() && record->event.pressed) {
-            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
-        }
-#endif
-        return;
-    }
-
-    process_record_handler(record);
-    post_process_record_quantum(record);
-}
-
-void process_record_handler(keyrecord_t *record) {
-    action_t action = store_or_get_action(record->event.pressed, record->event.key);
-    dprint("ACTION: ");
-    debug_action(action);
-#ifndef NO_ACTION_LAYER
-    dprint(" layer_state: ");
-    layer_debug();
-    dprint(" default_layer_state: ");
-    default_layer_debug();
-#endif
-    dprintln();
-
-    process_action(record, action);
-}
-
-#if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE)
-void register_button(bool pressed, enum mouse_buttons button) {
-#    ifdef PS2_MOUSE_ENABLE
-    tp_buttons = pressed ? tp_buttons | button : tp_buttons & ~button;
-#    endif
-#    ifdef POINTING_DEVICE_ENABLE
-    report_mouse_t currentReport = pointing_device_get_report();
-    currentReport.buttons        = pressed ? currentReport.buttons | button : currentReport.buttons & ~button;
-    pointing_device_set_report(currentReport);
-#    endif
-}
-#endif
-
-/** \brief Take an action and processes it.
- *
- * FIXME: Needs documentation.
- */
-void process_action(keyrecord_t *record, action_t action) {
-    keyevent_t event = record->event;
-#ifndef NO_ACTION_TAPPING
-    uint8_t tap_count = record->tap.count;
-#endif
-
-#ifndef NO_ACTION_ONESHOT
-    bool do_release_oneshot = false;
-    // notice we only clear the one shot layer if the pressed key is not a modifier.
-    if (is_oneshot_layer_active() && event.pressed && (action.kind.id == ACT_USAGE || !IS_MOD(action.key.code))
-#    ifdef SWAP_HANDS_ENABLE
-        && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)
-#    endif
-    ) {
-        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
-        do_release_oneshot = !is_oneshot_layer_active();
-    }
-#endif
-
-    switch (action.kind.id) {
-        /* Key and Mods */
-        case ACT_LMODS:
-        case ACT_RMODS: {
-            uint8_t mods = (action.kind.id == ACT_LMODS) ? action.key.mods : action.key.mods << 4;
-            if (event.pressed) {
-                if (mods) {
-                    if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
-                        // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.
-                        // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT).
-                        // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO).
-                        add_mods(mods);
-                    } else {
-                        add_weak_mods(mods);
-                    }
-                    send_keyboard_report();
-                }
-                register_code(action.key.code);
-            } else {
-                unregister_code(action.key.code);
-                if (mods) {
-                    if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
-                        del_mods(mods);
-                    } else {
-                        del_weak_mods(mods);
-                    }
-                    send_keyboard_report();
-                }
-            }
-        } break;
-#ifndef NO_ACTION_TAPPING
-        case ACT_LMODS_TAP:
-        case ACT_RMODS_TAP: {
-            uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ? action.key.mods : action.key.mods << 4;
-            switch (action.layer_tap.code) {
-#    ifndef NO_ACTION_ONESHOT
-                case MODS_ONESHOT:
-                    // Oneshot modifier
-                    if (event.pressed) {
-                        if (tap_count == 0) {
-                            dprint("MODS_TAP: Oneshot: 0\n");
-                            register_mods(mods | get_oneshot_mods());
-                        } else if (tap_count == 1) {
-                            dprint("MODS_TAP: Oneshot: start\n");
-                            set_oneshot_mods(mods | get_oneshot_mods());
-#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
-                        } else if (tap_count == ONESHOT_TAP_TOGGLE) {
-                            dprint("MODS_TAP: Toggling oneshot");
-                            clear_oneshot_mods();
-                            set_oneshot_locked_mods(mods);
-                            register_mods(mods);
-#        endif
-                        } else {
-                            register_mods(mods | get_oneshot_mods());
-                        }
-                    } else {
-                        if (tap_count == 0) {
-                            clear_oneshot_mods();
-                            unregister_mods(mods);
-                        } else if (tap_count == 1) {
-                            // Retain Oneshot mods
-#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
-                            if (mods & get_mods()) {
-                                clear_oneshot_locked_mods();
-                                clear_oneshot_mods();
-                                unregister_mods(mods);
-                            }
-                        } else if (tap_count == ONESHOT_TAP_TOGGLE) {
-                            // Toggle Oneshot Layer
-#        endif
-                        } else {
-                            clear_oneshot_mods();
-                            unregister_mods(mods);
-                        }
-                    }
-                    break;
-#    endif
-                case MODS_TAP_TOGGLE:
-                    if (event.pressed) {
-                        if (tap_count <= TAPPING_TOGGLE) {
-                            register_mods(mods);
-                        }
-                    } else {
-                        if (tap_count < TAPPING_TOGGLE) {
-                            unregister_mods(mods);
-                        }
-                    }
-                    break;
-                default:
-                    if (event.pressed) {
-                        if (tap_count > 0) {
-#    if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
-                            if (
-#        ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
-                                !get_ignore_mod_tap_interrupt(get_event_keycode(record->event, false), record) &&
-#        endif
-                                record->tap.interrupted) {
-                                dprint("mods_tap: tap: cancel: add_mods\n");
-                                // ad hoc: set 0 to cancel tap
-                                record->tap.count = 0;
-                                register_mods(mods);
-                            } else
-#    endif
-                            {
-                                dprint("MODS_TAP: Tap: register_code\n");
-                                register_code(action.key.code);
-                            }
-                        } else {
-                            dprint("MODS_TAP: No tap: add_mods\n");
-                            register_mods(mods);
-                        }
-                    } else {
-                        if (tap_count > 0) {
-                            dprint("MODS_TAP: Tap: unregister_code\n");
-                            if (action.layer_tap.code == KC_CAPS) {
-                                wait_ms(TAP_HOLD_CAPS_DELAY);
-                            } else {
-                                wait_ms(TAP_CODE_DELAY);
-                            }
-                            unregister_code(action.key.code);
-                        } else {
-                            dprint("MODS_TAP: No tap: add_mods\n");
-                            unregister_mods(mods);
-                        }
-                    }
-                    break;
-            }
-        } break;
-#endif
-#ifdef EXTRAKEY_ENABLE
-        /* other HID usage */
-        case ACT_USAGE:
-            switch (action.usage.page) {
-                case PAGE_SYSTEM:
-                    if (event.pressed) {
-                        host_system_send(action.usage.code);
-                    } else {
-                        host_system_send(0);
-                    }
-                    break;
-                case PAGE_CONSUMER:
-                    if (event.pressed) {
-                        host_consumer_send(action.usage.code);
-                    } else {
-                        host_consumer_send(0);
-                    }
-                    break;
-            }
-            break;
-#endif
-#ifdef MOUSEKEY_ENABLE
-        /* Mouse key */
-        case ACT_MOUSEKEY:
-            if (event.pressed) {
-                mousekey_on(action.key.code);
-            } else {
-                mousekey_off(action.key.code);
-            }
-            switch (action.key.code) {
-#    if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE)
-#        ifdef POINTING_DEVICE_ENABLE
-                case KC_MS_BTN1 ... KC_MS_BTN8:
-#        else
-                case KC_MS_BTN1 ... KC_MS_BTN3:
-#        endif
-                    register_button(event.pressed, MOUSE_BTN_MASK(action.key.code - KC_MS_BTN1));
-                    break;
-#    endif
-                default:
-                    mousekey_send();
-                    break;
-            }
-            break;
-#endif
-#ifndef NO_ACTION_LAYER
-        case ACT_LAYER:
-            if (action.layer_bitop.on == 0) {
-                /* Default Layer Bitwise Operation */
-                if (!event.pressed) {
-                    uint8_t       shift = action.layer_bitop.part * 4;
-                    layer_state_t bits  = ((layer_state_t)action.layer_bitop.bits) << shift;
-                    layer_state_t mask  = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0;
-                    switch (action.layer_bitop.op) {
-                        case OP_BIT_AND:
-                            default_layer_and(bits | mask);
-                            break;
-                        case OP_BIT_OR:
-                            default_layer_or(bits | mask);
-                            break;
-                        case OP_BIT_XOR:
-                            default_layer_xor(bits | mask);
-                            break;
-                        case OP_BIT_SET:
-                            default_layer_set(bits | mask);
-                            break;
-                    }
-                }
-            } else {
-                /* Layer Bitwise Operation */
-                if (event.pressed ? (action.layer_bitop.on & ON_PRESS) : (action.layer_bitop.on & ON_RELEASE)) {
-                    uint8_t       shift = action.layer_bitop.part * 4;
-                    layer_state_t bits  = ((layer_state_t)action.layer_bitop.bits) << shift;
-                    layer_state_t mask  = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0;
-                    switch (action.layer_bitop.op) {
-                        case OP_BIT_AND:
-                            layer_and(bits | mask);
-                            break;
-                        case OP_BIT_OR:
-                            layer_or(bits | mask);
-                            break;
-                        case OP_BIT_XOR:
-                            layer_xor(bits | mask);
-                            break;
-                        case OP_BIT_SET:
-                            layer_state_set(bits | mask);
-                            break;
-                    }
-                }
-            }
-            break;
-        case ACT_LAYER_MODS:
-            if (event.pressed) {
-                layer_on(action.layer_mods.layer);
-                register_mods(action.layer_mods.mods);
-            } else {
-                unregister_mods(action.layer_mods.mods);
-                layer_off(action.layer_mods.layer);
-            }
-            break;
-#    ifndef NO_ACTION_TAPPING
-        case ACT_LAYER_TAP:
-        case ACT_LAYER_TAP_EXT:
-            switch (action.layer_tap.code) {
-                case OP_TAP_TOGGLE:
-                    /* tap toggle */
-                    if (event.pressed) {
-                        if (tap_count < TAPPING_TOGGLE) {
-                            layer_invert(action.layer_tap.val);
-                        }
-                    } else {
-                        if (tap_count <= TAPPING_TOGGLE) {
-                            layer_invert(action.layer_tap.val);
-                        }
-                    }
-                    break;
-                case OP_ON_OFF:
-                    event.pressed ? layer_on(action.layer_tap.val) : layer_off(action.layer_tap.val);
-                    break;
-                case OP_OFF_ON:
-                    event.pressed ? layer_off(action.layer_tap.val) : layer_on(action.layer_tap.val);
-                    break;
-                case OP_SET_CLEAR:
-                    event.pressed ? layer_move(action.layer_tap.val) : layer_clear();
-                    break;
-#        ifndef NO_ACTION_ONESHOT
-                case OP_ONESHOT:
-                    // Oneshot modifier
-#            if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
-                    do_release_oneshot = false;
-                    if (event.pressed) {
-                        del_mods(get_oneshot_locked_mods());
-                        if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
-                            reset_oneshot_layer();
-                            layer_off(action.layer_tap.val);
-                            break;
-                        } else if (tap_count < ONESHOT_TAP_TOGGLE) {
-                            layer_on(action.layer_tap.val);
-                            set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
-                        }
-                    } else {
-                        add_mods(get_oneshot_locked_mods());
-                        if (tap_count >= ONESHOT_TAP_TOGGLE) {
-                            reset_oneshot_layer();
-                            clear_oneshot_locked_mods();
-                            set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
-                        } else {
-                            clear_oneshot_layer_state(ONESHOT_PRESSED);
-                        }
-                    }
-#            else
-                    if (event.pressed) {
-                        layer_on(action.layer_tap.val);
-                        set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
-                    } else {
-                        clear_oneshot_layer_state(ONESHOT_PRESSED);
-                        if (tap_count > 1) {
-                            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
-                        }
-                    }
-#            endif
-                    break;
-#        endif
-                default:
-                    /* tap key */
-                    if (event.pressed) {
-                        if (tap_count > 0) {
-                            dprint("KEYMAP_TAP_KEY: Tap: register_code\n");
-                            register_code(action.layer_tap.code);
-                        } else {
-                            dprint("KEYMAP_TAP_KEY: No tap: On on press\n");
-                            layer_on(action.layer_tap.val);
-                        }
-                    } else {
-                        if (tap_count > 0) {
-                            dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n");
-                            if (action.layer_tap.code == KC_CAPS) {
-                                wait_ms(TAP_HOLD_CAPS_DELAY);
-                            } else {
-                                wait_ms(TAP_CODE_DELAY);
-                            }
-                            unregister_code(action.layer_tap.code);
-                        } else {
-                            dprint("KEYMAP_TAP_KEY: No tap: Off on release\n");
-                            layer_off(action.layer_tap.val);
-                        }
-                    }
-                    break;
-            }
-            break;
-#    endif
-#endif
-            /* Extentions */
-#ifndef NO_ACTION_MACRO
-        case ACT_MACRO:
-            action_macro_play(action_get_macro(record, action.func.id, action.func.opt));
-            break;
-#endif
-#ifdef SWAP_HANDS_ENABLE
-        case ACT_SWAP_HANDS:
-            switch (action.swap.code) {
-                case OP_SH_TOGGLE:
-                    if (event.pressed) {
-                        swap_hands = !swap_hands;
-                    }
-                    break;
-                case OP_SH_ON_OFF:
-                    swap_hands = event.pressed;
-                    break;
-                case OP_SH_OFF_ON:
-                    swap_hands = !event.pressed;
-                    break;
-                case OP_SH_ON:
-                    if (!event.pressed) {
-                        swap_hands = true;
-                    }
-                    break;
-                case OP_SH_OFF:
-                    if (!event.pressed) {
-                        swap_hands = false;
-                    }
-                    break;
-#    ifndef NO_ACTION_ONESHOT
-                case OP_SH_ONESHOT:
-                    if (event.pressed) {
-                        set_oneshot_swaphands();
-                    } else {
-                        release_oneshot_swaphands();
-                    }
-                    break;
-#    endif
-
-#    ifndef NO_ACTION_TAPPING
-                case OP_SH_TAP_TOGGLE:
-                    /* tap toggle */
-
-                    if (event.pressed) {
-                        if (swap_held) {
-                            swap_held = false;
-                        } else {
-                            swap_hands = !swap_hands;
-                        }
-                    } else {
-                        if (tap_count < TAPPING_TOGGLE) {
-                            swap_hands = !swap_hands;
-                        }
-                    }
-                    break;
-                default:
-                    /* tap key */
-                    if (tap_count > 0) {
-                        if (swap_held) {
-                            swap_hands = !swap_hands;  // undo hold set up in _tap_hint
-                            swap_held  = false;
-                        }
-                        if (event.pressed) {
-                            register_code(action.swap.code);
-                        } else {
-                            wait_ms(TAP_CODE_DELAY);
-                            unregister_code(action.swap.code);
-                            *record = (keyrecord_t){};  // hack: reset tap mode
-                        }
-                    } else {
-                        if (swap_held && !event.pressed) {
-                            swap_hands = !swap_hands;  // undo hold set up in _tap_hint
-                            swap_held  = false;
-                        }
-                    }
-#    endif
-            }
-#endif
-#ifndef NO_ACTION_FUNCTION
-        case ACT_FUNCTION:
-            action_function(record, action.func.id, action.func.opt);
-            break;
-#endif
-        default:
-            break;
-    }
-
-#ifndef NO_ACTION_LAYER
-    // if this event is a layer action, update the leds
-    switch (action.kind.id) {
-        case ACT_LAYER:
-        case ACT_LAYER_MODS:
-#    ifndef NO_ACTION_TAPPING
-        case ACT_LAYER_TAP:
-        case ACT_LAYER_TAP_EXT:
-#    endif
-            led_set(host_keyboard_leds());
-            break;
-        default:
-            break;
-    }
-#endif
-
-#ifndef NO_ACTION_TAPPING
-#    if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
-    if (!is_tap_action(action)) {
-        retro_tapping_counter = 0;
-    } else {
-        if (event.pressed) {
-            if (tap_count > 0) {
-                retro_tapping_counter = 0;
-            }
-        } else {
-            if (tap_count > 0) {
-                retro_tapping_counter = 0;
-            } else {
-                if (
-#        ifdef RETRO_TAPPING_PER_KEY
-                    get_retro_tapping(get_event_keycode(record->event, false), record) &&
-#        endif
-                    retro_tapping_counter == 2) {
-                    tap_code(action.layer_tap.code);
-                }
-                retro_tapping_counter = 0;
-            }
-        }
-    }
-#    endif
-#endif
-
-#ifdef SWAP_HANDS_ENABLE
-#    ifndef NO_ACTION_ONESHOT
-    if (event.pressed && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)) {
-        use_oneshot_swaphands();
-    }
-#    endif
-#endif
-
-#ifndef NO_ACTION_ONESHOT
-    /* Because we switch layers after a oneshot event, we need to release the
-     * key before we leave the layer or no key up event will be generated.
-     */
-    if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED)) {
-        record->event.pressed = false;
-        layer_on(get_oneshot_layer());
-        process_record(record);
-        layer_off(get_oneshot_layer());
-    }
-#endif
-}
-
-/** \brief Utilities for actions. (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-void register_code(uint8_t code) {
-    if (code == KC_NO) {
-        return;
-    }
-#ifdef LOCKING_SUPPORT_ENABLE
-    else if (KC_LOCKING_CAPS == code) {
-#    ifdef LOCKING_RESYNC_ENABLE
-        // Resync: ignore if caps lock already is on
-        if (host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK)) return;
-#    endif
-        add_key(KC_CAPSLOCK);
-        send_keyboard_report();
-        wait_ms(100);
-        del_key(KC_CAPSLOCK);
-        send_keyboard_report();
-    }
-
-    else if (KC_LOCKING_NUM == code) {
-#    ifdef LOCKING_RESYNC_ENABLE
-        if (host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)) return;
-#    endif
-        add_key(KC_NUMLOCK);
-        send_keyboard_report();
-        wait_ms(100);
-        del_key(KC_NUMLOCK);
-        send_keyboard_report();
-    }
-
-    else if (KC_LOCKING_SCROLL == code) {
-#    ifdef LOCKING_RESYNC_ENABLE
-        if (host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK)) return;
-#    endif
-        add_key(KC_SCROLLLOCK);
-        send_keyboard_report();
-        wait_ms(100);
-        del_key(KC_SCROLLLOCK);
-        send_keyboard_report();
-    }
-#endif
-
-    else if IS_KEY (code) {
-        // TODO: should push command_proc out of this block?
-        if (command_proc(code)) return;
-
-#ifndef NO_ACTION_ONESHOT
-/* TODO: remove
-        if (oneshot_state.mods && !oneshot_state.disabled) {
-            uint8_t tmp_mods = get_mods();
-            add_mods(oneshot_state.mods);
-
-            add_key(code);
-            send_keyboard_report();
-
-            set_mods(tmp_mods);
-            send_keyboard_report();
-            oneshot_cancel();
-        } else
-*/
-#endif
-        {
-            // Force a new key press if the key is already pressed
-            // without this, keys with the same keycode, but different
-            // modifiers will be reported incorrectly, see issue #1708
-            if (is_key_pressed(keyboard_report, code)) {
-                del_key(code);
-                send_keyboard_report();
-            }
-            add_key(code);
-            send_keyboard_report();
-        }
-    } else if IS_MOD (code) {
-        add_mods(MOD_BIT(code));
-        send_keyboard_report();
-    }
-#ifdef EXTRAKEY_ENABLE
-    else if IS_SYSTEM (code) {
-        host_system_send(KEYCODE2SYSTEM(code));
-    } else if IS_CONSUMER (code) {
-        host_consumer_send(KEYCODE2CONSUMER(code));
-    }
-#endif
-#ifdef MOUSEKEY_ENABLE
-    else if IS_MOUSEKEY (code) {
-        mousekey_on(code);
-        mousekey_send();
-    }
-#endif
-}
-
-/** \brief Utilities for actions. (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-void unregister_code(uint8_t code) {
-    if (code == KC_NO) {
-        return;
-    }
-#ifdef LOCKING_SUPPORT_ENABLE
-    else if (KC_LOCKING_CAPS == code) {
-#    ifdef LOCKING_RESYNC_ENABLE
-        // Resync: ignore if caps lock already is off
-        if (!(host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK))) return;
-#    endif
-        add_key(KC_CAPSLOCK);
-        send_keyboard_report();
-        del_key(KC_CAPSLOCK);
-        send_keyboard_report();
-    }
-
-    else if (KC_LOCKING_NUM == code) {
-#    ifdef LOCKING_RESYNC_ENABLE
-        if (!(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK))) return;
-#    endif
-        add_key(KC_NUMLOCK);
-        send_keyboard_report();
-        del_key(KC_NUMLOCK);
-        send_keyboard_report();
-    }
-
-    else if (KC_LOCKING_SCROLL == code) {
-#    ifdef LOCKING_RESYNC_ENABLE
-        if (!(host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK))) return;
-#    endif
-        add_key(KC_SCROLLLOCK);
-        send_keyboard_report();
-        del_key(KC_SCROLLLOCK);
-        send_keyboard_report();
-    }
-#endif
-
-    else if IS_KEY (code) {
-        del_key(code);
-        send_keyboard_report();
-    } else if IS_MOD (code) {
-        del_mods(MOD_BIT(code));
-        send_keyboard_report();
-    } else if IS_SYSTEM (code) {
-        host_system_send(0);
-    } else if IS_CONSUMER (code) {
-        host_consumer_send(0);
-    }
-#ifdef MOUSEKEY_ENABLE
-    else if IS_MOUSEKEY (code) {
-        mousekey_off(code);
-        mousekey_send();
-    }
-#endif
-}
-
-/** \brief Tap a keycode with a delay.
- *
- * \param code The basic keycode to tap.
- * \param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it.
- */
-void tap_code_delay(uint8_t code, uint16_t delay) {
-    register_code(code);
-    for (uint16_t i = delay; i > 0; i--) {
-        wait_ms(1);
-    }
-    unregister_code(code);
-}
-
-/** \brief Tap a keycode with the default delay.
- *
- * \param code The basic keycode to tap. If `code` is `KC_CAPS`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.
- */
-void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }
-
-/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately.
- *
- * \param mods A bitfield of modifiers to register.
- */
-void register_mods(uint8_t mods) {
-    if (mods) {
-        add_mods(mods);
-        send_keyboard_report();
-    }
-}
-
-/** \brief Removes the given physically pressed modifiers and sends a keyboard report immediately.
- *
- * \param mods A bitfield of modifiers to unregister.
- */
-void unregister_mods(uint8_t mods) {
-    if (mods) {
-        del_mods(mods);
-        send_keyboard_report();
-    }
-}
-
-/** \brief Adds the given weak modifiers and sends a keyboard report immediately.
- *
- * \param mods A bitfield of modifiers to register.
- */
-void register_weak_mods(uint8_t mods) {
-    if (mods) {
-        add_weak_mods(mods);
-        send_keyboard_report();
-    }
-}
-
-/** \brief Removes the given weak modifiers and sends a keyboard report immediately.
- *
- * \param mods A bitfield of modifiers to unregister.
- */
-void unregister_weak_mods(uint8_t mods) {
-    if (mods) {
-        del_weak_mods(mods);
-        send_keyboard_report();
-    }
-}
-
-/** \brief Utilities for actions. (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-void clear_keyboard(void) {
-    clear_mods();
-    clear_keyboard_but_mods();
-}
-
-/** \brief Utilities for actions. (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-void clear_keyboard_but_mods(void) {
-    clear_keys();
-    clear_keyboard_but_mods_and_keys();
-}
-
-/** \brief Utilities for actions. (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-void clear_keyboard_but_mods_and_keys() {
-#ifdef EXTRAKEY_ENABLE
-    host_system_send(0);
-    host_consumer_send(0);
-#endif
-    clear_weak_mods();
-    clear_macro_mods();
-    send_keyboard_report();
-#ifdef MOUSEKEY_ENABLE
-    mousekey_clear();
-    mousekey_send();
-#endif
-}
-
-/** \brief Utilities for actions. (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-bool is_tap_key(keypos_t key) {
-    action_t action = layer_switch_get_action(key);
-    return is_tap_action(action);
-}
-
-/** \brief Utilities for actions. (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-bool is_tap_action(action_t action) {
-    switch (action.kind.id) {
-        case ACT_LMODS_TAP:
-        case ACT_RMODS_TAP:
-        case ACT_LAYER_TAP:
-        case ACT_LAYER_TAP_EXT:
-            switch (action.layer_tap.code) {
-                case KC_NO ... KC_RGUI:
-                case OP_TAP_TOGGLE:
-                case OP_ONESHOT:
-                    return true;
-            }
-            return false;
-        case ACT_SWAP_HANDS:
-            switch (action.swap.code) {
-                case KC_NO ... KC_RGUI:
-                case OP_SH_TAP_TOGGLE:
-                    return true;
-            }
-            return false;
-        case ACT_MACRO:
-        case ACT_FUNCTION:
-            if (action.func.opt & FUNC_TAP) {
-                return true;
-            }
-            return false;
-    }
-    return false;
-}
-
-/** \brief Debug print (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-void debug_event(keyevent_t event) { dprintf("%04X%c(%u)", (event.key.row << 8 | event.key.col), (event.pressed ? 'd' : 'u'), event.time); }
-/** \brief Debug print (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-void debug_record(keyrecord_t record) {
-    debug_event(record.event);
-#ifndef NO_ACTION_TAPPING
-    dprintf(":%u%c", record.tap.count, (record.tap.interrupted ? '-' : ' '));
-#endif
-}
-
-/** \brief Debug print (FIXME: Needs better description)
- *
- * FIXME: Needs documentation.
- */
-void debug_action(action_t action) {
-    switch (action.kind.id) {
-        case ACT_LMODS:
-            dprint("ACT_LMODS");
-            break;
-        case ACT_RMODS:
-            dprint("ACT_RMODS");
-            break;
-        case ACT_LMODS_TAP:
-            dprint("ACT_LMODS_TAP");
-            break;
-        case ACT_RMODS_TAP:
-            dprint("ACT_RMODS_TAP");
-            break;
-        case ACT_USAGE:
-            dprint("ACT_USAGE");
-            break;
-        case ACT_MOUSEKEY:
-            dprint("ACT_MOUSEKEY");
-            break;
-        case ACT_LAYER:
-            dprint("ACT_LAYER");
-            break;
-        case ACT_LAYER_MODS:
-            dprint("ACT_LAYER_MODS");
-            break;
-        case ACT_LAYER_TAP:
-            dprint("ACT_LAYER_TAP");
-            break;
-        case ACT_LAYER_TAP_EXT:
-            dprint("ACT_LAYER_TAP_EXT");
-            break;
-        case ACT_MACRO:
-            dprint("ACT_MACRO");
-            break;
-        case ACT_FUNCTION:
-            dprint("ACT_FUNCTION");
-            break;
-        case ACT_SWAP_HANDS:
-            dprint("ACT_SWAP_HANDS");
-            break;
-        default:
-            dprint("UNKNOWN");
-            break;
-    }
-    dprintf("[%X:%02X]", action.kind.param >> 8, action.kind.param & 0xff);
-}
diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h
deleted file mode 100644
index 8cb4722c6e..0000000000
--- a/tmk_core/common/action.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
-Copyright 2012,2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "keyboard.h"
-#include "keycode.h"
-#include "action_code.h"
-#include "action_macro.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Disable macro and function features when LTO is enabled, since they break */
-#ifdef LTO_ENABLE
-#    ifndef NO_ACTION_MACRO
-#        define NO_ACTION_MACRO
-#    endif
-#    ifndef NO_ACTION_FUNCTION
-#        define NO_ACTION_FUNCTION
-#    endif
-#endif
-
-/* tapping count and state */
-typedef struct {
-    bool    interrupted : 1;
-    bool    reserved2 : 1;
-    bool    reserved1 : 1;
-    bool    reserved0 : 1;
-    uint8_t count : 4;
-} tap_t;
-
-/* Key event container for recording */
-typedef struct {
-    keyevent_t event;
-#ifndef NO_ACTION_TAPPING
-    tap_t tap;
-#endif
-} keyrecord_t;
-
-/* Execute action per keyevent */
-void action_exec(keyevent_t event);
-
-/* action for key */
-action_t action_for_key(uint8_t layer, keypos_t key);
-
-/* macro */
-const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt);
-
-/* user defined special function */
-void action_function(keyrecord_t *record, uint8_t id, uint8_t opt);
-
-/* keyboard-specific key event (pre)processing */
-bool process_record_quantum(keyrecord_t *record);
-
-/* Utilities for actions.  */
-#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
-extern bool disable_action_cache;
-#endif
-
-/* Code for handling one-handed key modifiers. */
-#ifdef SWAP_HANDS_ENABLE
-extern bool                   swap_hands;
-extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS];
-#    if (MATRIX_COLS <= 8)
-typedef uint8_t swap_state_row_t;
-#    elif (MATRIX_COLS <= 16)
-typedef uint16_t swap_state_row_t;
-#    elif (MATRIX_COLS <= 32)
-typedef uint32_t swap_state_row_t;
-#    else
-#        error "MATRIX_COLS: invalid value"
-#    endif
-
-void process_hand_swap(keyevent_t *record);
-#endif
-
-void process_record_nocache(keyrecord_t *record);
-void process_record(keyrecord_t *record);
-void process_record_handler(keyrecord_t *record);
-void post_process_record_quantum(keyrecord_t *record);
-void process_action(keyrecord_t *record, action_t action);
-void register_code(uint8_t code);
-void unregister_code(uint8_t code);
-void tap_code(uint8_t code);
-void tap_code_delay(uint8_t code, uint16_t delay);
-void register_mods(uint8_t mods);
-void unregister_mods(uint8_t mods);
-void register_weak_mods(uint8_t mods);
-void unregister_weak_mods(uint8_t mods);
-// void set_mods(uint8_t mods);
-void clear_keyboard(void);
-void clear_keyboard_but_mods(void);
-void clear_keyboard_but_mods_and_keys(void);
-void layer_switch(uint8_t new_layer);
-bool is_tap_key(keypos_t key);
-bool is_tap_action(action_t action);
-
-#ifndef NO_ACTION_TAPPING
-void process_record_tap_hint(keyrecord_t *record);
-#endif
-
-/* debug */
-void debug_event(keyevent_t event);
-void debug_record(keyrecord_t record);
-void debug_action(action_t action);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/tmk_core/common/action_code.h b/tmk_core/common/action_code.h
deleted file mode 100644
index eb18c36ae8..0000000000
--- a/tmk_core/common/action_code.h
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
-Copyright 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-/** \brief Action codes
- *
- * 16bit code: action_kind(4bit) + action_parameter(12bit)
- *
- * Key Actions(00xx)
- * -----------------
- * ACT_MODS(000r):
- * 000r|0000|0000 0000    No action code
- * 000r|0000|0000 0001    Transparent code
- * 000r|0000| keycode     Key
- * 000r|mods|0000 0000    Modifiers
- * 000r|mods| keycode     Modifiers+Key(Modified key)
- *   r: Left/Right flag(Left:0, Right:1)
- *
- * ACT_MODS_TAP(001r):
- * 001r|mods|0000 0000    Modifiers with OneShot
- * 001r|mods|0000 0001    Modifiers with tap toggle
- * 001r|mods|0000 00xx    (reserved)
- * 001r|mods| keycode     Modifiers with Tap Key(Dual role)
- *
- * Other Keys(01xx)
- * ----------------
- * ACT_USAGE(0100): TODO: Not needed?
- * 0100|00| usage(10)     System control(0x80) - General Desktop page(0x01)
- * 0100|01| usage(10)     Consumer control(0x01) - Consumer page(0x0C)
- * 0100|10| usage(10)     (reserved)
- * 0100|11| usage(10)     (reserved)
- *
- * ACT_MOUSEKEY(0101): TODO: Merge these two actions to conserve space?
- * 0101|xxxx| keycode     Mouse key
- *
- * ACT_SWAP_HANDS(0110):
- * 0110|xxxx| keycode     Swap hands (keycode on tap, or options)
- *
- * 0111|xxxx xxxx xxxx    (reserved)
- *
- * Layer Actions(10xx)
- * -------------------
- * ACT_LAYER(1000):
- * 1000|oo00|pppE BBBB   Default Layer Bitwise operation
- *   oo:    operation(00:AND, 01:OR, 10:XOR, 11:SET)
- *   ppp:   4-bit chunk part(0-7)
- *   EBBBB: bits and extra bit
- * 1000|ooee|pppE BBBB   Layer Bitwise Operation
- *   oo:    operation(00:AND, 01:OR, 10:XOR, 11:SET)
- *   ppp:   4-bit chunk part(0-7)
- *   EBBBB: bits and extra bit
- *   ee:    on event(01:press, 10:release, 11:both)
- *
- * ACT_LAYER_MODS(1001):
- * 1001|LLLL| mods       Layer with modifiers held
- *
- * ACT_LAYER_TAP(101x):
- * 101E|LLLL| keycode    On/Off with tap key    (0x00-DF)[TAP]
- * 101E|LLLL|1110 mods   On/Off with modifiers  (0xE0-EF)[NOT TAP]
- * 101E|LLLL|1111 0000   Invert with tap toggle (0xF0)   [TAP]
- * 101E|LLLL|1111 0001   On/Off                 (0xF1)   [NOT TAP]
- * 101E|LLLL|1111 0010   Off/On                 (0xF2)   [NOT TAP]
- * 101E|LLLL|1111 0011   Set/Clear              (0xF3)   [NOT TAP]
- * 101E|LLLL|1111 0100   One Shot Layer         (0xF4)   [TAP]
- * 101E|LLLL|1111 xxxx   Reserved               (0xF5-FF)
- *   ELLLL: layer 0-31(E: extra bit for layer 16-31)
- *
- * Extensions(11xx)
- * ----------------
- * ACT_MACRO(1100):
- * 1100|opt | id(8)      Macro play?
- * 1100|1111| id(8)      Macro record?
- *
- * 1101|xxxx xxxx xxxx   (reserved)
- * 1110|xxxx xxxx xxxx   (reserved)
- *
- * ACT_FUNCTION(1111):
- * 1111| address(12)     Function?
- * 1111|opt | id(8)      Function?
- */
-enum action_kind_id {
-    /* Key Actions */
-    ACT_MODS      = 0b0000,
-    ACT_LMODS     = 0b0000,
-    ACT_RMODS     = 0b0001,
-    ACT_MODS_TAP  = 0b0010,
-    ACT_LMODS_TAP = 0b0010,
-    ACT_RMODS_TAP = 0b0011,
-    /* Other Keys */
-    ACT_USAGE    = 0b0100,
-    ACT_MOUSEKEY = 0b0101,
-    /* One-hand Support */
-    ACT_SWAP_HANDS = 0b0110,
-    /* Layer Actions */
-    ACT_LAYER         = 0b1000,
-    ACT_LAYER_MODS    = 0b1001,
-    ACT_LAYER_TAP     = 0b1010, /* Layer  0-15 */
-    ACT_LAYER_TAP_EXT = 0b1011, /* Layer 16-31 */
-    /* Extensions */
-    ACT_MACRO    = 0b1100,
-    ACT_FUNCTION = 0b1111
-};
-
-/** \brief Action Code Struct
- *
- * NOTE:
- * In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15).
- * AVR looks like a little endian in avr-gcc.
- * Not portable across compiler/endianness?
- *
- * Byte order and bit order of 0x1234:
- *   Big endian:                Little endian:
- *   --------------------       --------------------
- *   FEDC BA98  7654 3210       0123 4567  89AB CDEF
- *   0001 0010  0011 0100       0010 1100  0100 1000
- *     0x12       0x34            0x34       0x12
- */
-typedef union {
-    uint16_t code;
-    struct action_kind {
-        uint16_t param : 12;
-        uint8_t  id : 4;
-    } kind;
-    struct action_key {
-        uint8_t code : 8;
-        uint8_t mods : 4;
-        uint8_t kind : 4;
-    } key;
-    struct action_layer_bitop {
-        uint8_t bits : 4;
-        uint8_t xbit : 1;
-        uint8_t part : 3;
-        uint8_t on : 2;
-        uint8_t op : 2;
-        uint8_t kind : 4;
-    } layer_bitop;
-    struct action_layer_mods {
-        uint8_t mods : 8;
-        uint8_t layer : 4;
-        uint8_t kind : 4;
-    } layer_mods;
-    struct action_layer_tap {
-        uint8_t code : 8;
-        uint8_t val : 5;
-        uint8_t kind : 3;
-    } layer_tap;
-    struct action_usage {
-        uint16_t code : 10;
-        uint8_t  page : 2;
-        uint8_t  kind : 4;
-    } usage;
-    struct action_function {
-        uint8_t id : 8;
-        uint8_t opt : 4;
-        uint8_t kind : 4;
-    } func;
-    struct action_swap {
-        uint8_t code : 8;
-        uint8_t opt : 4;
-        uint8_t kind : 4;
-    } swap;
-} action_t;
-
-/* action utility */
-#define ACTION_NO 0
-#define ACTION_TRANSPARENT 1
-#define ACTION(kind, param) ((kind) << 12 | (param))
-
-/** \brief Key Actions
- *
- * Mod bits:    43210
- *   bit 0      ||||+- Control
- *   bit 1      |||+-- Shift
- *   bit 2      ||+--- Alt
- *   bit 3      |+---- Gui
- *   bit 4      +----- LR flag(Left:0, Right:1)
- */
-enum mods_bit {
-    MOD_LCTL = 0x01,
-    MOD_LSFT = 0x02,
-    MOD_LALT = 0x04,
-    MOD_LGUI = 0x08,
-    MOD_RCTL = 0x11,
-    MOD_RSFT = 0x12,
-    MOD_RALT = 0x14,
-    MOD_RGUI = 0x18,
-};
-enum mods_codes {
-    MODS_ONESHOT    = 0x00,
-    MODS_TAP_TOGGLE = 0x01,
-};
-#define ACTION_KEY(key) ACTION(ACT_MODS, (key))
-#define ACTION_MODS(mods) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | 0)
-#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | (key))
-#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | (key))
-#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_ONESHOT)
-#define ACTION_MODS_TAP_TOGGLE(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_TAP_TOGGLE)
-
-/** \brief Other Keys
- */
-enum usage_pages { PAGE_SYSTEM, PAGE_CONSUMER };
-#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id))
-#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id))
-#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key)
-
-/** \brief Layer Actions
- */
-enum layer_param_on {
-    ON_PRESS   = 1,
-    ON_RELEASE = 2,
-    ON_BOTH    = 3,
-};
-
-/** \brief Layer Actions
- */
-enum layer_param_bit_op {
-    OP_BIT_AND = 0,
-    OP_BIT_OR  = 1,
-    OP_BIT_XOR = 2,
-    OP_BIT_SET = 3,
-};
-
-/** \brief Layer Actions
- */
-enum layer_param_tap_op {
-    OP_TAP_TOGGLE = 0xF0,
-    OP_ON_OFF,
-    OP_OFF_ON,
-    OP_SET_CLEAR,
-    OP_ONESHOT,
-};
-#define ACTION_LAYER_BITOP(op, part, bits, on) ACTION(ACT_LAYER, (op) << 10 | (on) << 8 | (part) << 5 | ((bits)&0x1f))
-#define ACTION_LAYER_TAP(layer, key) ACTION(ACT_LAYER_TAP, (layer) << 8 | (key))
-/* Default Layer */
-#define ACTION_DEFAULT_LAYER_SET(layer) ACTION_DEFAULT_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4))
-/* Layer Operation */
-#define ACTION_LAYER_CLEAR(on) ACTION_LAYER_BIT_AND(0, 0, (on))
-#define ACTION_LAYER_MOMENTARY(layer) ACTION_LAYER_ON_OFF(layer)
-#define ACTION_LAYER_TOGGLE(layer) ACTION_LAYER_INVERT(layer, ON_RELEASE)
-#define ACTION_LAYER_INVERT(layer, on) ACTION_LAYER_BIT_XOR((layer) / 4, 1 << ((layer) % 4), (on))
-#define ACTION_LAYER_ON(layer, on) ACTION_LAYER_BIT_OR((layer) / 4, 1 << ((layer) % 4), (on))
-#define ACTION_LAYER_OFF(layer, on) ACTION_LAYER_BIT_AND((layer) / 4, ~(1 << ((layer) % 4)), (on))
-#define ACTION_LAYER_SET(layer, on) ACTION_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4), (on))
-#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF)
-#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON)
-#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR)
-#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT)
-#define ACTION_LAYER_MODS(layer, mods) ACTION(ACT_LAYER_MODS, (layer) << 8 | (mods))
-/* With Tapping */
-#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key))
-#define ACTION_LAYER_TAP_TOGGLE(layer) ACTION_LAYER_TAP((layer), OP_TAP_TOGGLE)
-/* Bitwise Operation */
-#define ACTION_LAYER_BIT_AND(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), (on))
-#define ACTION_LAYER_BIT_OR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), (on))
-#define ACTION_LAYER_BIT_XOR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), (on))
-#define ACTION_LAYER_BIT_SET(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), (on))
-/* Default Layer Bitwise Operation */
-#define ACTION_DEFAULT_LAYER_BIT_AND(part, bits) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), 0)
-#define ACTION_DEFAULT_LAYER_BIT_OR(part, bits) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), 0)
-#define ACTION_DEFAULT_LAYER_BIT_XOR(part, bits) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), 0)
-#define ACTION_DEFAULT_LAYER_BIT_SET(part, bits) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), 0)
-
-/* Macro */
-#define ACTION_MACRO(id) ACTION(ACT_MACRO, (id))
-#define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP << 8 | (id))
-#define ACTION_MACRO_OPT(id, opt) ACTION(ACT_MACRO, (opt) << 8 | (id))
-/* Function */
-enum function_opts {
-    FUNC_TAP = 0x8, /* indciates function is tappable */
-};
-#define ACTION_FUNCTION(id) ACTION(ACT_FUNCTION, (id))
-#define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP << 8 | (id))
-#define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt) << 8 | (id))
-/* OneHand Support */
-enum swap_hands_param_tap_op {
-    OP_SH_TOGGLE = 0xF0,
-    OP_SH_TAP_TOGGLE,
-    OP_SH_ON_OFF,
-    OP_SH_OFF_ON,
-    OP_SH_OFF,
-    OP_SH_ON,
-    OP_SH_ONESHOT,
-};
-
-#define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF()
-#define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE)
-#define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE)
-#define ACTION_SWAP_HANDS_ONESHOT() ACTION(ACT_SWAP_HANDS, OP_SH_ONESHOT)
-#define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key)
-#define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF)
-#define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON)
-#define ACTION_SWAP_HANDS_ON() ACTION(ACT_SWAP_HANDS, OP_SH_ON)
-#define ACTION_SWAP_HANDS_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_OFF)
diff --git a/tmk_core/common/action_layer.c b/tmk_core/common/action_layer.c
deleted file mode 100644
index af2d7d964b..0000000000
--- a/tmk_core/common/action_layer.c
+++ /dev/null
@@ -1,279 +0,0 @@
-#include <stdint.h>
-#include "keyboard.h"
-#include "action.h"
-#include "util.h"
-#include "action_layer.h"
-
-#ifdef DEBUG_ACTION
-#    include "debug.h"
-#else
-#    include "nodebug.h"
-#endif
-
-/** \brief Default Layer State
- */
-layer_state_t default_layer_state = 0;
-
-/** \brief Default Layer State Set At user Level
- *
- * Run user code on default layer state change
- */
-__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state) { return state; }
-
-/** \brief Default Layer State Set At Keyboard Level
- *
- *  Run keyboard code on default layer state change
- */
-__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state) { return default_layer_state_set_user(state); }
-
-/** \brief Default Layer State Set
- *
- * Static function to set the default layer state, prints debug info and clears keys
- */
-static void default_layer_state_set(layer_state_t state) {
-    state = default_layer_state_set_kb(state);
-    debug("default_layer_state: ");
-    default_layer_debug();
-    debug(" to ");
-    default_layer_state = state;
-    default_layer_debug();
-    debug("\n");
-#ifdef STRICT_LAYER_RELEASE
-    clear_keyboard_but_mods();  // To avoid stuck keys
-#else
-    clear_keyboard_but_mods_and_keys();  // Don't reset held keys
-#endif
-}
-
-/** \brief Default Layer Print
- *
- * Print out the hex value of the 32-bit default layer state, as well as the value of the highest bit.
- */
-void default_layer_debug(void) { dprintf("%08lX(%u)", default_layer_state, get_highest_layer(default_layer_state)); }
-
-/** \brief Default Layer Set
- *
- * Sets the default layer state.
- */
-void default_layer_set(layer_state_t state) { default_layer_state_set(state); }
-
-#ifndef NO_ACTION_LAYER
-/** \brief Default Layer Or
- *
- * Turns on the default layer based on matching bits between specifed layer and existing layer state
- */
-void default_layer_or(layer_state_t state) { default_layer_state_set(default_layer_state | state); }
-/** \brief Default Layer And
- *
- * Turns on default layer based on matching enabled bits between specifed layer and existing layer state
- */
-void default_layer_and(layer_state_t state) { default_layer_state_set(default_layer_state & state); }
-/** \brief Default Layer Xor
- *
- * Turns on default layer based on non-matching bits between specifed layer and existing layer state
- */
-void default_layer_xor(layer_state_t state) { default_layer_state_set(default_layer_state ^ state); }
-#endif
-
-#ifndef NO_ACTION_LAYER
-/** \brief Keymap Layer State
- */
-layer_state_t layer_state = 0;
-
-/** \brief Layer state set user
- *
- * Runs user code on layer state change
- */
-__attribute__((weak)) layer_state_t layer_state_set_user(layer_state_t state) { return state; }
-
-/** \brief Layer state set keyboard
- *
- * Runs keyboard code on layer state change
- */
-__attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) { return layer_state_set_user(state); }
-
-/** \brief Layer state set
- *
- * Sets the layer to match the specifed state (a bitmask)
- */
-void layer_state_set(layer_state_t state) {
-    state = layer_state_set_kb(state);
-    dprint("layer_state: ");
-    layer_debug();
-    dprint(" to ");
-    layer_state = state;
-    layer_debug();
-    dprintln();
-#    ifdef STRICT_LAYER_RELEASE
-    clear_keyboard_but_mods();  // To avoid stuck keys
-#    else
-    clear_keyboard_but_mods_and_keys();  // Don't reset held keys
-#    endif
-}
-
-/** \brief Layer clear
- *
- * Turn off all layers
- */
-void layer_clear(void) { layer_state_set(0); }
-
-/** \brief Layer state is
- *
- * Return whether the given state is on (it might still be shadowed by a higher state, though)
- */
-bool layer_state_is(uint8_t layer) { return layer_state_cmp(layer_state, layer); }
-
-/** \brief Layer state compare
- *
- * Used for comparing layers {mostly used for unit testing}
- */
-bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) {
-    if (!cmp_layer_state) {
-        return layer == 0;
-    }
-    return (cmp_layer_state & (1UL << layer)) != 0;
-}
-
-/** \brief Layer move
- *
- * Turns on the given layer and turn off all other layers
- */
-void layer_move(uint8_t layer) { layer_state_set(1UL << layer); }
-
-/** \brief Layer on
- *
- * Turns on given layer
- */
-void layer_on(uint8_t layer) { layer_state_set(layer_state | (1UL << layer)); }
-
-/** \brief Layer off
- *
- * Turns off given layer
- */
-void layer_off(uint8_t layer) { layer_state_set(layer_state & ~(1UL << layer)); }
-
-/** \brief Layer invert
- *
- * Toggle the given layer (set it if it's unset, or unset it if it's set)
- */
-void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ (1UL << layer)); }
-
-/** \brief Layer or
- *
- * Turns on layers based on matching bits between specifed layer and existing layer state
- */
-void layer_or(layer_state_t state) { layer_state_set(layer_state | state); }
-/** \brief Layer and
- *
- * Turns on layers based on matching enabled bits between specifed layer and existing layer state
- */
-void layer_and(layer_state_t state) { layer_state_set(layer_state & state); }
-/** \brief Layer xor
- *
- * Turns on layers based on non-matching bits between specifed layer and existing layer state
- */
-void layer_xor(layer_state_t state) { layer_state_set(layer_state ^ state); }
-
-/** \brief Layer debug printing
- *
- * Print out the hex value of the 32-bit layer state, as well as the value of the highest bit.
- */
-void layer_debug(void) { dprintf("%08lX(%u)", layer_state, get_highest_layer(layer_state)); }
-#endif
-
-#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
-/** \brief source layer cache
- */
-
-uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}};
-
-/** \brief update source layers cache
- *
- * Updates the cached keys when changing layers
- */
-void update_source_layers_cache(keypos_t key, uint8_t layer) {
-    const uint8_t key_number  = key.col + (key.row * MATRIX_COLS);
-    const uint8_t storage_row = key_number / 8;
-    const uint8_t storage_bit = key_number % 8;
-
-    for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
-        source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit);
-    }
-}
-
-/** \brief read source layers cache
- *
- * reads the cached keys stored when the layer was changed
- */
-uint8_t read_source_layers_cache(keypos_t key) {
-    const uint8_t key_number  = key.col + (key.row * MATRIX_COLS);
-    const uint8_t storage_row = key_number / 8;
-    const uint8_t storage_bit = key_number % 8;
-    uint8_t       layer       = 0;
-
-    for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
-        layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number;
-    }
-
-    return layer;
-}
-#endif
-
-/** \brief Store or get action (FIXME: Needs better summary)
- *
- * Make sure the action triggered when the key is released is the same
- * one as the one triggered on press. It's important for the mod keys
- * when the layer is switched after the down event but before the up
- * event as they may get stuck otherwise.
- */
-action_t store_or_get_action(bool pressed, keypos_t key) {
-#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
-    if (disable_action_cache) {
-        return layer_switch_get_action(key);
-    }
-
-    uint8_t layer;
-
-    if (pressed) {
-        layer = layer_switch_get_layer(key);
-        update_source_layers_cache(key, layer);
-    } else {
-        layer = read_source_layers_cache(key);
-    }
-    return action_for_key(layer, key);
-#else
-    return layer_switch_get_action(key);
-#endif
-}
-
-/** \brief Layer switch get layer
- *
- * Gets the layer based on key info
- */
-uint8_t layer_switch_get_layer(keypos_t key) {
-#ifndef NO_ACTION_LAYER
-    action_t action;
-    action.code = ACTION_TRANSPARENT;
-
-    layer_state_t layers = layer_state | default_layer_state;
-    /* check top layer first */
-    for (int8_t i = MAX_LAYER - 1; i >= 0; i--) {
-        if (layers & (1UL << i)) {
-            action = action_for_key(i, key);
-            if (action.code != ACTION_TRANSPARENT) {
-                return i;
-            }
-        }
-    }
-    /* fall back to layer 0 */
-    return 0;
-#else
-    return get_highest_layer(default_layer_state);
-#endif
-}
-
-/** \brief Layer switch get layer
- *
- * Gets action code based on key position
- */
-action_t layer_switch_get_action(keypos_t key) { return action_for_key(layer_switch_get_layer(key), key); }
diff --git a/tmk_core/common/action_layer.h b/tmk_core/common/action_layer.h
deleted file mode 100644
index d72cd3e3a5..0000000000
--- a/tmk_core/common/action_layer.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
-Copyright 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-#include <stdint.h>
-#include "keyboard.h"
-#include "action.h"
-
-#if defined(LAYER_STATE_8BIT)
-typedef uint8_t layer_state_t;
-#    define MAX_LAYER_BITS 3
-#    ifndef MAX_LAYER
-#        define MAX_LAYER 8
-#    endif
-#    define get_highest_layer(state) biton(state)
-#elif defined(LAYER_STATE_16BIT)
-typedef uint16_t layer_state_t;
-#    define MAX_LAYER_BITS 4
-#    ifndef MAX_LAYER
-#        define MAX_LAYER 16
-#    endif
-#    define get_highest_layer(state) biton16(state)
-#else
-typedef uint32_t layer_state_t;
-#    define MAX_LAYER_BITS 5
-#    ifndef MAX_LAYER
-#        define MAX_LAYER 32
-#    endif
-#    define get_highest_layer(state) biton32(state)
-#endif
-
-/*
- * Default Layer
- */
-extern layer_state_t default_layer_state;
-void                 default_layer_debug(void);
-void                 default_layer_set(layer_state_t state);
-
-__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state);
-__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state);
-
-#ifndef NO_ACTION_LAYER
-/* bitwise operation */
-void default_layer_or(layer_state_t state);
-void default_layer_and(layer_state_t state);
-void default_layer_xor(layer_state_t state);
-#else
-#    define default_layer_or(state)
-#    define default_layer_and(state)
-#    define default_layer_xor(state)
-#endif
-
-/*
- * Keymap Layer
- */
-#ifndef NO_ACTION_LAYER
-extern layer_state_t layer_state;
-
-void layer_state_set(layer_state_t state);
-bool layer_state_is(uint8_t layer);
-bool layer_state_cmp(layer_state_t layer1, uint8_t layer2);
-
-void layer_debug(void);
-void layer_clear(void);
-void layer_move(uint8_t layer);
-void layer_on(uint8_t layer);
-void layer_off(uint8_t layer);
-void layer_invert(uint8_t layer);
-/* bitwise operation */
-void          layer_or(layer_state_t state);
-void          layer_and(layer_state_t state);
-void          layer_xor(layer_state_t state);
-layer_state_t layer_state_set_user(layer_state_t state);
-layer_state_t layer_state_set_kb(layer_state_t state);
-#else
-#    define layer_state 0
-
-#    define layer_state_set(layer)
-#    define layer_state_is(layer) (layer == 0)
-#    define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & 1UL << layer) != 0)
-
-#    define layer_debug()
-#    define layer_clear()
-#    define layer_move(layer) (void)layer
-#    define layer_on(layer) (void)layer
-#    define layer_off(layer) (void)layer
-#    define layer_invert(layer) (void)layer
-#    define layer_or(state) (void)state
-#    define layer_and(state) (void)state
-#    define layer_xor(state) (void)state
-#    define layer_state_set_kb(state) (void)state
-#    define layer_state_set_user(state) (void)state
-#endif
-
-/* pressed actions cache */
-#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
-
-void    update_source_layers_cache(keypos_t key, uint8_t layer);
-uint8_t read_source_layers_cache(keypos_t key);
-#endif
-action_t store_or_get_action(bool pressed, keypos_t key);
-
-/* return the topmost non-transparent layer currently associated with key */
-uint8_t layer_switch_get_layer(keypos_t key);
-
-/* return action depending on current layer status */
-action_t layer_switch_get_action(keypos_t key);
diff --git a/tmk_core/common/action_macro.c b/tmk_core/common/action_macro.c
deleted file mode 100644
index 92228c0ba8..0000000000
--- a/tmk_core/common/action_macro.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
-Copyright 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-#include "action.h"
-#include "action_util.h"
-#include "action_macro.h"
-#include "wait.h"
-
-#ifdef DEBUG_ACTION
-#    include "debug.h"
-#else
-#    include "nodebug.h"
-#endif
-
-#ifndef NO_ACTION_MACRO
-
-#    define MACRO_READ() (macro = MACRO_GET(macro_p++))
-/** \brief Action Macro Play
- *
- * FIXME: Needs doc
- */
-void action_macro_play(const macro_t *macro_p) {
-    macro_t macro    = END;
-    uint8_t interval = 0;
-
-    if (!macro_p) return;
-    while (true) {
-        switch (MACRO_READ()) {
-            case KEY_DOWN:
-                MACRO_READ();
-                dprintf("KEY_DOWN(%02X)\n", macro);
-                if (IS_MOD(macro)) {
-                    add_macro_mods(MOD_BIT(macro));
-                    send_keyboard_report();
-                } else {
-                    register_code(macro);
-                }
-                break;
-            case KEY_UP:
-                MACRO_READ();
-                dprintf("KEY_UP(%02X)\n", macro);
-                if (IS_MOD(macro)) {
-                    del_macro_mods(MOD_BIT(macro));
-                    send_keyboard_report();
-                } else {
-                    unregister_code(macro);
-                }
-                break;
-            case WAIT:
-                MACRO_READ();
-                dprintf("WAIT(%u)\n", macro);
-                {
-                    uint8_t ms = macro;
-                    while (ms--) wait_ms(1);
-                }
-                break;
-            case INTERVAL:
-                interval = MACRO_READ();
-                dprintf("INTERVAL(%u)\n", interval);
-                break;
-            case 0x04 ... 0x73:
-                dprintf("DOWN(%02X)\n", macro);
-                register_code(macro);
-                break;
-            case 0x84 ... 0xF3:
-                dprintf("UP(%02X)\n", macro);
-                unregister_code(macro & 0x7F);
-                break;
-            case END:
-            default:
-                return;
-        }
-        // interval
-        {
-            uint8_t ms = interval;
-            while (ms--) wait_ms(1);
-        }
-    }
-}
-#endif
diff --git a/tmk_core/common/action_macro.h b/tmk_core/common/action_macro.h
deleted file mode 100644
index 685e2c6ffc..0000000000
--- a/tmk_core/common/action_macro.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
-Copyright 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-#include <stdint.h>
-#include "progmem.h"
-
-typedef uint8_t macro_t;
-
-#define MACRO_NONE (macro_t *)0
-#define MACRO(...)                                          \
-    ({                                                      \
-        static const macro_t __m[] PROGMEM = {__VA_ARGS__}; \
-        &__m[0];                                            \
-    })
-#define MACRO_GET(p) pgm_read_byte(p)
-
-// Sends press when the macro key is pressed, release when release, or tap_macro when the key has been tapped
-#define MACRO_TAP_HOLD(record, press, release, tap_macro) (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? (press) : MACRO_NONE) : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (tap_macro) : (release)))
-
-// Holds down the modifier mod when the macro key is held, or sends macro instead when tapped
-#define MACRO_TAP_HOLD_MOD(record, macro, mod) MACRO_TAP_HOLD(record, (MACRO(D(mod), END)), MACRO(U(mod), END), macro)
-
-// Holds down the modifier mod when the macro key is held, or pressed a shifted key when tapped (eg: shift+3 for #)
-#define MACRO_TAP_SHFT_KEY_HOLD_MOD(record, key, mod) MACRO_TAP_HOLD_MOD(record, (MACRO(I(10), D(LSFT), T(key), U(LSFT), END)), mod)
-
-// Momentary switch layer when held, sends macro if tapped
-#define MACRO_TAP_HOLD_LAYER(record, macro, layer)                                                         \
-    (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? ({             \
-        layer_on((layer));                                                                                 \
-        MACRO_NONE;                                                                                        \
-    })                                                                                                     \
-                                                                                          : MACRO_NONE)    \
-                               : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (macro) : ({ \
-                                     layer_off((layer));                                                   \
-                                     MACRO_NONE;                                                           \
-                                 })))
-
-// Momentary switch layer when held, presses a shifted key when tapped (eg: shift+3 for #)
-#define MACRO_TAP_SHFT_KEY_HOLD_LAYER(record, key, layer) MACRO_TAP_HOLD_LAYER(record, MACRO(I(10), D(LSFT), T(key), U(LSFT), END), layer)
-
-#ifndef NO_ACTION_MACRO
-void action_macro_play(const macro_t *macro_p);
-#else
-#    define action_macro_play(macro)
-#endif
-
-/* Macro commands
- *   code(0x04-73)                      // key down(1byte)
- *   code(0x04-73) | 0x80               // key up(1byte)
- *   { KEY_DOWN, code(0x04-0xff) }      // key down(2bytes)
- *   { KEY_UP,   code(0x04-0xff) }      // key up(2bytes)
- *   WAIT                               // wait milli-seconds
- *   INTERVAL                           // set interval between macro commands
- *   END                                // stop macro execution
- *
- * Ideas(Not implemented):
- *   modifiers
- *   system usage
- *   consumer usage
- *   unicode usage
- *   function call
- *   conditionals
- *   loop
- */
-enum macro_command_id {
-    /* 0x00 - 0x03 */
-    END = 0x00,
-    KEY_DOWN,
-    KEY_UP,
-
-    /* 0x04 - 0x73 (reserved for keycode down) */
-
-    /* 0x74 - 0x83 */
-    WAIT = 0x74,
-    INTERVAL,
-
-    /* 0x84 - 0xf3 (reserved for keycode up) */
-
-    /* 0xf4 - 0xff */
-};
-
-/* TODO: keycode:0x04-0x73 can be handled by 1byte command  else 2bytes are needed
- * if keycode between 0x04 and 0x73
- *      keycode / (keycode|0x80)
- * else
- *      {KEY_DOWN, keycode} / {KEY_UP, keycode}
- */
-#define DOWN(key) KEY_DOWN, (key)
-#define UP(key) KEY_UP, (key)
-#define TYPE(key) DOWN(key), UP(key)
-#define WAIT(ms) WAIT, (ms)
-#define INTERVAL(ms) INTERVAL, (ms)
-
-/* key down */
-#define D(key) DOWN(KC_##key)
-/* key up */
-#define U(key) UP(KC_##key)
-/* key type */
-#define T(key) TYPE(KC_##key)
-/* wait */
-#define W(ms) WAIT(ms)
-/* interval */
-#define I(ms) INTERVAL(ms)
-
-/* for backward comaptibility */
-#define MD(key) DOWN(KC_##key)
-#define MU(key) UP(KC_##key)
diff --git a/tmk_core/common/action_tapping.c b/tmk_core/common/action_tapping.c
deleted file mode 100644
index 56044e096d..0000000000
--- a/tmk_core/common/action_tapping.c
+++ /dev/null
@@ -1,426 +0,0 @@
-#include <stdint.h>
-#include <stdbool.h>
-#include "action.h"
-#include "action_layer.h"
-#include "action_tapping.h"
-#include "keycode.h"
-#include "timer.h"
-
-#ifdef DEBUG_ACTION
-#    include "debug.h"
-#else
-#    include "nodebug.h"
-#endif
-
-#ifndef NO_ACTION_TAPPING
-
-#    define IS_TAPPING() !IS_NOEVENT(tapping_key.event)
-#    define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
-#    define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
-#    define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
-
-__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; }
-
-#    ifdef TAPPING_TERM_PER_KEY
-#        define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_event_keycode(tapping_key.event, false), &tapping_key))
-#    else
-#        define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
-#    endif
-
-#    ifdef TAPPING_FORCE_HOLD_PER_KEY
-__attribute__((weak)) bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) { return false; }
-#    endif
-
-#    ifdef PERMISSIVE_HOLD_PER_KEY
-__attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { return false; }
-#    endif
-
-static keyrecord_t tapping_key                         = {};
-static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
-static uint8_t     waiting_buffer_head                 = 0;
-static uint8_t     waiting_buffer_tail                 = 0;
-
-static bool process_tapping(keyrecord_t *record);
-static bool waiting_buffer_enq(keyrecord_t record);
-static void waiting_buffer_clear(void);
-static bool waiting_buffer_typed(keyevent_t event);
-static bool waiting_buffer_has_anykey_pressed(void);
-static void waiting_buffer_scan_tap(void);
-static void debug_tapping_key(void);
-static void debug_waiting_buffer(void);
-
-/** \brief Action Tapping Process
- *
- * FIXME: Needs doc
- */
-void action_tapping_process(keyrecord_t record) {
-    if (process_tapping(&record)) {
-        if (!IS_NOEVENT(record.event)) {
-            debug("processed: ");
-            debug_record(record);
-            debug("\n");
-        }
-    } else {
-        if (!waiting_buffer_enq(record)) {
-            // clear all in case of overflow.
-            debug("OVERFLOW: CLEAR ALL STATES\n");
-            clear_keyboard();
-            waiting_buffer_clear();
-            tapping_key = (keyrecord_t){};
-        }
-    }
-
-    // process waiting_buffer
-    if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
-        debug("---- action_exec: process waiting_buffer -----\n");
-    }
-    for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
-        if (process_tapping(&waiting_buffer[waiting_buffer_tail])) {
-            debug("processed: waiting_buffer[");
-            debug_dec(waiting_buffer_tail);
-            debug("] = ");
-            debug_record(waiting_buffer[waiting_buffer_tail]);
-            debug("\n\n");
-        } else {
-            break;
-        }
-    }
-    if (!IS_NOEVENT(record.event)) {
-        debug("\n");
-    }
-}
-
-/** \brief Tapping
- *
- * Rule: Tap key is typed(pressed and released) within TAPPING_TERM.
- *       (without interfering by typing other key)
- */
-/* return true when key event is processed or consumed. */
-bool process_tapping(keyrecord_t *keyp) {
-    keyevent_t event = keyp->event;
-
-    // if tapping
-    if (IS_TAPPING_PRESSED()) {
-        if (WITHIN_TAPPING_TERM(event)) {
-            if (tapping_key.tap.count == 0) {
-                if (IS_TAPPING_KEY(event.key) && !event.pressed) {
-                    // first tap!
-                    debug("Tapping: First tap(0->1).\n");
-                    tapping_key.tap.count = 1;
-                    debug_tapping_key();
-                    process_record(&tapping_key);
-
-                    // copy tapping state
-                    keyp->tap = tapping_key.tap;
-                    // enqueue
-                    return false;
-                }
-                /* Process a key typed within TAPPING_TERM
-                 * This can register the key before settlement of tapping,
-                 * useful for long TAPPING_TERM but may prevent fast typing.
-                 */
-#    if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY)
-                else if (((
-#        ifdef TAPPING_TERM_PER_KEY
-                              get_tapping_term(get_event_keycode(tapping_key.event, false), keyp)
-#        else
-                              TAPPING_TERM
-#        endif
-                              >= 500)
-
-#        ifdef PERMISSIVE_HOLD_PER_KEY
-                          || get_permissive_hold(get_event_keycode(tapping_key.event, false), keyp)
-#        elif defined(PERMISSIVE_HOLD)
-                          || true
-#        endif
-                              ) &&
-                         IS_RELEASED(event) && waiting_buffer_typed(event)) {
-                    debug("Tapping: End. No tap. Interfered by typing key\n");
-                    process_record(&tapping_key);
-                    tapping_key = (keyrecord_t){};
-                    debug_tapping_key();
-                    // enqueue
-                    return false;
-                }
-#    endif
-                /* Process release event of a key pressed before tapping starts
-                 * Without this unexpected repeating will occur with having fast repeating setting
-                 * https://github.com/tmk/tmk_keyboard/issues/60
-                 */
-                else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) {
-                    // Modifier should be retained till end of this tapping.
-                    action_t action = layer_switch_get_action(event.key);
-                    switch (action.kind.id) {
-                        case ACT_LMODS:
-                        case ACT_RMODS:
-                            if (action.key.mods && !action.key.code) return false;
-                            if (IS_MOD(action.key.code)) return false;
-                            break;
-                        case ACT_LMODS_TAP:
-                        case ACT_RMODS_TAP:
-                            if (action.key.mods && keyp->tap.count == 0) return false;
-                            if (IS_MOD(action.key.code)) return false;
-                            break;
-                    }
-                    // Release of key should be process immediately.
-                    debug("Tapping: release event of a key pressed before tapping\n");
-                    process_record(keyp);
-                    return true;
-                } else {
-                    // set interrupted flag when other key preesed during tapping
-                    if (event.pressed) {
-                        tapping_key.tap.interrupted = true;
-                    }
-                    // enqueue
-                    return false;
-                }
-            }
-            // tap_count > 0
-            else {
-                if (IS_TAPPING_KEY(event.key) && !event.pressed) {
-                    debug("Tapping: Tap release(");
-                    debug_dec(tapping_key.tap.count);
-                    debug(")\n");
-                    keyp->tap = tapping_key.tap;
-                    process_record(keyp);
-                    tapping_key = *keyp;
-                    debug_tapping_key();
-                    return true;
-                } else if (is_tap_key(event.key) && event.pressed) {
-                    if (tapping_key.tap.count > 1) {
-                        debug("Tapping: Start new tap with releasing last tap(>1).\n");
-                        // unregister key
-                        process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false});
-                    } else {
-                        debug("Tapping: Start while last tap(1).\n");
-                    }
-                    tapping_key = *keyp;
-                    waiting_buffer_scan_tap();
-                    debug_tapping_key();
-                    return true;
-                } else {
-                    if (!IS_NOEVENT(event)) {
-                        debug("Tapping: key event while last tap(>0).\n");
-                    }
-                    process_record(keyp);
-                    return true;
-                }
-            }
-        }
-        // after TAPPING_TERM
-        else {
-            if (tapping_key.tap.count == 0) {
-                debug("Tapping: End. Timeout. Not tap(0): ");
-                debug_event(event);
-                debug("\n");
-                process_record(&tapping_key);
-                tapping_key = (keyrecord_t){};
-                debug_tapping_key();
-                return false;
-            } else {
-                if (IS_TAPPING_KEY(event.key) && !event.pressed) {
-                    debug("Tapping: End. last timeout tap release(>0).");
-                    keyp->tap = tapping_key.tap;
-                    process_record(keyp);
-                    tapping_key = (keyrecord_t){};
-                    return true;
-                } else if (is_tap_key(event.key) && event.pressed) {
-                    if (tapping_key.tap.count > 1) {
-                        debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
-                        // unregister key
-                        process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false});
-                    } else {
-                        debug("Tapping: Start while last timeout tap(1).\n");
-                    }
-                    tapping_key = *keyp;
-                    waiting_buffer_scan_tap();
-                    debug_tapping_key();
-                    return true;
-                } else {
-                    if (!IS_NOEVENT(event)) {
-                        debug("Tapping: key event while last timeout tap(>0).\n");
-                    }
-                    process_record(keyp);
-                    return true;
-                }
-            }
-        }
-    } else if (IS_TAPPING_RELEASED()) {
-        if (WITHIN_TAPPING_TERM(event)) {
-            if (event.pressed) {
-                if (IS_TAPPING_KEY(event.key)) {
-//#    ifndef TAPPING_FORCE_HOLD
-#    if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
-                    if (
-#        ifdef TAPPING_FORCE_HOLD_PER_KEY
-                        !get_tapping_force_hold(get_event_keycode(tapping_key.event, false), keyp) &&
-#        endif
-                        !tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
-                        // sequential tap.
-                        keyp->tap = tapping_key.tap;
-                        if (keyp->tap.count < 15) keyp->tap.count += 1;
-                        debug("Tapping: Tap press(");
-                        debug_dec(keyp->tap.count);
-                        debug(")\n");
-                        process_record(keyp);
-                        tapping_key = *keyp;
-                        debug_tapping_key();
-                        return true;
-                    }
-#    endif
-                    // FIX: start new tap again
-                    tapping_key = *keyp;
-                    return true;
-                } else if (is_tap_key(event.key)) {
-                    // Sequential tap can be interfered with other tap key.
-                    debug("Tapping: Start with interfering other tap.\n");
-                    tapping_key = *keyp;
-                    waiting_buffer_scan_tap();
-                    debug_tapping_key();
-                    return true;
-                } else {
-                    // should none in buffer
-                    // FIX: interrupted when other key is pressed
-                    tapping_key.tap.interrupted = true;
-                    process_record(keyp);
-                    return true;
-                }
-            } else {
-                if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n");
-                process_record(keyp);
-                return true;
-            }
-        } else {
-            // FIX: process_action here?
-            // timeout. no sequential tap.
-            debug("Tapping: End(Timeout after releasing last tap): ");
-            debug_event(event);
-            debug("\n");
-            tapping_key = (keyrecord_t){};
-            debug_tapping_key();
-            return false;
-        }
-    }
-    // not tapping state
-    else {
-        if (event.pressed && is_tap_key(event.key)) {
-            debug("Tapping: Start(Press tap key).\n");
-            tapping_key = *keyp;
-            process_record_tap_hint(&tapping_key);
-            waiting_buffer_scan_tap();
-            debug_tapping_key();
-            return true;
-        } else {
-            process_record(keyp);
-            return true;
-        }
-    }
-}
-
-/** \brief Waiting buffer enq
- *
- * FIXME: Needs docs
- */
-bool waiting_buffer_enq(keyrecord_t record) {
-    if (IS_NOEVENT(record.event)) {
-        return true;
-    }
-
-    if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {
-        debug("waiting_buffer_enq: Over flow.\n");
-        return false;
-    }
-
-    waiting_buffer[waiting_buffer_head] = record;
-    waiting_buffer_head                 = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;
-
-    debug("waiting_buffer_enq: ");
-    debug_waiting_buffer();
-    return true;
-}
-
-/** \brief Waiting buffer clear
- *
- * FIXME: Needs docs
- */
-void waiting_buffer_clear(void) {
-    waiting_buffer_head = 0;
-    waiting_buffer_tail = 0;
-}
-
-/** \brief Waiting buffer typed
- *
- * FIXME: Needs docs
- */
-bool waiting_buffer_typed(keyevent_t event) {
-    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
-        if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {
-            return true;
-        }
-    }
-    return false;
-}
-
-/** \brief Waiting buffer has anykey pressed
- *
- * FIXME: Needs docs
- */
-__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) {
-    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
-        if (waiting_buffer[i].event.pressed) return true;
-    }
-    return false;
-}
-
-/** \brief Scan buffer for tapping
- *
- * FIXME: Needs docs
- */
-void waiting_buffer_scan_tap(void) {
-    // tapping already is settled
-    if (tapping_key.tap.count > 0) return;
-    // invalid state: tapping_key released && tap.count == 0
-    if (!tapping_key.event.pressed) return;
-
-    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
-        if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) {
-            tapping_key.tap.count       = 1;
-            waiting_buffer[i].tap.count = 1;
-            process_record(&tapping_key);
-
-            debug("waiting_buffer_scan_tap: found at [");
-            debug_dec(i);
-            debug("]\n");
-            debug_waiting_buffer();
-            return;
-        }
-    }
-}
-
-/** \brief Tapping key debug print
- *
- * FIXME: Needs docs
- */
-static void debug_tapping_key(void) {
-    debug("TAPPING_KEY=");
-    debug_record(tapping_key);
-    debug("\n");
-}
-
-/** \brief Waiting buffer debug print
- *
- * FIXME: Needs docs
- */
-static void debug_waiting_buffer(void) {
-    debug("{ ");
-    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
-        debug("[");
-        debug_dec(i);
-        debug("]=");
-        debug_record(waiting_buffer[i]);
-        debug(" ");
-    }
-    debug("}\n");
-}
-
-#endif
diff --git a/tmk_core/common/action_tapping.h b/tmk_core/common/action_tapping.h
deleted file mode 100644
index 893ccb1ce1..0000000000
--- a/tmk_core/common/action_tapping.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-Copyright 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-/* period of tapping(ms) */
-#ifndef TAPPING_TERM
-#    define TAPPING_TERM 200
-#endif
-
-/* tap count needed for toggling a feature */
-#ifndef TAPPING_TOGGLE
-#    define TAPPING_TOGGLE 5
-#endif
-
-#define WAITING_BUFFER_SIZE 8
-
-#ifndef NO_ACTION_TAPPING
-uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
-void     action_tapping_process(keyrecord_t record);
-
-uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record);
-bool     get_permissive_hold(uint16_t keycode, keyrecord_t *record);
-bool     get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record);
-bool     get_tapping_force_hold(uint16_t keycode, keyrecord_t *record);
-bool     get_retro_tapping(uint16_t keycode, keyrecord_t *record);
-#endif
diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c
deleted file mode 100644
index a57c8bf66a..0000000000
--- a/tmk_core/common/action_util.c
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
-Copyright 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-#include "host.h"
-#include "report.h"
-#include "debug.h"
-#include "action_util.h"
-#include "action_layer.h"
-#include "timer.h"
-#include "keycode_config.h"
-
-extern keymap_config_t keymap_config;
-
-static uint8_t real_mods  = 0;
-static uint8_t weak_mods  = 0;
-static uint8_t macro_mods = 0;
-
-#ifdef USB_6KRO_ENABLE
-#    define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
-#    define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
-#    define RO_INC(a) RO_ADD(a, 1)
-#    define RO_DEC(a) RO_SUB(a, 1)
-static int8_t cb_head  = 0;
-static int8_t cb_tail  = 0;
-static int8_t cb_count = 0;
-#endif
-
-// TODO: pointer variable is not needed
-// report_keyboard_t keyboard_report = {};
-report_keyboard_t *keyboard_report = &(report_keyboard_t){};
-
-extern inline void add_key(uint8_t key);
-extern inline void del_key(uint8_t key);
-extern inline void clear_keys(void);
-
-#ifndef NO_ACTION_ONESHOT
-static uint8_t oneshot_mods        = 0;
-static uint8_t oneshot_locked_mods = 0;
-uint8_t        get_oneshot_locked_mods(void) { return oneshot_locked_mods; }
-void           set_oneshot_locked_mods(uint8_t mods) {
-    if (mods != oneshot_locked_mods) {
-        oneshot_locked_mods = mods;
-        oneshot_locked_mods_changed_kb(oneshot_locked_mods);
-    }
-}
-void clear_oneshot_locked_mods(void) {
-    if (oneshot_locked_mods) {
-        oneshot_locked_mods = 0;
-        oneshot_locked_mods_changed_kb(oneshot_locked_mods);
-    }
-}
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-static uint16_t oneshot_time = 0;
-bool            has_oneshot_mods_timed_out(void) { return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT; }
-#    else
-bool has_oneshot_mods_timed_out(void) { return false; }
-#    endif
-#endif
-
-/* oneshot layer */
-#ifndef NO_ACTION_ONESHOT
-/** \brief oneshot_layer_data bits
- * LLLL LSSS
- * where:
- *   L => are layer bits
- *   S => oneshot state bits
- */
-static int8_t oneshot_layer_data = 0;
-
-inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; }
-inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; }
-
-#    ifdef SWAP_HANDS_ENABLE
-enum {
-    SHO_OFF,
-    SHO_ACTIVE,   // Swap hands button was pressed, and we didn't send any swapped keys yet
-    SHO_PRESSED,  // Swap hands button is currently pressed
-    SHO_USED,     // Swap hands button is still pressed, and we already sent swapped keys
-} swap_hands_oneshot = SHO_OFF;
-#    endif
-
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-static uint16_t oneshot_layer_time = 0;
-inline bool     has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); }
-#        ifdef SWAP_HANDS_ENABLE
-static uint16_t oneshot_swaphands_time = 0;
-inline bool     has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && (swap_hands_oneshot == SHO_ACTIVE); }
-#        endif
-#    endif
-
-#    ifdef SWAP_HANDS_ENABLE
-
-void set_oneshot_swaphands(void) {
-    swap_hands_oneshot = SHO_PRESSED;
-    swap_hands         = true;
-#        if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-    oneshot_swaphands_time = timer_read();
-    if (oneshot_layer_time != 0) {
-        oneshot_layer_time = oneshot_swaphands_time;
-    }
-#        endif
-}
-
-void release_oneshot_swaphands(void) {
-    if (swap_hands_oneshot == SHO_PRESSED) {
-        swap_hands_oneshot = SHO_ACTIVE;
-    }
-    if (swap_hands_oneshot == SHO_USED) {
-        clear_oneshot_swaphands();
-    }
-}
-
-void use_oneshot_swaphands(void) {
-    if (swap_hands_oneshot == SHO_PRESSED) {
-        swap_hands_oneshot = SHO_USED;
-    }
-    if (swap_hands_oneshot == SHO_ACTIVE) {
-        clear_oneshot_swaphands();
-    }
-}
-
-void clear_oneshot_swaphands(void) {
-    swap_hands_oneshot = SHO_OFF;
-    swap_hands         = false;
-#        if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-    oneshot_swaphands_time = 0;
-#        endif
-}
-
-#    endif
-
-/** \brief Set oneshot layer
- *
- * FIXME: needs doc
- */
-void set_oneshot_layer(uint8_t layer, uint8_t state) {
-    if (!keymap_config.oneshot_disable) {
-        oneshot_layer_data = layer << 3 | state;
-        layer_on(layer);
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-        oneshot_layer_time = timer_read();
-#    endif
-        oneshot_layer_changed_kb(get_oneshot_layer());
-    } else {
-        layer_on(layer);
-    }
-}
-/** \brief Reset oneshot layer
- *
- * FIXME: needs doc
- */
-void reset_oneshot_layer(void) {
-    oneshot_layer_data = 0;
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-    oneshot_layer_time = 0;
-#    endif
-    oneshot_layer_changed_kb(get_oneshot_layer());
-}
-/** \brief Clear oneshot layer
- *
- * FIXME: needs doc
- */
-void clear_oneshot_layer_state(oneshot_fullfillment_t state) {
-    uint8_t start_state = oneshot_layer_data;
-    oneshot_layer_data &= ~state;
-    if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) {
-        layer_off(get_oneshot_layer());
-        reset_oneshot_layer();
-    }
-}
-/** \brief Is oneshot layer active
- *
- * FIXME: needs doc
- */
-bool is_oneshot_layer_active(void) { return get_oneshot_layer_state(); }
-
-/** \brief set oneshot
- *
- * FIXME: needs doc
- */
-void oneshot_set(bool active) {
-    if (keymap_config.oneshot_disable != active) {
-        keymap_config.oneshot_disable = active;
-        eeconfig_update_keymap(keymap_config.raw);
-        dprintf("Oneshot: active: %d\n", active);
-    }
-}
-
-/** \brief toggle oneshot
- *
- * FIXME: needs doc
- */
-void oneshot_toggle(void) { oneshot_set(!keymap_config.oneshot_disable); }
-
-/** \brief enable oneshot
- *
- * FIXME: needs doc
- */
-void oneshot_enable(void) { oneshot_set(true); }
-
-/** \brief disable oneshot
- *
- * FIXME: needs doc
- */
-void oneshot_disable(void) { oneshot_set(false); }
-
-bool is_oneshot_enabled(void) { return keymap_config.oneshot_disable; }
-
-#endif
-
-/** \brief Send keyboard report
- *
- * FIXME: needs doc
- */
-void send_keyboard_report(void) {
-    keyboard_report->mods = real_mods;
-    keyboard_report->mods |= weak_mods;
-    keyboard_report->mods |= macro_mods;
-#ifndef NO_ACTION_ONESHOT
-    if (oneshot_mods) {
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-        if (has_oneshot_mods_timed_out()) {
-            dprintf("Oneshot: timeout\n");
-            clear_oneshot_mods();
-        }
-#    endif
-        keyboard_report->mods |= oneshot_mods;
-        if (has_anykey(keyboard_report)) {
-            clear_oneshot_mods();
-        }
-    }
-
-#endif
-    host_keyboard_send(keyboard_report);
-}
-
-/** \brief Get mods
- *
- * FIXME: needs doc
- */
-uint8_t get_mods(void) { return real_mods; }
-/** \brief add mods
- *
- * FIXME: needs doc
- */
-void add_mods(uint8_t mods) { real_mods |= mods; }
-/** \brief del mods
- *
- * FIXME: needs doc
- */
-void del_mods(uint8_t mods) { real_mods &= ~mods; }
-/** \brief set mods
- *
- * FIXME: needs doc
- */
-void set_mods(uint8_t mods) { real_mods = mods; }
-/** \brief clear mods
- *
- * FIXME: needs doc
- */
-void clear_mods(void) { real_mods = 0; }
-
-/** \brief get weak mods
- *
- * FIXME: needs doc
- */
-uint8_t get_weak_mods(void) { return weak_mods; }
-/** \brief add weak mods
- *
- * FIXME: needs doc
- */
-void add_weak_mods(uint8_t mods) { weak_mods |= mods; }
-/** \brief del weak mods
- *
- * FIXME: needs doc
- */
-void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; }
-/** \brief set weak mods
- *
- * FIXME: needs doc
- */
-void set_weak_mods(uint8_t mods) { weak_mods = mods; }
-/** \brief clear weak mods
- *
- * FIXME: needs doc
- */
-void clear_weak_mods(void) { weak_mods = 0; }
-
-/* macro modifier */
-/** \brief get macro mods
- *
- * FIXME: needs doc
- */
-uint8_t get_macro_mods(void) { return macro_mods; }
-/** \brief add macro mods
- *
- * FIXME: needs doc
- */
-void add_macro_mods(uint8_t mods) { macro_mods |= mods; }
-/** \brief del macro mods
- *
- * FIXME: needs doc
- */
-void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; }
-/** \brief set macro mods
- *
- * FIXME: needs doc
- */
-void set_macro_mods(uint8_t mods) { macro_mods = mods; }
-/** \brief clear macro mods
- *
- * FIXME: needs doc
- */
-void clear_macro_mods(void) { macro_mods = 0; }
-
-#ifndef NO_ACTION_ONESHOT
-/** \brief get oneshot mods
- *
- * FIXME: needs doc
- */
-uint8_t get_oneshot_mods(void) { return oneshot_mods; }
-
-void add_oneshot_mods(uint8_t mods) {
-    if ((oneshot_mods & mods) != mods) {
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-        oneshot_time = timer_read();
-#    endif
-        oneshot_mods |= mods;
-        oneshot_mods_changed_kb(mods);
-    }
-}
-
-void del_oneshot_mods(uint8_t mods) {
-    if (oneshot_mods & mods) {
-        oneshot_mods &= ~mods;
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-        oneshot_time = oneshot_mods ? timer_read() : 0;
-#    endif
-        oneshot_mods_changed_kb(oneshot_mods);
-    }
-}
-
-/** \brief set oneshot mods
- *
- * FIXME: needs doc
- */
-void set_oneshot_mods(uint8_t mods) {
-    if (!keymap_config.oneshot_disable) {
-        if (oneshot_mods != mods) {
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-            oneshot_time = timer_read();
-#    endif
-            oneshot_mods = mods;
-            oneshot_mods_changed_kb(mods);
-        }
-    }
-}
-
-/** \brief clear oneshot mods
- *
- * FIXME: needs doc
- */
-void clear_oneshot_mods(void) {
-    if (oneshot_mods) {
-        oneshot_mods = 0;
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-        oneshot_time = 0;
-#    endif
-        oneshot_mods_changed_kb(oneshot_mods);
-    }
-}
-#endif
-
-/** \brief Called when the one shot modifiers have been changed.
- *
- * \param mods Contains the active modifiers active after the change.
- */
-__attribute__((weak)) void oneshot_locked_mods_changed_user(uint8_t mods) {}
-
-/** \brief Called when the locked one shot modifiers have been changed.
- *
- * \param mods Contains the active modifiers active after the change.
- */
-__attribute__((weak)) void oneshot_locked_mods_changed_kb(uint8_t mods) { oneshot_locked_mods_changed_user(mods); }
-
-/** \brief Called when the one shot modifiers have been changed.
- *
- * \param mods Contains the active modifiers active after the change.
- */
-__attribute__((weak)) void oneshot_mods_changed_user(uint8_t mods) {}
-
-/** \brief Called when the one shot modifiers have been changed.
- *
- * \param mods Contains the active modifiers active after the change.
- */
-__attribute__((weak)) void oneshot_mods_changed_kb(uint8_t mods) { oneshot_mods_changed_user(mods); }
-
-/** \brief Called when the one shot layers have been changed.
- *
- * \param layer Contains the layer that is toggled on, or zero when toggled off.
- */
-__attribute__((weak)) void oneshot_layer_changed_user(uint8_t layer) {}
-
-/** \brief Called when the one shot layers have been changed.
- *
- * \param layer Contains the layer that is toggled on, or zero when toggled off.
- */
-__attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) { oneshot_layer_changed_user(layer); }
-
-/** \brief inspect keyboard state
- *
- * FIXME: needs doc
- */
-uint8_t has_anymod(void) { return bitpop(real_mods); }
diff --git a/tmk_core/common/action_util.h b/tmk_core/common/action_util.h
deleted file mode 100644
index f2b3897ae5..0000000000
--- a/tmk_core/common/action_util.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
-Copyright 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-#include <stdint.h>
-#include "report.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern report_keyboard_t *keyboard_report;
-
-void send_keyboard_report(void);
-
-/* key */
-inline void add_key(uint8_t key) { add_key_to_report(keyboard_report, key); }
-
-inline void del_key(uint8_t key) { del_key_from_report(keyboard_report, key); }
-
-inline void clear_keys(void) { clear_keys_from_report(keyboard_report); }
-
-/* modifier */
-uint8_t get_mods(void);
-void    add_mods(uint8_t mods);
-void    del_mods(uint8_t mods);
-void    set_mods(uint8_t mods);
-void    clear_mods(void);
-
-/* weak modifier */
-uint8_t get_weak_mods(void);
-void    add_weak_mods(uint8_t mods);
-void    del_weak_mods(uint8_t mods);
-void    set_weak_mods(uint8_t mods);
-void    clear_weak_mods(void);
-
-/* macro modifier */
-uint8_t get_macro_mods(void);
-void    add_macro_mods(uint8_t mods);
-void    del_macro_mods(uint8_t mods);
-void    set_macro_mods(uint8_t mods);
-void    clear_macro_mods(void);
-
-/* oneshot modifier */
-uint8_t get_oneshot_mods(void);
-void    add_oneshot_mods(uint8_t mods);
-void    del_oneshot_mods(uint8_t mods);
-void    set_oneshot_mods(uint8_t mods);
-void    clear_oneshot_mods(void);
-bool    has_oneshot_mods_timed_out(void);
-
-uint8_t get_oneshot_locked_mods(void);
-void    set_oneshot_locked_mods(uint8_t mods);
-void    clear_oneshot_locked_mods(void);
-
-typedef enum { ONESHOT_PRESSED = 0b01, ONESHOT_OTHER_KEY_PRESSED = 0b10, ONESHOT_START = 0b11, ONESHOT_TOGGLED = 0b100 } oneshot_fullfillment_t;
-void    set_oneshot_layer(uint8_t layer, uint8_t state);
-uint8_t get_oneshot_layer(void);
-void    clear_oneshot_layer_state(oneshot_fullfillment_t state);
-void    reset_oneshot_layer(void);
-bool    is_oneshot_layer_active(void);
-uint8_t get_oneshot_layer_state(void);
-bool    has_oneshot_layer_timed_out(void);
-bool    has_oneshot_swaphands_timed_out(void);
-
-void oneshot_locked_mods_changed_user(uint8_t mods);
-void oneshot_locked_mods_changed_kb(uint8_t mods);
-void oneshot_mods_changed_user(uint8_t mods);
-void oneshot_mods_changed_kb(uint8_t mods);
-void oneshot_layer_changed_user(uint8_t layer);
-void oneshot_layer_changed_kb(uint8_t layer);
-
-void oneshot_toggle(void);
-void oneshot_enable(void);
-void oneshot_disable(void);
-bool is_oneshot_enabled(void);
-
-/* inspect */
-uint8_t has_anymod(void);
-
-#ifdef SWAP_HANDS_ENABLE
-void set_oneshot_swaphands(void);
-void release_oneshot_swaphands(void);
-void use_oneshot_swaphands(void);
-void clear_oneshot_swaphands(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/tmk_core/common/arm_atsam/_timer.h b/tmk_core/common/arm_atsam/_timer.h
new file mode 100644
index 0000000000..77402b612a
--- /dev/null
+++ b/tmk_core/common/arm_atsam/_timer.h
@@ -0,0 +1,19 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+// The platform is 32-bit, so prefer 32-bit timers to avoid overflow
+#define FAST_TIMER_T_SIZE 32
diff --git a/tmk_core/common/arm_atsam/platform.c b/tmk_core/common/arm_atsam/platform.c
new file mode 100644
index 0000000000..3e35b4fe4c
--- /dev/null
+++ b/tmk_core/common/arm_atsam/platform.c
@@ -0,0 +1,21 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "platform_deps.h"
+
+void platform_setup(void) {
+    // do nothing
+}
diff --git a/tmk_core/common/avr/_timer.h b/tmk_core/common/avr/_timer.h
new file mode 100644
index 0000000000..b81e0f68b7
--- /dev/null
+++ b/tmk_core/common/avr/_timer.h
@@ -0,0 +1,19 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+// The platform is 8-bit, so prefer 16-bit timers to reduce code size
+#define FAST_TIMER_T_SIZE 16
diff --git a/tmk_core/common/avr/_wait.h b/tmk_core/common/avr/_wait.h
index 56eb316faf..683db6ae57 100644
--- a/tmk_core/common/avr/_wait.h
+++ b/tmk_core/common/avr/_wait.h
@@ -17,8 +17,28 @@
 
 #include <util/delay.h>
 
-#define wait_ms(ms) _delay_ms(ms)
-#define wait_us(us) _delay_us(us)
+#define wait_ms(ms)                             \
+    do {                                        \
+        if (__builtin_constant_p(ms)) {         \
+            _delay_ms(ms);                      \
+        } else {                                \
+            for (uint16_t i = ms; i > 0; i--) { \
+                _delay_ms(1);                   \
+            }                                   \
+        }                                       \
+    } while (0)
+#define wait_us(us)                             \
+    do {                                        \
+        if (__builtin_constant_p(us)) {         \
+            _delay_us(us);                      \
+        } else {                                \
+            for (uint16_t i = us; i > 0; i--) { \
+                _delay_us(1);                   \
+            }                                   \
+        }                                       \
+    } while (0)
+#define wait_cpuclock(n) __builtin_avr_delay_cycles(n)
+#define CPU_CLOCK F_CPU
 
 /* The AVR series GPIOs have a one clock read delay for changes in the digital input signal.
  * But here's more margin to make it two clocks. */
@@ -26,4 +46,4 @@
 #    define GPIO_INPUT_PIN_DELAY 2
 #endif
 
-#define waitInputPinDelay() __builtin_avr_delay_cycles(GPIO_INPUT_PIN_DELAY)
+#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)
diff --git a/tmk_core/common/avr/gpio.h b/tmk_core/common/avr/gpio.h
index 231556c29c..e9be68491d 100644
--- a/tmk_core/common/avr/gpio.h
+++ b/tmk_core/common/avr/gpio.h
@@ -20,6 +20,8 @@
 
 typedef uint8_t pin_t;
 
+/* Operation of GPIO by pin. */
+
 #define setPinInput(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
 #define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
 #define setPinInputLow(pin) _Static_assert(0, "AVR processors cannot implement an input as pull low")
@@ -32,3 +34,16 @@ typedef uint8_t pin_t;
 #define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF)))
 
 #define togglePin(pin) (PORTx_ADDRESS(pin) ^= _BV((pin)&0xF))
+
+/* Operation of GPIO by port. */
+
+typedef uint8_t port_data_t;
+
+#define readPort(port) PINx_ADDRESS(port)
+
+#define setPortBitInput(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
+#define setPortBitInputHigh(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) |= _BV((bit)&0xF))
+#define setPortBitOutput(port, bit) (DDRx_ADDRESS(port) |= _BV((bit)&0xF))
+
+#define writePortBitLow(port, bit) (PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
+#define writePortBitHigh(port, bit) (PORTx_ADDRESS(port) |= _BV((bit)&0xF))
diff --git a/tmk_core/common/avr/platform.c b/tmk_core/common/avr/platform.c
new file mode 100644
index 0000000000..3e35b4fe4c
--- /dev/null
+++ b/tmk_core/common/avr/platform.c
@@ -0,0 +1,21 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "platform_deps.h"
+
+void platform_setup(void) {
+    // do nothing
+}
diff --git a/tmk_core/common/chibios/_timer.h b/tmk_core/common/chibios/_timer.h
new file mode 100644
index 0000000000..77402b612a
--- /dev/null
+++ b/tmk_core/common/chibios/_timer.h
@@ -0,0 +1,19 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+// The platform is 32-bit, so prefer 32-bit timers to avoid overflow
+#define FAST_TIMER_T_SIZE 32
diff --git a/tmk_core/common/chibios/_wait.c b/tmk_core/common/chibios/_wait.c
new file mode 100644
index 0000000000..1fbea2dd5e
--- /dev/null
+++ b/tmk_core/common/chibios/_wait.c
@@ -0,0 +1,89 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __OPTIMIZE__
+#    pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed"
+#endif
+
+#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t   nop\n\t nop\n\t nop\n\t nop\n\t"
+
+__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */
+    /* The argument n must be a constant expression.
+     * That way, compiler optimization will remove unnecessary code. */
+    if (n < 1) {
+        return;
+    }
+    if (n > 8) {
+        unsigned int n8 = n / 8;
+        n               = n - n8 * 8;
+        switch (n8) {
+            case 16:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 15:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 14:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 13:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 12:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 11:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 10:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 9:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 8:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 7:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 6:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 5:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 4:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 3:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 2:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 1:
+                asm volatile(CLOCK_DELAY_NOP8::: "memory");
+            case 0:
+                break;
+        }
+    }
+    switch (n) {
+        case 8:
+            asm volatile("nop" ::: "memory");
+        case 7:
+            asm volatile("nop" ::: "memory");
+        case 6:
+            asm volatile("nop" ::: "memory");
+        case 5:
+            asm volatile("nop" ::: "memory");
+        case 4:
+            asm volatile("nop" ::: "memory");
+        case 3:
+            asm volatile("nop" ::: "memory");
+        case 2:
+            asm volatile("nop" ::: "memory");
+        case 1:
+            asm volatile("nop" ::: "memory");
+        case 0:
+            break;
+    }
+}
diff --git a/tmk_core/common/chibios/_wait.h b/tmk_core/common/chibios/_wait.h
index 5bface53e1..b740afbd24 100644
--- a/tmk_core/common/chibios/_wait.h
+++ b/tmk_core/common/chibios/_wait.h
@@ -16,6 +16,7 @@
 #pragma once
 
 #include <ch.h>
+#include <hal.h>
 
 /* chThdSleepX of zero maps to infinite - so we map to a tiny delay to still yield */
 #define wait_ms(ms)                     \
@@ -26,14 +27,23 @@
             chThdSleepMicroseconds(1);  \
         }                               \
     } while (0)
-#define wait_us(us)                     \
-    do {                                \
-        if (us != 0) {                  \
-            chThdSleepMicroseconds(us); \
-        } else {                        \
-            chThdSleepMicroseconds(1);  \
-        }                               \
-    } while (0)
+
+#ifdef WAIT_US_TIMER
+void wait_us(uint16_t duration);
+#else
+#    define wait_us(us)                     \
+        do {                                \
+            if (us != 0) {                  \
+                chThdSleepMicroseconds(us); \
+            } else {                        \
+                chThdSleepMicroseconds(1);  \
+            }                               \
+        } while (0)
+#endif
+
+#include "_wait.c"
+
+#define CPU_CLOCK STM32_SYSCLK
 
 /* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus
  * to which the GPIO is connected.
@@ -45,11 +55,8 @@
  * If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used.
  * (A fairly large value of 0.25 microseconds is set.)
  */
-
-#include "wait.c"
-
 #ifndef GPIO_INPUT_PIN_DELAY
-#    define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4)
+#    define GPIO_INPUT_PIN_DELAY (CPU_CLOCK / 1000000L / 4)
 #endif
 
 #define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)
diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c
index 11f7abf432..f9514ee5f3 100644
--- a/tmk_core/common/chibios/bootloader.c
+++ b/tmk_core/common/chibios/bootloader.c
@@ -95,7 +95,7 @@ void enter_bootloader_mode_if_requested(void) {
     }
 }
 
-#elif defined(KL2x) || defined(K20x) || defined(MK66F18)  // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS
+#elif defined(KL2x) || defined(K20x) || defined(MK66F18) || defined(MIMXRT1062)  // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS
 /* Kinetis */
 
 #    if defined(BOOTLOADER_KIIBOHD)
@@ -103,7 +103,8 @@ void enter_bootloader_mode_if_requested(void) {
 #        define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000
 const uint8_t              sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff";
 __attribute__((weak)) void bootloader_jump(void) {
-    __builtin_memcpy((void *)VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic));
+    void *volatile vbat = (void *)VBAT;
+    __builtin_memcpy(vbat, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic));
     // request reset
     SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk;
 }
diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c
index ea51989728..5bf852fde1 100644
--- a/tmk_core/common/chibios/eeprom_stm32.c
+++ b/tmk_core/common/chibios/eeprom_stm32.c
@@ -14,185 +14,751 @@
  * Artur F.
  *
  * Modifications for QMK and STM32F303 by Yiancar
+ * Modifications to add flash wear leveling by Ilya Zhuravlev
+ * Modifications to increase flash density by Don Kjer
  */
 
 #include <stdio.h>
-#include <string.h>
+#include <stdbool.h>
+#include "debug.h"
 #include "eeprom_stm32.h"
-/*****************************************************************************
- * Allows to use the internal flash to store non volatile data. To initialize
- * the functionality use the EEPROM_Init() function. Be sure that by reprogramming
- * of the controller just affected pages will be deleted. In other case the non
- * volatile data will be lost.
- ******************************************************************************/
+#include "flash_stm32.h"
+
+/*
+ * We emulate eeprom by writing a snapshot compacted view of eeprom contents,
+ * followed by a write log of any change since that snapshot:
+ *
+ * === SIMULATED EEPROM CONTENTS ===
+ *
+ * ┌─ Compacted ┬ Write Log ─┐
+ * │............│[BYTE][BYTE]│
+ * │FFFF....FFFF│[WRD0][WRD1]│
+ * │FFFFFFFFFFFF│[WORD][NEXT]│
+ * │....FFFFFFFF│[BYTE][WRD0]│
+ * ├────────────┼────────────┤
+ * └──PAGE_BASE │            │
+ *    PAGE_LAST─┴─WRITE_BASE │
+ *                WRITE_LAST ┘
+ *
+ * Compacted contents are the 1's complement of the actual EEPROM contents.
+ * e.g. An 'FFFF' represents a '0000' value.
+ *
+ * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom.
+ * The size of the compacted-area and write log are configurable, and the combined
+ * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent.
+ * Simulated Eeprom contents are located at the end of available flash space.
+ *
+ * The following configuration defines can be set:
+ *
+ * FEE_DENSITY_PAGES   # Total number of pages to use for eeprom simulation (Compact + Write log)
+ * FEE_DENSITY_BYTES   # Size of simulated eeprom. (Defaults to half the space allocated by FEE_DENSITY_PAGES)
+ * NOTE: The current implementation does not include page swapping,
+ * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents.
+ *
+ * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals
+ * FEE_DENSITY_PAGES * FEE_PAGE_SIZE - FEE_DENSITY_BYTES.
+ * The larger the write log, the less frequently the compacted area needs to be rewritten.
+ *
+ *
+ * *** General Algorithm ***
+ *
+ * During initialization:
+ * The contents of the Compacted-flash area are loaded and the 1's complement value
+ * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache).
+ * Write log entries are processed until a 0xFFFF is reached.
+ * Each log entry updates a byte or word in the cache.
+ *
+ * During reads:
+ * EEPROM contents are given back directly from the cache in memory.
+ *
+ * During writes:
+ * The contents of the cache is updated first.
+ * If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash
+ * Otherwise:
+ * If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area.
+ * Otherwise a Write log entry is constructed and appended to the next free position in the Write log.
+ *
+ *
+ * *** Write Log Structure ***
+ *
+ * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned.
+ *
+ * === WRITE LOG ENTRY FORMATS ===
+ *
+ * ╔═══ Byte-Entry ══╗
+ * ║0XXXXXXX║YYYYYYYY║
+ * ║ └──┬──┘║└──┬───┘║
+ * ║ Address║ Value  ║
+ * ╚════════╩════════╝
+ * 0 <= Address < 0x80 (128)
+ *
+ * ╔ Word-Encoded 0 ╗
+ * ║100XXXXXXXXXXXXX║
+ * ║  │└─────┬─────┘║
+ * ║  │Address >> 1 ║
+ * ║  └── Value: 0  ║
+ * ╚════════════════╝
+ * 0 <= Address <= 0x3FFE (16382)
+ *
+ * ╔ Word-Encoded 1 ╗
+ * ║101XXXXXXXXXXXXX║
+ * ║  │└─────┬─────┘║
+ * ║  │Address >> 1 ║
+ * ║  └── Value: 1  ║
+ * ╚════════════════╝
+ * 0 <= Address <= 0x3FFE (16382)
+ *
+ * ╔═══ Reserved ═══╗
+ * ║110XXXXXXXXXXXXX║
+ * ╚════════════════╝
+ *
+ * ╔═══════════ Word-Next ═══════════╗
+ * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║
+ * ║   └─────┬─────┘║└───────┬──────┘║
+ * ║(Address-128)>>1║     ~Value     ║
+ * ╚════════════════╩════════════════╝
+ * (  0 <= Address <  0x0080 (128): Reserved)
+ * 0x80 <= Address <= 0x3FFE (16382)
+ *
+ * Write Log entry ranges:
+ * 0x0000 ... 0x7FFF - Byte-Entry;     address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF)
+ * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0
+ * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1
+ * 0xC000 ... 0xDFFF - Reserved
+ * 0xE000 ... 0xFFBF - Word-Next;      address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry)
+ * 0xFFC0 ... 0xFFFE - Reserved
+ * 0xFFFF            - Unprogrammed
+ *
+ */
 
-/* Private macro -------------------------------------------------------------*/
-/* Private variables ---------------------------------------------------------*/
-/* Functions -----------------------------------------------------------------*/
+/* These bits are used for optimizing encoding of bytes, 0 and 1 */
+#define FEE_WORD_ENCODING 0x8000
+#define FEE_VALUE_NEXT 0x6000
+#define FEE_VALUE_RESERVED 0x4000
+#define FEE_VALUE_ENCODED 0x2000
+#define FEE_BYTE_RANGE 0x80
+
+// HACK ALERT. This definition may not match your processor
+// To Do. Work out correct value for EEPROM_PAGE_SIZE on the STM32F103CT6 etc
+#if defined(EEPROM_EMU_STM32F303xC)
+#    define MCU_STM32F303CC
+#elif defined(EEPROM_EMU_STM32F103xB)
+#    define MCU_STM32F103RB
+#elif defined(EEPROM_EMU_STM32F072xB)
+#    define MCU_STM32F072CB
+#elif defined(EEPROM_EMU_STM32F042x6)
+#    define MCU_STM32F042K6
+#elif !defined(FEE_PAGE_SIZE) || !defined(FEE_DENSITY_PAGES) || !defined(FEE_MCU_FLASH_SIZE)
+#    error "not implemented."
+#endif
+
+#if !defined(FEE_PAGE_SIZE) || !defined(FEE_DENSITY_PAGES)
+#    if defined(MCU_STM32F103RB) || defined(MCU_STM32F042K6)
+#        ifndef FEE_PAGE_SIZE
+#            define FEE_PAGE_SIZE 0x400  // Page size = 1KByte
+#        endif
+#        ifndef FEE_DENSITY_PAGES
+#            define FEE_DENSITY_PAGES 2  // How many pages are used
+#        endif
+#    elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB)
+#        ifndef FEE_PAGE_SIZE
+#            define FEE_PAGE_SIZE 0x800  // Page size = 2KByte
+#        endif
+#        ifndef FEE_DENSITY_PAGES
+#            define FEE_DENSITY_PAGES 4  // How many pages are used
+#        endif
+#    else
+#        error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)."
+#    endif
+#endif
+
+#ifndef FEE_MCU_FLASH_SIZE
+#    if defined(MCU_STM32F103RB) || defined(MCU_STM32F072CB)
+#        define FEE_MCU_FLASH_SIZE 128  // Size in Kb
+#    elif defined(MCU_STM32F042K6)
+#        define FEE_MCU_FLASH_SIZE 32  // Size in Kb
+#    elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE)
+#        define FEE_MCU_FLASH_SIZE 512  // Size in Kb
+#    elif defined(MCU_STM32F103RD)
+#        define FEE_MCU_FLASH_SIZE 384  // Size in Kb
+#    elif defined(MCU_STM32F303CC)
+#        define FEE_MCU_FLASH_SIZE 256  // Size in Kb
+#    else
+#        error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)."
+#    endif
+#endif
+
+#define FEE_XSTR(x) FEE_STR(x)
+#define FEE_STR(x) #x
+
+/* Size of combined compacted eeprom and write log pages */
+#define FEE_DENSITY_MAX_SIZE (FEE_DENSITY_PAGES * FEE_PAGE_SIZE)
+/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */
+#define FEE_ADDRESS_MAX_SIZE 0x4000
+
+#ifndef EEPROM_START_ADDRESS /* *TODO: Get rid of this check */
+#    if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024)
+#        pragma message FEE_XSTR(FEE_DENSITY_MAX_SIZE) " > " FEE_XSTR(FEE_MCU_FLASH_SIZE * 1024)
+#        error emulated eeprom: FEE_DENSITY_PAGES is greater than available flash size
+#    endif
+#endif
+
+/* Size of emulated eeprom */
+#ifdef FEE_DENSITY_BYTES
+#    if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE)
+#        pragma message FEE_XSTR(FEE_DENSITY_BYTES) " > " FEE_XSTR(FEE_DENSITY_MAX_SIZE)
+#        error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE
+#    endif
+#    if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE)
+#        pragma message FEE_XSTR(FEE_DENSITY_BYTES) " == " FEE_XSTR(FEE_DENSITY_MAX_SIZE)
+#        warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log.  This will greatly increase the flash wear rate!
+#    endif
+#    if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE
+#        pragma message FEE_XSTR(FEE_DENSITY_BYTES) " > " FEE_XSTR(FEE_ADDRESS_MAX_SIZE)
+#        error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows
+#    endif
+#    if ((FEE_DENSITY_BYTES) % 2) == 1
+#        error emulated eeprom: FEE_DENSITY_BYTES must be even
+#    endif
+#else
+/* Default to half of allocated space used for emulated eeprom, half for write log */
+#    define FEE_DENSITY_BYTES (FEE_DENSITY_PAGES * FEE_PAGE_SIZE / 2)
+#endif
+
+/* Size of write log */
+#define FEE_WRITE_LOG_BYTES (FEE_DENSITY_PAGES * FEE_PAGE_SIZE - FEE_DENSITY_BYTES)
+
+/* Start of the emulated eeprom compacted flash area */
+#ifndef FEE_FLASH_BASE
+#    define FEE_FLASH_BASE 0x8000000
+#endif
+#define FEE_PAGE_BASE_ADDRESS ((uintptr_t)(FEE_FLASH_BASE) + FEE_MCU_FLASH_SIZE * 1024 - FEE_WRITE_LOG_BYTES - FEE_DENSITY_BYTES)
+/* End of the emulated eeprom compacted flash area */
+#define FEE_PAGE_LAST_ADDRESS (FEE_PAGE_BASE_ADDRESS + FEE_DENSITY_BYTES)
+/* Start of the emulated eeprom write log */
+#define FEE_WRITE_LOG_BASE_ADDRESS FEE_PAGE_LAST_ADDRESS
+/* End of the emulated eeprom write log */
+#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES)
+
+/* Flash word value after erase */
+#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
+
+#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES)
+#    error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available
+#endif
+
+/* In-memory contents of emulated eeprom for faster access */
+/* *TODO: Implement page swapping */
+static uint16_t WordBuf[FEE_DENSITY_BYTES / 2];
+static uint8_t *DataBuf = (uint8_t *)WordBuf;
+
+/* Pointer to the first available slot within the write log */
+static uint16_t *empty_slot;
+
+// #define DEBUG_EEPROM_OUTPUT
+
+/*
+ * Debug print utils
+ */
+
+#if defined(DEBUG_EEPROM_OUTPUT)
+
+#    define debug_eeprom debug_enable
+#    define eeprom_println(s) println(s)
+#    define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__);
+
+#else /* NO_DEBUG */
+
+#    define debug_eeprom false
+#    define eeprom_println(s)
+#    define eeprom_printf(fmt, ...)
+
+#endif /* NO_DEBUG */
+
+void print_eeprom(void) {
+#ifndef NO_DEBUG
+    int empty_rows = 0;
+    for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) {
+        if (i % 16 == 0) {
+            if (i >= FEE_DENSITY_BYTES - 16) {
+                /* Make sure we display the last row */
+                empty_rows = 0;
+            }
+            /* Check if this row is uninitialized */
+            ++empty_rows;
+            for (uint16_t j = 0; j < 16; j++) {
+                if (DataBuf[i + j]) {
+                    empty_rows = 0;
+                    break;
+                }
+            }
+            if (empty_rows > 1) {
+                /* Repeat empty row */
+                if (empty_rows == 2) {
+                    /* Only display the first repeat empty row */
+                    println("*");
+                }
+                i += 15;
+                continue;
+            }
+            xprintf("%04x", i);
+        }
+        if (i % 8 == 0) print(" ");
+
+        xprintf(" %02x", DataBuf[i]);
+        if ((i + 1) % 16 == 0) {
+            println("");
+        }
+    }
+#endif
+}
 
-uint8_t DataBuf[FEE_PAGE_SIZE];
-/*****************************************************************************
- *  Delete Flash Space used for user Data, deletes the whole space between
- *  RW_PAGE_BASE_ADDRESS and the last uC Flash Page
- ******************************************************************************/
 uint16_t EEPROM_Init(void) {
-    // unlock flash
-    FLASH_Unlock();
+    /* Load emulated eeprom contents from compacted flash into memory */
+    uint16_t *src  = (uint16_t *)FEE_PAGE_BASE_ADDRESS;
+    uint16_t *dest = (uint16_t *)DataBuf;
+    for (; src < (uint16_t *)FEE_PAGE_LAST_ADDRESS; ++src, ++dest) {
+        *dest = ~*src;
+    }
+
+    if (debug_eeprom) {
+        println("EEPROM_Init Compacted Pages:");
+        print_eeprom();
+        println("EEPROM_Init Write Log:");
+    }
+
+    /* Replay write log */
+    uint16_t *log_addr;
+    for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) {
+        uint16_t address = *log_addr;
+        if (address == FEE_EMPTY_WORD) {
+            break;
+        }
+        /* Check for lowest 128-bytes optimization */
+        if (!(address & FEE_WORD_ENCODING)) {
+            uint8_t bvalue = (uint8_t)address;
+            address >>= 8;
+            DataBuf[address] = bvalue;
+            eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue);
+        } else {
+            uint16_t wvalue;
+            /* Check if value is in next word */
+            if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) {
+                /* Read value from next word */
+                if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
+                    break;
+                }
+                wvalue = ~*log_addr;
+                if (!wvalue) {
+                    eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr);
+                    /* Possibly incomplete write.  Ignore and continue */
+                    continue;
+                }
+                address &= 0x1FFF;
+                address <<= 1;
+                /* Writes to addresses less than 128 are byte log entries */
+                address += FEE_BYTE_RANGE;
+            } else {
+                /* Reserved for future use */
+                if (address & FEE_VALUE_RESERVED) {
+                    eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr);
+                    continue;
+                }
+                /* Optimization for 0 or 1 values. */
+                wvalue = (address & FEE_VALUE_ENCODED) >> 13;
+                address &= 0x1FFF;
+                address <<= 1;
+            }
+            if (address < FEE_DENSITY_BYTES) {
+                eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue);
+                *(uint16_t *)(&DataBuf[address]) = wvalue;
+            } else {
+                eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue);
+            }
+        }
+    }
 
-    // Clear Flags
-    // FLASH_ClearFlag(FLASH_SR_EOP|FLASH_SR_PGERR|FLASH_SR_WRPERR);
+    empty_slot = log_addr;
+
+    if (debug_eeprom) {
+        println("EEPROM_Init Final DataBuf:");
+        print_eeprom();
+    }
 
     return FEE_DENSITY_BYTES;
 }
-/*****************************************************************************
- *  Erase the whole reserved Flash Space used for user Data
- ******************************************************************************/
-void EEPROM_Erase(void) {
-    int page_num = 0;
 
-    // delete all pages from specified start page to the last page
-    do {
+/* Clear flash contents (doesn't touch in-memory DataBuf) */
+static void eeprom_clear(void) {
+    FLASH_Unlock();
+
+    for (uint16_t page_num = 0; page_num < FEE_DENSITY_PAGES; ++page_num) {
+        eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)));
         FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
-        page_num++;
-    } while (page_num < FEE_DENSITY_PAGES);
+    }
+
+    FLASH_Lock();
+
+    empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS;
+    eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot);
 }
-/*****************************************************************************
- *  Writes once data byte to flash on specified address. If a byte is already
- *  written, the whole page must be copied to a buffer, the byte changed and
- *  the manipulated buffer written after PageErase.
- *******************************************************************************/
-uint16_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
-    FLASH_Status FlashStatus = FLASH_COMPLETE;
 
-    uint32_t page;
-    int      i;
+/* Erase emulated eeprom */
+void EEPROM_Erase(void) {
+    eeprom_println("EEPROM_Erase");
+    /* Erase compacted pages and write log */
+    eeprom_clear();
+    /* re-initialize to reset DataBuf */
+    EEPROM_Init();
+}
 
-    // exit if desired address is above the limit (e.G. under 2048 Bytes for 4 pages)
-    if (Address > FEE_DENSITY_BYTES) {
-        return 0;
+/* Compact write log */
+static uint8_t eeprom_compact(void) {
+    /* Erase compacted pages and write log */
+    eeprom_clear();
+
+    FLASH_Unlock();
+
+    FLASH_Status final_status = FLASH_COMPLETE;
+
+    /* Write emulated eeprom contents from memory to compacted flash */
+    uint16_t *src  = (uint16_t *)DataBuf;
+    uintptr_t dest = FEE_PAGE_BASE_ADDRESS;
+    uint16_t  value;
+    for (; dest < FEE_PAGE_LAST_ADDRESS; ++src, dest += 2) {
+        value = *src;
+        if (value) {
+            eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value);
+            FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value);
+            if (status != FLASH_COMPLETE) final_status = status;
+        }
     }
 
-    // calculate which page is affected (Pagenum1/Pagenum2...PagenumN)
-    page = FEE_ADDR_OFFSET(Address) / FEE_PAGE_SIZE;
+    FLASH_Lock();
 
-    // if current data is 0xFF, the byte is empty, just overwrite with the new one
-    if ((*(__IO uint16_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) == FEE_EMPTY_WORD) {
-        FlashStatus = FLASH_ProgramHalfWord(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address), (uint16_t)(0x00FF & DataByte));
+    if (debug_eeprom) {
+        println("eeprom_compacted:");
+        print_eeprom();
+    }
+
+    return final_status;
+}
+
+static uint8_t eeprom_write_direct_entry(uint16_t Address) {
+    /* Check if we can just write this directly to the compacted flash area */
+    uintptr_t directAddress = FEE_PAGE_BASE_ADDRESS + (Address & 0xFFFE);
+    if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) {
+        /* Write the value directly to the compacted area without a log entry */
+        uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]);
+        /* Early exit if a write isn't needed */
+        if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE;
+
+        FLASH_Unlock();
+
+        eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value);
+        FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value);
+
+        FLASH_Lock();
+        return status;
+    }
+    return 0;
+}
+
+static uint8_t eeprom_write_log_word_entry(uint16_t Address) {
+    FLASH_Status final_status = FLASH_COMPLETE;
+
+    uint16_t value = *(uint16_t *)(&DataBuf[Address]);
+    eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value);
+
+    /* MSB signifies the lowest 128-byte optimization is not in effect */
+    uint16_t encoding = FEE_WORD_ENCODING;
+    uint8_t  entry_size;
+    if (value <= 1) {
+        encoding |= value << 13;
+        entry_size = 2;
     } else {
-        // Copy Page to a buffer
-        memcpy(DataBuf, (uint8_t *)FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE), FEE_PAGE_SIZE);  // !!! Calculate base address for the desired page
+        encoding |= FEE_VALUE_NEXT;
+        entry_size = 4;
+        /* Writes to addresses less than 128 are byte log entries */
+        Address -= FEE_BYTE_RANGE;
+    }
+
+    /* if we can't find an empty spot, we must compact emulated eeprom */
+    if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) {
+        /* compact the write log into the compacted flash area */
+        return eeprom_compact();
+    }
+
+    /* Word log writes should be word-aligned.  Take back a bit */
+    Address >>= 1;
+    Address |= encoding;
+
+    /* ok we found a place let's write our data */
+    FLASH_Unlock();
+
+    /* address */
+    eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address);
+    final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address);
+
+    /* value */
+    if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) {
+        eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value);
+        FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value);
+        if (status != FLASH_COMPLETE) final_status = status;
+    }
+
+    FLASH_Lock();
 
-        // check if new data is differ to current data, return if not, proceed if yes
-        if (DataByte == *(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) {
-            return 0;
+    return final_status;
+}
+
+static uint8_t eeprom_write_log_byte_entry(uint16_t Address) {
+    eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]);
+
+    /* if couldn't find an empty spot, we must compact emulated eeprom */
+    if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
+        /* compact the write log into the compacted flash area */
+        return eeprom_compact();
+    }
+
+    /* ok we found a place let's write our data */
+    FLASH_Unlock();
+
+    /* Pack address and value into the same word */
+    uint16_t value = (Address << 8) | DataBuf[Address];
+
+    /* write to flash */
+    eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value);
+    FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value);
+
+    FLASH_Lock();
+
+    return status;
+}
+
+uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
+    /* if the address is out-of-bounds, do nothing */
+    if (Address >= FEE_DENSITY_BYTES) {
+        eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte);
+        return FLASH_BAD_ADDRESS;
+    }
+
+    /* if the value is the same, don't bother writing it */
+    if (DataBuf[Address] == DataByte) {
+        eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte);
+        return 0;
+    }
+
+    /* keep DataBuf cache in sync */
+    DataBuf[Address] = DataByte;
+    eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]);
+
+    /* perform the write into flash memory */
+    /* First, attempt to write directly into the compacted flash area */
+    FLASH_Status status = eeprom_write_direct_entry(Address);
+    if (!status) {
+        /* Otherwise append to the write log */
+        if (Address < FEE_BYTE_RANGE) {
+            status = eeprom_write_log_byte_entry(Address);
+        } else {
+            status = eeprom_write_log_word_entry(Address & 0xFFFE);
         }
+    }
+    if (status != 0 && status != FLASH_COMPLETE) {
+        eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status);
+    }
+    return status;
+}
 
-        // manipulate desired data byte in temp data array if new byte is differ to the current
-        DataBuf[FEE_ADDR_OFFSET(Address) % FEE_PAGE_SIZE] = DataByte;
+uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) {
+    /* if the address is out-of-bounds, do nothing */
+    if (Address >= FEE_DENSITY_BYTES) {
+        eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord);
+        return FLASH_BAD_ADDRESS;
+    }
 
-        // Erase Page
-        FlashStatus = FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE));
+    /* Check for word alignment */
+    FLASH_Status final_status = FLASH_COMPLETE;
+    if (Address % 2) {
+        final_status        = EEPROM_WriteDataByte(Address, DataWord);
+        FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8);
+        if (status != FLASH_COMPLETE) final_status = status;
+        if (final_status != 0 && final_status != FLASH_COMPLETE) {
+            eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
+        }
+        return final_status;
+    }
+
+    /* if the value is the same, don't bother writing it */
+    uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]);
+    if (oldValue == DataWord) {
+        eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord);
+        return 0;
+    }
+
+    /* keep DataBuf cache in sync */
+    *(uint16_t *)(&DataBuf[Address]) = DataWord;
+    eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address]));
 
-        // Write new data (whole page) to flash if data has been changed
-        for (i = 0; i < (FEE_PAGE_SIZE / 2); i++) {
-            if ((__IO uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)]) != 0xFFFF) {
-                FlashStatus = FLASH_ProgramHalfWord((FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE)) + (i * 2), (uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)]));
+    /* perform the write into flash memory */
+    /* First, attempt to write directly into the compacted flash area */
+    final_status = eeprom_write_direct_entry(Address);
+    if (!final_status) {
+        /* Otherwise append to the write log */
+        /* Check if we need to fall back to byte write */
+        if (Address < FEE_BYTE_RANGE) {
+            final_status = FLASH_COMPLETE;
+            /* Only write a byte if it has changed */
+            if ((uint8_t)oldValue != (uint8_t)DataWord) {
+                final_status = eeprom_write_log_byte_entry(Address);
             }
+            FLASH_Status status = FLASH_COMPLETE;
+            /* Only write a byte if it has changed */
+            if ((oldValue >> 8) != (DataWord >> 8)) {
+                status = eeprom_write_log_byte_entry(Address + 1);
+            }
+            if (status != FLASH_COMPLETE) final_status = status;
+        } else {
+            final_status = eeprom_write_log_word_entry(Address);
         }
     }
-    return FlashStatus;
+    if (final_status != 0 && final_status != FLASH_COMPLETE) {
+        eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
+    }
+    return final_status;
 }
-/*****************************************************************************
- *  Read once data byte from a specified address.
- *******************************************************************************/
+
 uint8_t EEPROM_ReadDataByte(uint16_t Address) {
     uint8_t DataByte = 0xFF;
 
-    // Get Byte from specified address
-    DataByte = (*(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address)));
+    if (Address < FEE_DENSITY_BYTES) {
+        DataByte = DataBuf[Address];
+    }
+
+    eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte);
 
     return DataByte;
 }
 
+uint16_t EEPROM_ReadDataWord(uint16_t Address) {
+    uint16_t DataWord = 0xFFFF;
+
+    if (Address < FEE_DENSITY_BYTES - 1) {
+        /* Check word alignment */
+        if (Address % 2) {
+            DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8);
+        } else {
+            DataWord = *(uint16_t *)(&DataBuf[Address]);
+        }
+    }
+
+    eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord);
+
+    return DataWord;
+}
+
 /*****************************************************************************
  *  Wrap library in AVR style functions.
  *******************************************************************************/
-uint8_t eeprom_read_byte(const uint8_t *Address) {
-    const uint16_t p = (const uint32_t)Address;
-    return EEPROM_ReadDataByte(p);
-}
+uint8_t eeprom_read_byte(const uint8_t *Address) { return EEPROM_ReadDataByte((const uintptr_t)Address); }
 
-void eeprom_write_byte(uint8_t *Address, uint8_t Value) {
-    uint16_t p = (uint32_t)Address;
-    EEPROM_WriteDataByte(p, Value);
-}
+void eeprom_write_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); }
 
-void eeprom_update_byte(uint8_t *Address, uint8_t Value) {
-    uint16_t p = (uint32_t)Address;
-    EEPROM_WriteDataByte(p, Value);
-}
+void eeprom_update_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); }
 
-uint16_t eeprom_read_word(const uint16_t *Address) {
-    const uint16_t p = (const uint32_t)Address;
-    return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8);
-}
+uint16_t eeprom_read_word(const uint16_t *Address) { return EEPROM_ReadDataWord((const uintptr_t)Address); }
 
-void eeprom_write_word(uint16_t *Address, uint16_t Value) {
-    uint16_t p = (uint32_t)Address;
-    EEPROM_WriteDataByte(p, (uint8_t)Value);
-    EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8));
-}
+void eeprom_write_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); }
 
-void eeprom_update_word(uint16_t *Address, uint16_t Value) {
-    uint16_t p = (uint32_t)Address;
-    EEPROM_WriteDataByte(p, (uint8_t)Value);
-    EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8));
-}
+void eeprom_update_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); }
 
 uint32_t eeprom_read_dword(const uint32_t *Address) {
-    const uint16_t p = (const uint32_t)Address;
-    return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8) | (EEPROM_ReadDataByte(p + 2) << 16) | (EEPROM_ReadDataByte(p + 3) << 24);
+    const uint16_t p = (const uintptr_t)Address;
+    /* Check word alignment */
+    if (p % 2) {
+        /* Not aligned */
+        return (uint32_t)EEPROM_ReadDataByte(p) | (uint32_t)(EEPROM_ReadDataWord(p + 1) << 8) | (uint32_t)(EEPROM_ReadDataByte(p + 3) << 24);
+    } else {
+        /* Aligned */
+        return EEPROM_ReadDataWord(p) | (EEPROM_ReadDataWord(p + 2) << 16);
+    }
 }
 
 void eeprom_write_dword(uint32_t *Address, uint32_t Value) {
-    uint16_t p = (const uint32_t)Address;
-    EEPROM_WriteDataByte(p, (uint8_t)Value);
-    EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8));
-    EEPROM_WriteDataByte(p + 2, (uint8_t)(Value >> 16));
-    EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24));
-}
-
-void eeprom_update_dword(uint32_t *Address, uint32_t Value) {
-    uint16_t p             = (const uint32_t)Address;
-    uint32_t existingValue = EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8) | (EEPROM_ReadDataByte(p + 2) << 16) | (EEPROM_ReadDataByte(p + 3) << 24);
-    if (Value != existingValue) {
+    uint16_t p = (const uintptr_t)Address;
+    /* Check word alignment */
+    if (p % 2) {
+        /* Not aligned */
         EEPROM_WriteDataByte(p, (uint8_t)Value);
-        EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8));
-        EEPROM_WriteDataByte(p + 2, (uint8_t)(Value >> 16));
+        EEPROM_WriteDataWord(p + 1, (uint16_t)(Value >> 8));
         EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24));
+    } else {
+        /* Aligned */
+        EEPROM_WriteDataWord(p, (uint16_t)Value);
+        EEPROM_WriteDataWord(p + 2, (uint16_t)(Value >> 16));
     }
 }
 
+void eeprom_update_dword(uint32_t *Address, uint32_t Value) { eeprom_write_dword(Address, Value); }
+
 void eeprom_read_block(void *buf, const void *addr, size_t len) {
-    const uint8_t *p    = (const uint8_t *)addr;
+    const uint8_t *src  = (const uint8_t *)addr;
     uint8_t *      dest = (uint8_t *)buf;
-    while (len--) {
-        *dest++ = eeprom_read_byte(p++);
+
+    /* Check word alignment */
+    if (len && (uintptr_t)src % 2) {
+        /* Read the unaligned first byte */
+        *dest++ = eeprom_read_byte(src++);
+        --len;
+    }
+
+    uint16_t value;
+    bool     aligned = ((uintptr_t)dest % 2 == 0);
+    while (len > 1) {
+        value = eeprom_read_word((uint16_t *)src);
+        if (aligned) {
+            *(uint16_t *)dest = value;
+            dest += 2;
+        } else {
+            *dest++ = value;
+            *dest++ = value >> 8;
+        }
+        src += 2;
+        len -= 2;
+    }
+    if (len) {
+        *dest = eeprom_read_byte(src);
     }
 }
 
 void eeprom_write_block(const void *buf, void *addr, size_t len) {
-    uint8_t *      p   = (uint8_t *)addr;
-    const uint8_t *src = (const uint8_t *)buf;
-    while (len--) {
-        eeprom_write_byte(p++, *src++);
+    uint8_t *      dest = (uint8_t *)addr;
+    const uint8_t *src  = (const uint8_t *)buf;
+
+    /* Check word alignment */
+    if (len && (uintptr_t)dest % 2) {
+        /* Write the unaligned first byte */
+        eeprom_write_byte(dest++, *src++);
+        --len;
     }
-}
 
-void eeprom_update_block(const void *buf, void *addr, size_t len) {
-    uint8_t *      p   = (uint8_t *)addr;
-    const uint8_t *src = (const uint8_t *)buf;
-    while (len--) {
-        eeprom_write_byte(p++, *src++);
+    uint16_t value;
+    bool     aligned = ((uintptr_t)src % 2 == 0);
+    while (len > 1) {
+        if (aligned) {
+            value = *(uint16_t *)src;
+        } else {
+            value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8);
+        }
+        eeprom_write_word((uint16_t *)dest, value);
+        dest += 2;
+        src += 2;
+        len -= 2;
+    }
+
+    if (len) {
+        eeprom_write_byte(dest, *src);
     }
 }
+
+void eeprom_update_block(const void *buf, void *addr, size_t len) { eeprom_write_block(buf, addr, len); }
diff --git a/tmk_core/common/chibios/eeprom_stm32.h b/tmk_core/common/chibios/eeprom_stm32.h
index 4dac7c1b59..8fcfb556b8 100644
--- a/tmk_core/common/chibios/eeprom_stm32.h
+++ b/tmk_core/common/chibios/eeprom_stm32.h
@@ -23,62 +23,11 @@
 
 #pragma once
 
-#include <ch.h>
-#include <hal.h>
-#include "flash_stm32.h"
-
-// HACK ALERT. This definition may not match your processor
-// To Do. Work out correct value for EEPROM_PAGE_SIZE on the STM32F103CT6 etc
-#if defined(EEPROM_EMU_STM32F303xC)
-#    define MCU_STM32F303CC
-#elif defined(EEPROM_EMU_STM32F103xB)
-#    define MCU_STM32F103RB
-#elif defined(EEPROM_EMU_STM32F072xB)
-#    define MCU_STM32F072CB
-#elif defined(EEPROM_EMU_STM32F042x6)
-#    define MCU_STM32F042K6
-#else
-#    error "not implemented."
-#endif
-
-#ifndef EEPROM_PAGE_SIZE
-#    if defined(MCU_STM32F103RB) || defined(MCU_STM32F042K6)
-#        define FEE_PAGE_SIZE (uint16_t)0x400  // Page size = 1KByte
-#        define FEE_DENSITY_PAGES 2            // How many pages are used
-#    elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB)
-#        define FEE_PAGE_SIZE (uint16_t)0x800  // Page size = 2KByte
-#        define FEE_DENSITY_PAGES 4            // How many pages are used
-#    else
-#        error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)."
-#    endif
-#endif
-
-#ifndef EEPROM_START_ADDRESS
-#    if defined(MCU_STM32F103RB) || defined(MCU_STM32F072CB)
-#        define FEE_MCU_FLASH_SIZE 128  // Size in Kb
-#    elif defined(MCU_STM32F042K6)
-#        define FEE_MCU_FLASH_SIZE 32  // Size in Kb
-#    elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE)
-#        define FEE_MCU_FLASH_SIZE 512  // Size in Kb
-#    elif defined(MCU_STM32F103RD)
-#        define FEE_MCU_FLASH_SIZE 384  // Size in Kb
-#    elif defined(MCU_STM32F303CC)
-#        define FEE_MCU_FLASH_SIZE 256  // Size in Kb
-#    else
-#        error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)."
-#    endif
-#endif
-
-// DONT CHANGE
-// Choose location for the first EEPROM Page address on the top of flash
-#define FEE_PAGE_BASE_ADDRESS ((uint32_t)(0x8000000 + FEE_MCU_FLASH_SIZE * 1024 - FEE_DENSITY_PAGES * FEE_PAGE_SIZE))
-#define FEE_DENSITY_BYTES ((FEE_PAGE_SIZE / 2) * FEE_DENSITY_PAGES - 1)
-#define FEE_LAST_PAGE_ADDRESS (FEE_PAGE_BASE_ADDRESS + (FEE_PAGE_SIZE * FEE_DENSITY_PAGES))
-#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
-#define FEE_ADDR_OFFSET(Address) (Address * 2)  // 1Byte per Word will be saved to preserve Flash
-
-// Use this function to initialize the functionality
 uint16_t EEPROM_Init(void);
 void     EEPROM_Erase(void);
-uint16_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);
+uint8_t  EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);
+uint8_t  EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord);
 uint8_t  EEPROM_ReadDataByte(uint16_t Address);
+uint16_t EEPROM_ReadDataWord(uint16_t Address);
+
+void print_eeprom(void);
diff --git a/tmk_core/common/chibios/flash_stm32.h b/tmk_core/common/chibios/flash_stm32.h
index 90d5bff47e..9c6a7cc50f 100644
--- a/tmk_core/common/chibios/flash_stm32.h
+++ b/tmk_core/common/chibios/flash_stm32.h
@@ -22,8 +22,11 @@
 extern "C" {
 #endif
 
-#include <ch.h>
-#include <hal.h>
+#include <stdint.h>
+
+#ifdef FLASH_STM32_MOCKED
+extern uint8_t FlashBuf[MOCK_FLASH_SIZE];
+#endif
 
 typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status;
 
diff --git a/tmk_core/common/chibios/gpio.h b/tmk_core/common/chibios/gpio.h
index 5d0e142abc..4d057f1cab 100644
--- a/tmk_core/common/chibios/gpio.h
+++ b/tmk_core/common/chibios/gpio.h
@@ -20,6 +20,8 @@
 
 typedef ioline_t pin_t;
 
+/* Operation of GPIO by pin. */
+
 #define setPinInput(pin) palSetLineMode(pin, PAL_MODE_INPUT)
 #define setPinInputHigh(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLUP)
 #define setPinInputLow(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN)
@@ -32,3 +34,17 @@ typedef ioline_t pin_t;
 #define readPin(pin) palReadLine(pin)
 
 #define togglePin(pin) palToggleLine(pin)
+
+/* Operation of GPIO by port. */
+
+typedef uint16_t port_data_t;
+
+#define readPort(pin) palReadPort(PAL_PORT(pin))
+
+#define setPortBitInput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT)
+#define setPortBitInputHigh(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLUP)
+#define setPortBitInputLow(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLDOWN)
+#define setPortBitOutput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_OUTPUT_PUSHPULL)
+
+#define writePortBitLow(pin, bit) palClearLine(PAL_LINE(PAL_PORT(pin), bit))
+#define writePortBitHigh(pin, bit) palSetLine(PAL_LINE(PAL_PORT(pin), bit))
diff --git a/tmk_core/common/chibios/platform.c b/tmk_core/common/chibios/platform.c
new file mode 100644
index 0000000000..d4a229f278
--- /dev/null
+++ b/tmk_core/common/chibios/platform.c
@@ -0,0 +1,22 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "platform_deps.h"
+
+void platform_setup(void) {
+    halInit();
+    chSysInit();
+}
\ No newline at end of file
diff --git a/tmk_core/common/chibios/wait.c b/tmk_core/common/chibios/wait.c
index c6270fd95e..56fd6ffcec 100644
--- a/tmk_core/common/chibios/wait.c
+++ b/tmk_core/common/chibios/wait.c
@@ -14,76 +14,28 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef __OPTIMIZE__
-#    pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed"
-#endif
+#include <ch.h>
+#include <hal.h>
 
-#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"
+#include "_wait.h"
 
-__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */
-    /* The argument n must be a constant expression.
-     * That way, compiler optimization will remove unnecessary code. */
-    if (n < 1) {
-        return;
-    }
-    if (n > 8) {
-        unsigned int n8 = n / 8;
-        n               = n - n8 * 8;
-        switch (n8) {
-            case 16:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 15:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 14:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 13:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 12:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 11:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 10:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 9:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 8:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 7:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 6:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 5:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 4:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 3:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 2:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 1:
-                asm volatile(CLOCK_DELAY_NOP8::: "memory");
-            case 0:
-                break;
-        }
+#ifdef WAIT_US_TIMER
+void wait_us(uint16_t duration) {
+    static const GPTConfig gpt_cfg = {1000000, NULL, 0, 0}; /* 1MHz timer, no callback */
+
+    if (duration == 0) {
+        duration = 1;
     }
-    switch (n) {
-        case 8:
-            asm volatile("nop" ::: "memory");
-        case 7:
-            asm volatile("nop" ::: "memory");
-        case 6:
-            asm volatile("nop" ::: "memory");
-        case 5:
-            asm volatile("nop" ::: "memory");
-        case 4:
-            asm volatile("nop" ::: "memory");
-        case 3:
-            asm volatile("nop" ::: "memory");
-        case 2:
-            asm volatile("nop" ::: "memory");
-        case 1:
-            asm volatile("nop" ::: "memory");
-        case 0:
-            break;
+
+    /*
+     * Only use this timer on the main thread;
+     * other threads need to use their own timer.
+     */
+    if (chThdGetSelfX() == &ch.mainthread && duration < (1ULL << (sizeof(gptcnt_t) * 8))) {
+        gptStart(&WAIT_US_TIMER, &gpt_cfg);
+        gptPolledDelay(&WAIT_US_TIMER, duration);
+    } else {
+        chThdSleepMicroseconds(duration);
     }
-}
\ No newline at end of file
+}
+#endif
diff --git a/tmk_core/common/debug.c b/tmk_core/common/debug.c
deleted file mode 100644
index ea62deaa8c..0000000000
--- a/tmk_core/common/debug.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
-Copyright 2011 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-#include "debug.h"
-
-debug_config_t debug_config = {
-    .enable   = false,  //
-    .matrix   = false,  //
-    .keyboard = false,  //
-    .mouse    = false,  //
-    .reserved = 0       //
-};
diff --git a/tmk_core/common/debug.h b/tmk_core/common/debug.h
deleted file mode 100644
index 3d2e2315ef..0000000000
--- a/tmk_core/common/debug.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
-Copyright 2011 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-#include <stdbool.h>
-#include "print.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Debug output control
- */
-typedef union {
-    struct {
-        bool    enable : 1;
-        bool    matrix : 1;
-        bool    keyboard : 1;
-        bool    mouse : 1;
-        uint8_t reserved : 4;
-    };
-    uint8_t raw;
-} debug_config_t;
-
-extern debug_config_t debug_config;
-
-#ifdef __cplusplus
-}
-#endif
-
-/* for backward compatibility */
-#define debug_enable (debug_config.enable)
-#define debug_matrix (debug_config.matrix)
-#define debug_keyboard (debug_config.keyboard)
-#define debug_mouse (debug_config.mouse)
-
-/*
- * Debug print utils
- */
-#ifndef NO_DEBUG
-
-#    define dprint(s)                   \
-        do {                            \
-            if (debug_enable) print(s); \
-        } while (0)
-#    define dprintln(s)                   \
-        do {                              \
-            if (debug_enable) println(s); \
-        } while (0)
-#    define dprintf(fmt, ...)                              \
-        do {                                               \
-            if (debug_enable) xprintf(fmt, ##__VA_ARGS__); \
-        } while (0)
-#    define dmsg(s) dprintf("%s at %s: %S\n", __FILE__, __LINE__, PSTR(s))
-
-/* Deprecated. DO NOT USE these anymore, use dprintf instead. */
-#    define debug(s)                    \
-        do {                            \
-            if (debug_enable) print(s); \
-        } while (0)
-#    define debugln(s)                    \
-        do {                              \
-            if (debug_enable) println(s); \
-        } while (0)
-#    define debug_msg(s)             \
-        do {                         \
-            if (debug_enable) {      \
-                print(__FILE__);     \
-                print(" at ");       \
-                print_dec(__LINE__); \
-                print(" in ");       \
-                print(": ");         \
-                print(s);            \
-            }                        \
-        } while (0)
-#    define debug_dec(data)                    \
-        do {                                   \
-            if (debug_enable) print_dec(data); \
-        } while (0)
-#    define debug_decs(data)                    \
-        do {                                    \
-            if (debug_enable) print_decs(data); \
-        } while (0)
-#    define debug_hex4(data)                    \
-        do {                                    \
-            if (debug_enable) print_hex4(data); \
-        } while (0)
-#    define debug_hex8(data)                    \
-        do {                                    \
-            if (debug_enable) print_hex8(data); \
-        } while (0)
-#    define debug_hex16(data)                    \
-        do {                                     \
-            if (debug_enable) print_hex16(data); \
-        } while (0)
-#    define debug_hex32(data)                    \
-        do {                                     \
-            if (debug_enable) print_hex32(data); \
-        } while (0)
-#    define debug_bin8(data)                    \
-        do {                                    \
-            if (debug_enable) print_bin8(data); \
-        } while (0)
-#    define debug_bin16(data)                    \
-        do {                                     \
-            if (debug_enable) print_bin16(data); \
-        } while (0)
-#    define debug_bin32(data)                    \
-        do {                                     \
-            if (debug_enable) print_bin32(data); \
-        } while (0)
-#    define debug_bin_reverse8(data)                    \
-        do {                                            \
-            if (debug_enable) print_bin_reverse8(data); \
-        } while (0)
-#    define debug_bin_reverse16(data)                    \
-        do {                                             \
-            if (debug_enable) print_bin_reverse16(data); \
-        } while (0)
-#    define debug_bin_reverse32(data)                    \
-        do {                                             \
-            if (debug_enable) print_bin_reverse32(data); \
-        } while (0)
-#    define debug_hex(data) debug_hex8(data)
-#    define debug_bin(data) debug_bin8(data)
-#    define debug_bin_reverse(data) debug_bin8(data)
-
-#else /* NO_DEBUG */
-
-#    define dprint(s)
-#    define dprintln(s)
-#    define dprintf(fmt, ...)
-#    define dmsg(s)
-#    define debug(s)
-#    define debugln(s)
-#    define debug_msg(s)
-#    define debug_dec(data)
-#    define debug_decs(data)
-#    define debug_hex4(data)
-#    define debug_hex8(data)
-#    define debug_hex16(data)
-#    define debug_hex32(data)
-#    define debug_bin8(data)
-#    define debug_bin16(data)
-#    define debug_bin32(data)
-#    define debug_bin_reverse8(data)
-#    define debug_bin_reverse16(data)
-#    define debug_bin_reverse32(data)
-#    define debug_hex(data)
-#    define debug_bin(data)
-#    define debug_bin_reverse(data)
-
-#endif /* NO_DEBUG */
diff --git a/tmk_core/common/eeconfig.c b/tmk_core/common/eeconfig.c
deleted file mode 100644
index ffa56ab56d..0000000000
--- a/tmk_core/common/eeconfig.c
+++ /dev/null
@@ -1,211 +0,0 @@
-#include <stdint.h>
-#include <stdbool.h>
-#include "eeprom.h"
-#include "eeconfig.h"
-#include "action_layer.h"
-
-#ifdef STM32_EEPROM_ENABLE
-#    include <hal.h>
-#    include "eeprom_stm32.h"
-#endif
-
-#if defined(EEPROM_DRIVER)
-#    include "eeprom_driver.h"
-#endif
-
-#if defined(HAPTIC_ENABLE)
-#    include "haptic.h"
-#endif
-
-/** \brief eeconfig enable
- *
- * FIXME: needs doc
- */
-__attribute__((weak)) void eeconfig_init_user(void) {
-    // Reset user EEPROM value to blank, rather than to a set value
-    eeconfig_update_user(0);
-}
-
-__attribute__((weak)) void eeconfig_init_kb(void) {
-    // Reset Keyboard EEPROM value to blank, rather than to a set value
-    eeconfig_update_kb(0);
-
-    eeconfig_init_user();
-}
-
-/*
- * FIXME: needs doc
- */
-void eeconfig_init_quantum(void) {
-#ifdef STM32_EEPROM_ENABLE
-    EEPROM_Erase();
-#endif
-#if defined(EEPROM_DRIVER)
-    eeprom_driver_erase();
-#endif
-    eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER);
-    eeprom_update_byte(EECONFIG_DEBUG, 0);
-    eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0);
-    default_layer_state = 0;
-    eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, 0);
-    eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0);
-    eeprom_update_byte(EECONFIG_MOUSEKEY_ACCEL, 0);
-    eeprom_update_byte(EECONFIG_BACKLIGHT, 0);
-    eeprom_update_byte(EECONFIG_AUDIO, 0xFF);  // On by default
-    eeprom_update_dword(EECONFIG_RGBLIGHT, 0);
-    eeprom_update_byte(EECONFIG_STENOMODE, 0);
-    eeprom_update_dword(EECONFIG_HAPTIC, 0);
-    eeprom_update_byte(EECONFIG_VELOCIKEY, 0);
-    eeprom_update_dword(EECONFIG_RGB_MATRIX, 0);
-    eeprom_update_word(EECONFIG_RGB_MATRIX_EXTENDED, 0);
-
-    // TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS
-    //        within the emulated eeprom via dfu-util or another tool
-#if defined INIT_EE_HANDS_LEFT
-#    pragma message "Faking EE_HANDS for left hand"
-    eeprom_update_byte(EECONFIG_HANDEDNESS, 1);
-#elif defined INIT_EE_HANDS_RIGHT
-#    pragma message "Faking EE_HANDS for right hand"
-    eeprom_update_byte(EECONFIG_HANDEDNESS, 0);
-#endif
-
-#if defined(HAPTIC_ENABLE)
-    haptic_reset();
-#else
-    // this is used in case haptic is disabled, but we still want sane defaults
-    // in the haptic configuration eeprom. All zero will trigger a haptic_reset
-    // when a haptic-enabled firmware is loaded onto the keyboard.
-    eeprom_update_dword(EECONFIG_HAPTIC, 0);
-#endif
-
-    eeconfig_init_kb();
-}
-
-/** \brief eeconfig initialization
- *
- * FIXME: needs doc
- */
-void eeconfig_init(void) { eeconfig_init_quantum(); }
-
-/** \brief eeconfig enable
- *
- * FIXME: needs doc
- */
-void eeconfig_enable(void) { eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); }
-
-/** \brief eeconfig disable
- *
- * FIXME: needs doc
- */
-void eeconfig_disable(void) {
-#ifdef STM32_EEPROM_ENABLE
-    EEPROM_Erase();
-#endif
-#if defined(EEPROM_DRIVER)
-    eeprom_driver_erase();
-#endif
-    eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF);
-}
-
-/** \brief eeconfig is enabled
- *
- * FIXME: needs doc
- */
-bool eeconfig_is_enabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER); }
-
-/** \brief eeconfig is disabled
- *
- * FIXME: needs doc
- */
-bool eeconfig_is_disabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF); }
-
-/** \brief eeconfig read debug
- *
- * FIXME: needs doc
- */
-uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); }
-/** \brief eeconfig update debug
- *
- * FIXME: needs doc
- */
-void eeconfig_update_debug(uint8_t val) { eeprom_update_byte(EECONFIG_DEBUG, val); }
-
-/** \brief eeconfig read default layer
- *
- * FIXME: needs doc
- */
-uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); }
-/** \brief eeconfig update default layer
- *
- * FIXME: needs doc
- */
-void eeconfig_update_default_layer(uint8_t val) { eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val); }
-
-/** \brief eeconfig read keymap
- *
- * FIXME: needs doc
- */
-uint16_t eeconfig_read_keymap(void) { return (eeprom_read_byte(EECONFIG_KEYMAP_LOWER_BYTE) | (eeprom_read_byte(EECONFIG_KEYMAP_UPPER_BYTE) << 8)); }
-/** \brief eeconfig update keymap
- *
- * FIXME: needs doc
- */
-void eeconfig_update_keymap(uint16_t val) {
-    eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, val & 0xFF);
-    eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, (val >> 8) & 0xFF);
-}
-
-/** \brief eeconfig read audio
- *
- * FIXME: needs doc
- */
-uint8_t eeconfig_read_audio(void) { return eeprom_read_byte(EECONFIG_AUDIO); }
-/** \brief eeconfig update audio
- *
- * FIXME: needs doc
- */
-void eeconfig_update_audio(uint8_t val) { eeprom_update_byte(EECONFIG_AUDIO, val); }
-
-/** \brief eeconfig read kb
- *
- * FIXME: needs doc
- */
-uint32_t eeconfig_read_kb(void) { return eeprom_read_dword(EECONFIG_KEYBOARD); }
-/** \brief eeconfig update kb
- *
- * FIXME: needs doc
- */
-void eeconfig_update_kb(uint32_t val) { eeprom_update_dword(EECONFIG_KEYBOARD, val); }
-
-/** \brief eeconfig read user
- *
- * FIXME: needs doc
- */
-uint32_t eeconfig_read_user(void) { return eeprom_read_dword(EECONFIG_USER); }
-/** \brief eeconfig update user
- *
- * FIXME: needs doc
- */
-void eeconfig_update_user(uint32_t val) { eeprom_update_dword(EECONFIG_USER, val); }
-
-/** \brief eeconfig read haptic
- *
- * FIXME: needs doc
- */
-uint32_t eeconfig_read_haptic(void) { return eeprom_read_dword(EECONFIG_HAPTIC); }
-/** \brief eeconfig update haptic
- *
- * FIXME: needs doc
- */
-void eeconfig_update_haptic(uint32_t val) { eeprom_update_dword(EECONFIG_HAPTIC, val); }
-
-/** \brief eeconfig read split handedness
- *
- * FIXME: needs doc
- */
-bool eeconfig_read_handedness(void) { return !!eeprom_read_byte(EECONFIG_HANDEDNESS); }
-/** \brief eeconfig update split handedness
- *
- * FIXME: needs doc
- */
-void eeconfig_update_handedness(bool val) { eeprom_update_byte(EECONFIG_HANDEDNESS, !!val); }
diff --git a/tmk_core/common/eeconfig.h b/tmk_core/common/eeconfig.h
deleted file mode 100644
index a88071729d..0000000000
--- a/tmk_core/common/eeconfig.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-Copyright 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#ifndef EECONFIG_MAGIC_NUMBER
-#    define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEA  // When changing, decrement this value to avoid future re-init issues
-#endif
-#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF
-
-/* EEPROM parameter address */
-#define EECONFIG_MAGIC (uint16_t *)0
-#define EECONFIG_DEBUG (uint8_t *)2
-#define EECONFIG_DEFAULT_LAYER (uint8_t *)3
-#define EECONFIG_KEYMAP (uint8_t *)4
-#define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)5
-#define EECONFIG_BACKLIGHT (uint8_t *)6
-#define EECONFIG_AUDIO (uint8_t *)7
-#define EECONFIG_RGBLIGHT (uint32_t *)8
-#define EECONFIG_UNICODEMODE (uint8_t *)12
-#define EECONFIG_STENOMODE (uint8_t *)13
-// EEHANDS for two handed boards
-#define EECONFIG_HANDEDNESS (uint8_t *)14
-#define EECONFIG_KEYBOARD (uint32_t *)15
-#define EECONFIG_USER (uint32_t *)19
-#define EECONFIG_VELOCIKEY (uint8_t *)23
-
-#define EECONFIG_HAPTIC (uint32_t *)24
-
-// Mutually exclusive
-#define EECONFIG_LED_MATRIX (uint32_t *)28
-#define EECONFIG_RGB_MATRIX (uint32_t *)28
-// Speed & Flags
-#define EECONFIG_LED_MATRIX_EXTENDED (uint16_t *)32
-#define EECONFIG_RGB_MATRIX_EXTENDED (uint16_t *)32
-
-// TODO: Combine these into a single word and single block of EEPROM
-#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34
-// Size of EEPROM being used, other code can refer to this for available EEPROM
-#define EECONFIG_SIZE 35
-/* debug bit */
-#define EECONFIG_DEBUG_ENABLE (1 << 0)
-#define EECONFIG_DEBUG_MATRIX (1 << 1)
-#define EECONFIG_DEBUG_KEYBOARD (1 << 2)
-#define EECONFIG_DEBUG_MOUSE (1 << 3)
-
-/* keyconf bit */
-#define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0)
-#define EECONFIG_KEYMAP_CAPSLOCK_TO_CONTROL (1 << 1)
-#define EECONFIG_KEYMAP_SWAP_LALT_LGUI (1 << 2)
-#define EECONFIG_KEYMAP_SWAP_RALT_RGUI (1 << 3)
-#define EECONFIG_KEYMAP_NO_GUI (1 << 4)
-#define EECONFIG_KEYMAP_SWAP_GRAVE_ESC (1 << 5)
-#define EECONFIG_KEYMAP_SWAP_BACKSLASH_BACKSPACE (1 << 6)
-#define EECONFIG_KEYMAP_NKRO (1 << 7)
-
-#define EECONFIG_KEYMAP_LOWER_BYTE EECONFIG_KEYMAP
-
-bool eeconfig_is_enabled(void);
-bool eeconfig_is_disabled(void);
-
-void eeconfig_init(void);
-void eeconfig_init_quantum(void);
-void eeconfig_init_kb(void);
-void eeconfig_init_user(void);
-
-void eeconfig_enable(void);
-
-void eeconfig_disable(void);
-
-uint8_t eeconfig_read_debug(void);
-void    eeconfig_update_debug(uint8_t val);
-
-uint8_t eeconfig_read_default_layer(void);
-void    eeconfig_update_default_layer(uint8_t val);
-
-uint16_t eeconfig_read_keymap(void);
-void     eeconfig_update_keymap(uint16_t val);
-
-#ifdef AUDIO_ENABLE
-uint8_t eeconfig_read_audio(void);
-void    eeconfig_update_audio(uint8_t val);
-#endif
-
-uint32_t eeconfig_read_kb(void);
-void     eeconfig_update_kb(uint32_t val);
-uint32_t eeconfig_read_user(void);
-void     eeconfig_update_user(uint32_t val);
-
-#ifdef HAPTIC_ENABLE
-uint32_t eeconfig_read_haptic(void);
-void     eeconfig_update_haptic(uint32_t val);
-#endif
-
-bool eeconfig_read_handedness(void);
-void eeconfig_update_handedness(bool val);
diff --git a/tmk_core/common/host.c b/tmk_core/common/host.c
index e7d92cfac6..f0c396b189 100644
--- a/tmk_core/common/host.c
+++ b/tmk_core/common/host.c
@@ -17,10 +17,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include <stdint.h>
 //#include <avr/interrupt.h>
+#include "keyboard.h"
 #include "keycode.h"
 #include "host.h"
 #include "util.h"
 #include "debug.h"
+#include "digitizer.h"
 
 #ifdef NKRO_ENABLE
 #    include "keycode_config.h"
@@ -35,15 +37,20 @@ void host_set_driver(host_driver_t *d) { driver = d; }
 
 host_driver_t *host_get_driver(void) { return driver; }
 
+#ifdef SPLIT_KEYBOARD
+uint8_t split_led_state = 0;
+void    set_split_host_keyboard_leds(uint8_t led_state) { split_led_state = led_state; }
+#endif
+
 uint8_t host_keyboard_leds(void) {
+#ifdef SPLIT_KEYBOARD
+    if (!is_keyboard_master()) return split_led_state;
+#endif
     if (!driver) return 0;
     return (*driver->keyboard_leds)();
 }
 
-led_t host_keyboard_led_state(void) {
-    if (!driver) return (led_t){0};
-    return (led_t)((*driver->keyboard_leds)());
-}
+led_t host_keyboard_led_state(void) { return (led_t)host_keyboard_leds(); }
 
 /* send report */
 void host_keyboard_send(report_keyboard_t *report) {
@@ -97,6 +104,24 @@ void host_consumer_send(uint16_t report) {
     (*driver->send_consumer)(report);
 }
 
+void host_digitizer_send(digitizer_t *digitizer) {
+    if (!driver) return;
+
+    report_digitizer_t report = {
+#ifdef DIGITIZER_SHARED_EP
+        .report_id = REPORT_ID_DIGITIZER,
+#endif
+        .tip     = digitizer->tipswitch & 0x1,
+        .inrange = digitizer->inrange & 0x1,
+        .x       = (uint16_t)(digitizer->x * 0x7FFF),
+        .y       = (uint16_t)(digitizer->y * 0x7FFF),
+    };
+
+    send_digitizer(&report);
+}
+
+__attribute__((weak)) void send_digitizer(report_digitizer_t *report) {}
+
 uint16_t host_last_system_report(void) { return last_system_report; }
 
 uint16_t host_last_consumer_report(void) { return last_consumer_report; }
diff --git a/tmk_core/common/host_driver.h b/tmk_core/common/host_driver.h
index f34a220530..2aebca043d 100644
--- a/tmk_core/common/host_driver.h
+++ b/tmk_core/common/host_driver.h
@@ -30,3 +30,5 @@ typedef struct {
     void (*send_system)(uint16_t);
     void (*send_consumer)(uint16_t);
 } host_driver_t;
+
+void send_digitizer(report_digitizer_t *report);
\ No newline at end of file
diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c
deleted file mode 100644
index 3d6092e71c..0000000000
--- a/tmk_core/common/keyboard.c
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
-Copyright 2011, 2012, 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <stdint.h>
-#include "keyboard.h"
-#include "matrix.h"
-#include "keymap.h"
-#include "host.h"
-#include "led.h"
-#include "keycode.h"
-#include "timer.h"
-#include "sync_timer.h"
-#include "print.h"
-#include "debug.h"
-#include "command.h"
-#include "util.h"
-#include "sendchar.h"
-#include "eeconfig.h"
-#include "action_layer.h"
-#ifdef BACKLIGHT_ENABLE
-#    include "backlight.h"
-#endif
-#ifdef MOUSEKEY_ENABLE
-#    include "mousekey.h"
-#endif
-#ifdef PS2_MOUSE_ENABLE
-#    include "ps2_mouse.h"
-#endif
-#ifdef SERIAL_MOUSE_ENABLE
-#    include "serial_mouse.h"
-#endif
-#ifdef ADB_MOUSE_ENABLE
-#    include "adb.h"
-#endif
-#ifdef RGBLIGHT_ENABLE
-#    include "rgblight.h"
-#endif
-#ifdef LED_MATRIX_ENABLE
-#    include "led_matrix.h"
-#endif
-#ifdef RGB_MATRIX_ENABLE
-#    include "rgb_matrix.h"
-#endif
-#ifdef ENCODER_ENABLE
-#    include "encoder.h"
-#endif
-#ifdef STENO_ENABLE
-#    include "process_steno.h"
-#endif
-#ifdef SERIAL_LINK_ENABLE
-#    include "serial_link/system/serial_link.h"
-#endif
-#ifdef VISUALIZER_ENABLE
-#    include "visualizer/visualizer.h"
-#endif
-#ifdef POINTING_DEVICE_ENABLE
-#    include "pointing_device.h"
-#endif
-#ifdef MIDI_ENABLE
-#    include "process_midi.h"
-#endif
-#ifdef JOYSTICK_ENABLE
-#    include "process_joystick.h"
-#endif
-#ifdef HD44780_ENABLE
-#    include "hd44780.h"
-#endif
-#ifdef QWIIC_ENABLE
-#    include "qwiic.h"
-#endif
-#ifdef OLED_DRIVER_ENABLE
-#    include "oled_driver.h"
-#endif
-#ifdef VELOCIKEY_ENABLE
-#    include "velocikey.h"
-#endif
-#ifdef VIA_ENABLE
-#    include "via.h"
-#endif
-#ifdef DIP_SWITCH_ENABLE
-#    include "dip_switch.h"
-#endif
-#ifdef STM32_EEPROM_ENABLE
-#    include "eeprom_stm32.h"
-#endif
-#ifdef EEPROM_DRIVER
-#    include "eeprom_driver.h"
-#endif
-
-static uint32_t last_input_modification_time = 0;
-uint32_t        last_input_activity_time(void) { return last_input_modification_time; }
-uint32_t        last_input_activity_elapsed(void) { return timer_elapsed32(last_input_modification_time); }
-
-static uint32_t last_matrix_modification_time = 0;
-uint32_t        last_matrix_activity_time(void) { return last_matrix_modification_time; }
-uint32_t        last_matrix_activity_elapsed(void) { return timer_elapsed32(last_matrix_modification_time); }
-void            last_matrix_activity_trigger(void) { last_matrix_modification_time = last_input_modification_time = timer_read32(); }
-
-static uint32_t last_encoder_modification_time = 0;
-uint32_t        last_encoder_activity_time(void) { return last_encoder_modification_time; }
-uint32_t        last_encoder_activity_elapsed(void) { return timer_elapsed32(last_encoder_modification_time); }
-void            last_encoder_activity_trigger(void) { last_encoder_modification_time = last_input_modification_time = timer_read32(); }
-
-// Only enable this if console is enabled to print to
-#if defined(DEBUG_MATRIX_SCAN_RATE)
-static uint32_t matrix_timer           = 0;
-static uint32_t matrix_scan_count      = 0;
-static uint32_t last_matrix_scan_count = 0;
-
-void matrix_scan_perf_task(void) {
-    matrix_scan_count++;
-
-    uint32_t timer_now = timer_read32();
-    if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) {
-#    if defined(CONSOLE_ENABLE)
-        dprintf("matrix scan frequency: %lu\n", matrix_scan_count);
-#    endif
-        last_matrix_scan_count = matrix_scan_count;
-        matrix_timer           = timer_now;
-        matrix_scan_count      = 0;
-    }
-}
-
-uint32_t get_matrix_scan_rate(void) { return last_matrix_scan_count; }
-#else
-#    define matrix_scan_perf_task()
-#endif
-
-#ifdef MATRIX_HAS_GHOST
-extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
-static matrix_row_t   get_real_keys(uint8_t row, matrix_row_t rowdata) {
-    matrix_row_t out = 0;
-    for (uint8_t col = 0; col < MATRIX_COLS; col++) {
-        // read each key in the row data and check if the keymap defines it as a real key
-        if (pgm_read_byte(&keymaps[0][row][col]) && (rowdata & (1 << col))) {
-            // this creates new row data, if a key is defined in the keymap, it will be set here
-            out |= 1 << col;
-        }
-    }
-    return out;
-}
-
-static inline bool popcount_more_than_one(matrix_row_t rowdata) {
-    rowdata &= rowdata - 1;  // if there are less than two bits (keys) set, rowdata will become zero
-    return rowdata;
-}
-
-static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) {
-    /* No ghost exists when less than 2 keys are down on the row.
-    If there are "active" blanks in the matrix, the key can't be pressed by the user,
-    there is no doubt as to which keys are really being pressed.
-    The ghosts will be ignored, they are KC_NO.   */
-    rowdata = get_real_keys(row, rowdata);
-    if ((popcount_more_than_one(rowdata)) == 0) {
-        return false;
-    }
-    /* Ghost occurs when the row shares a column line with other row,
-    and two columns are read on each row. Blanks in the matrix don't matter,
-    so they are filtered out.
-    If there are two or more real keys pressed and they match columns with
-    at least two of another row's real keys, the row will be ignored. Keep in mind,
-    we are checking one row at a time, not all of them at once.
-    */
-    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
-        if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-#endif
-
-void disable_jtag(void) {
-// To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles.
-#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
-    MCUCR |= _BV(JTD);
-    MCUCR |= _BV(JTD);
-#elif defined(__AVR_ATmega32A__)
-    MCUCSR |= _BV(JTD);
-    MCUCSR |= _BV(JTD);
-#endif
-}
-
-/** \brief matrix_setup
- *
- * FIXME: needs doc
- */
-__attribute__((weak)) void matrix_setup(void) {}
-
-/** \brief keyboard_pre_init_user
- *
- * FIXME: needs doc
- */
-__attribute__((weak)) void keyboard_pre_init_user(void) {}
-
-/** \brief keyboard_pre_init_kb
- *
- * FIXME: needs doc
- */
-__attribute__((weak)) void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); }
-
-/** \brief keyboard_post_init_user
- *
- * FIXME: needs doc
- */
-
-__attribute__((weak)) void keyboard_post_init_user() {}
-
-/** \brief keyboard_post_init_kb
- *
- * FIXME: needs doc
- */
-
-__attribute__((weak)) void keyboard_post_init_kb(void) { keyboard_post_init_user(); }
-
-/** \brief keyboard_setup
- *
- * FIXME: needs doc
- */
-void keyboard_setup(void) {
-#ifndef NO_JTAG_DISABLE
-    disable_jtag();
-#endif
-    print_set_sendchar(sendchar);
-#ifdef STM32_EEPROM_ENABLE
-    EEPROM_Init();
-#endif
-#ifdef EEPROM_DRIVER
-    eeprom_driver_init();
-#endif
-    matrix_setup();
-    keyboard_pre_init_kb();
-}
-
-/** \brief is_keyboard_master
- *
- * FIXME: needs doc
- */
-__attribute__((weak)) bool is_keyboard_master(void) { return true; }
-
-/** \brief is_keyboard_left
- *
- * FIXME: needs doc
- */
-__attribute__((weak)) bool is_keyboard_left(void) { return true; }
-
-/** \brief should_process_keypress
- *
- * Override this function if you have a condition where keypresses processing should change:
- *   - splits where the slave side needs to process for rgb/oled functionality
- */
-__attribute__((weak)) bool should_process_keypress(void) { return is_keyboard_master(); }
-
-/** \brief housekeeping_task_kb
- *
- * Override this function if you have a need to execute code for every keyboard main loop iteration.
- * This is specific to keyboard-level functionality.
- */
-__attribute__((weak)) void housekeeping_task_kb(void) {}
-
-/** \brief housekeeping_task_user
- *
- * Override this function if you have a need to execute code for every keyboard main loop iteration.
- * This is specific to user/keymap-level functionality.
- */
-__attribute__((weak)) void housekeeping_task_user(void) {}
-
-/** \brief housekeeping_task
- *
- * Invokes hooks for executing code after QMK is done after each loop iteration.
- */
-void housekeeping_task(void) {
-    housekeeping_task_kb();
-    housekeeping_task_user();
-}
-
-/** \brief keyboard_init
- *
- * FIXME: needs doc
- */
-void keyboard_init(void) {
-    timer_init();
-    sync_timer_init();
-    matrix_init();
-#ifdef VIA_ENABLE
-    via_init();
-#endif
-#ifdef QWIIC_ENABLE
-    qwiic_init();
-#endif
-#ifdef OLED_DRIVER_ENABLE
-    oled_init(OLED_ROTATION_0);
-#endif
-#ifdef PS2_MOUSE_ENABLE
-    ps2_mouse_init();
-#endif
-#ifdef SERIAL_MOUSE_ENABLE
-    serial_mouse_init();
-#endif
-#ifdef ADB_MOUSE_ENABLE
-    adb_mouse_init();
-#endif
-#ifdef BACKLIGHT_ENABLE
-    backlight_init();
-#endif
-#ifdef RGBLIGHT_ENABLE
-    rgblight_init();
-#endif
-#ifdef ENCODER_ENABLE
-    encoder_init();
-#endif
-#ifdef STENO_ENABLE
-    steno_init();
-#endif
-#ifdef POINTING_DEVICE_ENABLE
-    pointing_device_init();
-#endif
-#if defined(NKRO_ENABLE) && defined(FORCE_NKRO)
-    keymap_config.nkro = 1;
-    eeconfig_update_keymap(keymap_config.raw);
-#endif
-#ifdef DIP_SWITCH_ENABLE
-    dip_switch_init();
-#endif
-
-#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)
-    debug_enable = true;
-#endif
-
-    keyboard_post_init_kb(); /* Always keep this last */
-}
-
-/** \brief key_event_task
- *
- * This function is responsible for calling into other systems when they need to respond to electrical switch press events.
- * This is differnet than keycode events as no layer processing, or filtering occurs.
- */
-void switch_events(uint8_t row, uint8_t col, bool pressed) {
-#if defined(LED_MATRIX_ENABLE)
-    process_led_matrix(row, col, pressed);
-#endif
-#if defined(RGB_MATRIX_ENABLE)
-    process_rgb_matrix(row, col, pressed);
-#endif
-}
-
-/** \brief Keyboard task: Do keyboard routine jobs
- *
- * Do routine keyboard jobs:
- *
- * * scan matrix
- * * handle mouse movements
- * * run visualizer code
- * * handle midi commands
- * * light LEDs
- *
- * This is repeatedly called as fast as possible.
- */
-void keyboard_task(void) {
-    static matrix_row_t matrix_prev[MATRIX_ROWS];
-    static uint8_t      led_status    = 0;
-    matrix_row_t        matrix_row    = 0;
-    matrix_row_t        matrix_change = 0;
-#ifdef QMK_KEYS_PER_SCAN
-    uint8_t keys_processed = 0;
-#endif
-#ifdef ENCODER_ENABLE
-    bool encoders_changed = false;
-#endif
-
-    uint8_t matrix_changed = matrix_scan();
-    if (matrix_changed) last_matrix_activity_trigger();
-
-    for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
-        matrix_row    = matrix_get_row(r);
-        matrix_change = matrix_row ^ matrix_prev[r];
-        if (matrix_change) {
-#ifdef MATRIX_HAS_GHOST
-            if (has_ghost_in_row(r, matrix_row)) {
-                continue;
-            }
-#endif
-            if (debug_matrix) matrix_print();
-            matrix_row_t col_mask = 1;
-            for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) {
-                if (matrix_change & col_mask) {
-                    if (should_process_keypress()) {
-                        action_exec((keyevent_t){
-                            .key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */
-                        });
-                    }
-                    // record a processed key
-                    matrix_prev[r] ^= col_mask;
-
-                    switch_events(r, c, (matrix_row & col_mask));
-
-#ifdef QMK_KEYS_PER_SCAN
-                    // only jump out if we have processed "enough" keys.
-                    if (++keys_processed >= QMK_KEYS_PER_SCAN)
-#endif
-                        // process a key per task call
-                        goto MATRIX_LOOP_END;
-                }
-            }
-        }
-    }
-    // call with pseudo tick event when no real key event.
-#ifdef QMK_KEYS_PER_SCAN
-    // we can get here with some keys processed now.
-    if (!keys_processed)
-#endif
-        action_exec(TICK);
-
-MATRIX_LOOP_END:
-
-#ifdef DEBUG_MATRIX_SCAN_RATE
-    matrix_scan_perf_task();
-#endif
-
-#if defined(RGBLIGHT_ENABLE)
-    rgblight_task();
-#endif
-
-#ifdef LED_MATRIX_ENABLE
-    led_matrix_task();
-#endif
-#ifdef RGB_MATRIX_ENABLE
-    rgb_matrix_task();
-#endif
-
-#if defined(BACKLIGHT_ENABLE)
-#    if defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS)
-    backlight_task();
-#    endif
-#endif
-
-#ifdef ENCODER_ENABLE
-    encoders_changed = encoder_read();
-    if (encoders_changed) last_encoder_activity_trigger();
-#endif
-
-#ifdef QWIIC_ENABLE
-    qwiic_task();
-#endif
-
-#ifdef OLED_DRIVER_ENABLE
-    oled_task();
-#    ifndef OLED_DISABLE_TIMEOUT
-    // Wake up oled if user is using those fabulous keys or spinning those encoders!
-#        ifdef ENCODER_ENABLE
-    if (matrix_changed || encoders_changed) oled_on();
-#        else
-    if (matrix_changed) oled_on();
-#        endif
-#    endif
-#endif
-
-#ifdef MOUSEKEY_ENABLE
-    // mousekey repeat & acceleration
-    mousekey_task();
-#endif
-
-#ifdef PS2_MOUSE_ENABLE
-    ps2_mouse_task();
-#endif
-
-#ifdef SERIAL_MOUSE_ENABLE
-    serial_mouse_task();
-#endif
-
-#ifdef ADB_MOUSE_ENABLE
-    adb_mouse_task();
-#endif
-
-#ifdef SERIAL_LINK_ENABLE
-    serial_link_update();
-#endif
-
-#ifdef VISUALIZER_ENABLE
-    visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds());
-#endif
-
-#ifdef POINTING_DEVICE_ENABLE
-    pointing_device_task();
-#endif
-
-#ifdef MIDI_ENABLE
-    midi_task();
-#endif
-
-#ifdef VELOCIKEY_ENABLE
-    if (velocikey_enabled()) {
-        velocikey_decelerate();
-    }
-#endif
-
-#ifdef JOYSTICK_ENABLE
-    joystick_task();
-#endif
-
-    // update LED
-    if (led_status != host_keyboard_leds()) {
-        led_status = host_keyboard_leds();
-        keyboard_set_leds(led_status);
-    }
-}
-
-/** \brief keyboard set leds
- *
- * FIXME: needs doc
- */
-void keyboard_set_leds(uint8_t leds) {
-    if (debug_keyboard) {
-        debug("keyboard_set_led: ");
-        debug_hex8(leds);
-        debug("\n");
-    }
-    led_set(leds);
-}
diff --git a/tmk_core/common/keyboard.h b/tmk_core/common/keyboard.h
deleted file mode 100644
index 08f4e84f94..0000000000
--- a/tmk_core/common/keyboard.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-Copyright 2011,2012,2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* key matrix position */
-typedef struct {
-    uint8_t col;
-    uint8_t row;
-} keypos_t;
-
-/* key event */
-typedef struct {
-    keypos_t key;
-    bool     pressed;
-    uint16_t time;
-} keyevent_t;
-
-/* equivalent test of keypos_t */
-#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col)
-
-/* Rules for No Event:
- * 1) (time == 0) to handle (keyevent_t){} as empty event
- * 2) Matrix(255, 255) to make TICK event available
- */
-static inline bool IS_NOEVENT(keyevent_t event) { return event.time == 0 || (event.key.row == 255 && event.key.col == 255); }
-static inline bool IS_PRESSED(keyevent_t event) { return (!IS_NOEVENT(event) && event.pressed); }
-static inline bool IS_RELEASED(keyevent_t event) { return (!IS_NOEVENT(event) && !event.pressed); }
-
-/* Tick event */
-#define TICK \
-    (keyevent_t) { .key = (keypos_t){.row = 255, .col = 255}, .pressed = false, .time = (timer_read() | 1) }
-
-/* it runs once at early stage of startup before keyboard_init. */
-void keyboard_setup(void);
-/* it runs once after initializing host side protocol, debug and MCU peripherals. */
-void keyboard_init(void);
-/* it runs repeatedly in main loop */
-void keyboard_task(void);
-/* it runs when host LED status is updated */
-void keyboard_set_leds(uint8_t leds);
-/* it runs whenever code has to behave differently on a slave */
-bool is_keyboard_master(void);
-/* it runs whenever code has to behave differently on left vs right split */
-bool is_keyboard_left(void);
-
-void keyboard_pre_init_kb(void);
-void keyboard_pre_init_user(void);
-void keyboard_post_init_kb(void);
-void keyboard_post_init_user(void);
-
-void housekeeping_task(void);       // To be executed by the main loop in each backend TMK protocol
-void housekeeping_task_kb(void);    // To be overridden by keyboard-level code
-void housekeeping_task_user(void);  // To be overridden by user/keymap-level code
-
-uint32_t last_input_activity_time(void);     // Timestamp of the last matrix or encoder activity
-uint32_t last_input_activity_elapsed(void);  // Number of milliseconds since the last matrix or encoder activity
-
-uint32_t last_matrix_activity_time(void);     // Timestamp of the last matrix activity
-uint32_t last_matrix_activity_elapsed(void);  // Number of milliseconds since the last matrix activity
-
-uint32_t last_encoder_activity_time(void);     // Timestamp of the last encoder activity
-uint32_t last_encoder_activity_elapsed(void);  // Number of milliseconds since the last encoder activity
-
-uint32_t get_matrix_scan_rate(void);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/tmk_core/common/keycode.h b/tmk_core/common/keycode.h
deleted file mode 100644
index 8facabd818..0000000000
--- a/tmk_core/common/keycode.h
+++ /dev/null
@@ -1,560 +0,0 @@
-/*
-Copyright 2011,2012 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/*
- * Keycodes based on HID Keyboard/Keypad Usage Page (0x07) plus media keys from Generic Desktop Page (0x01) and Consumer Page (0x0C)
- *
- * See https://web.archive.org/web/20060218214400/http://www.usb.org/developers/devclass_docs/Hut1_12.pdf
- * or http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (older)
- */
-
-#pragma once
-
-/* FIXME: Add doxygen comments here */
-
-#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED)
-#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF)
-#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL)
-#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI)
-
-#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF))
-#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE)
-#define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_BRID)
-
-#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31)
-
-#define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2)
-#define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT)
-#define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN8)
-#define IS_MOUSEKEY_WHEEL(code) (KC_MS_WH_UP <= (code) && (code) <= KC_MS_WH_RIGHT)
-#define IS_MOUSEKEY_ACCEL(code) (KC_MS_ACCEL0 <= (code) && (code) <= KC_MS_ACCEL2)
-
-#define MOD_BIT(code) (1 << MOD_INDEX(code))
-#define MOD_INDEX(code) ((code)&0x07)
-
-#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL))
-#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))
-#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT))
-#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI))
-#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT)
-#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT)
-#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI)
-#define MOD_MASK_SA (MOD_MASK_SHIFT | MOD_MASK_ALT)
-#define MOD_MASK_SG (MOD_MASK_SHIFT | MOD_MASK_GUI)
-#define MOD_MASK_AG (MOD_MASK_ALT | MOD_MASK_GUI)
-#define MOD_MASK_CSA (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT)
-#define MOD_MASK_CSG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_GUI)
-#define MOD_MASK_CAG (MOD_MASK_CTRL | MOD_MASK_ALT | MOD_MASK_GUI)
-#define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)
-#define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)
-
-#define FN_BIT(code) (1 << FN_INDEX(code))
-#define FN_INDEX(code) ((code)-KC_FN0)
-#define FN_MIN KC_FN0
-#define FN_MAX KC_FN31
-
-/*
- * Short names for ease of definition of keymap
- */
-/* Transparent */
-#define KC_TRANSPARENT 0x01
-#define KC_TRNS KC_TRANSPARENT
-
-/* Punctuation */
-#define KC_ENT KC_ENTER
-#define KC_ESC KC_ESCAPE
-#define KC_BSPC KC_BSPACE
-#define KC_SPC KC_SPACE
-#define KC_MINS KC_MINUS
-#define KC_EQL KC_EQUAL
-#define KC_LBRC KC_LBRACKET
-#define KC_RBRC KC_RBRACKET
-#define KC_BSLS KC_BSLASH
-#define KC_NUHS KC_NONUS_HASH
-#define KC_SCLN KC_SCOLON
-#define KC_QUOT KC_QUOTE
-#define KC_GRV KC_GRAVE
-#define KC_COMM KC_COMMA
-#define KC_SLSH KC_SLASH
-#define KC_NUBS KC_NONUS_BSLASH
-
-/* Lock Keys */
-#define KC_CLCK KC_CAPSLOCK
-#define KC_CAPS KC_CAPSLOCK
-#define KC_SLCK KC_SCROLLLOCK
-#define KC_NLCK KC_NUMLOCK
-#define KC_LCAP KC_LOCKING_CAPS
-#define KC_LNUM KC_LOCKING_NUM
-#define KC_LSCR KC_LOCKING_SCROLL
-
-/* Commands */
-#define KC_PSCR KC_PSCREEN
-#define KC_PAUS KC_PAUSE
-#define KC_BRK KC_PAUSE
-#define KC_INS KC_INSERT
-#define KC_DEL KC_DELETE
-#define KC_PGDN KC_PGDOWN
-#define KC_RGHT KC_RIGHT
-#define KC_APP KC_APPLICATION
-#define KC_EXEC KC_EXECUTE
-#define KC_SLCT KC_SELECT
-#define KC_AGIN KC_AGAIN
-#define KC_PSTE KC_PASTE
-#define KC_ERAS KC_ALT_ERASE
-#define KC_CLR KC_CLEAR
-
-/* Keypad */
-#define KC_PSLS KC_KP_SLASH
-#define KC_PAST KC_KP_ASTERISK
-#define KC_PMNS KC_KP_MINUS
-#define KC_PPLS KC_KP_PLUS
-#define KC_PENT KC_KP_ENTER
-#define KC_P1 KC_KP_1
-#define KC_P2 KC_KP_2
-#define KC_P3 KC_KP_3
-#define KC_P4 KC_KP_4
-#define KC_P5 KC_KP_5
-#define KC_P6 KC_KP_6
-#define KC_P7 KC_KP_7
-#define KC_P8 KC_KP_8
-#define KC_P9 KC_KP_9
-#define KC_P0 KC_KP_0
-#define KC_PDOT KC_KP_DOT
-#define KC_PEQL KC_KP_EQUAL
-#define KC_PCMM KC_KP_COMMA
-
-/* Japanese specific */
-#define KC_ZKHK KC_GRAVE
-#define KC_RO KC_INT1
-#define KC_KANA KC_INT2
-#define KC_JYEN KC_INT3
-#define KC_HENK KC_INT4
-#define KC_MHEN KC_INT5
-
-/* Korean specific */
-#define KC_HAEN KC_LANG1
-#define KC_HANJ KC_LANG2
-
-/* Modifiers */
-#define KC_LCTL KC_LCTRL
-#define KC_LSFT KC_LSHIFT
-#define KC_LOPT KC_LALT
-#define KC_LCMD KC_LGUI
-#define KC_LWIN KC_LGUI
-#define KC_RCTL KC_RCTRL
-#define KC_RSFT KC_RSHIFT
-#define KC_ALGR KC_RALT
-#define KC_ROPT KC_RALT
-#define KC_RCMD KC_RGUI
-#define KC_RWIN KC_RGUI
-
-/* Generic Desktop Page (0x01) */
-#define KC_PWR KC_SYSTEM_POWER
-#define KC_SLEP KC_SYSTEM_SLEEP
-#define KC_WAKE KC_SYSTEM_WAKE
-
-/* Consumer Page (0x0C) */
-#define KC_MUTE KC_AUDIO_MUTE
-#define KC_VOLU KC_AUDIO_VOL_UP
-#define KC_VOLD KC_AUDIO_VOL_DOWN
-#define KC_MNXT KC_MEDIA_NEXT_TRACK
-#define KC_MPRV KC_MEDIA_PREV_TRACK
-#define KC_MSTP KC_MEDIA_STOP
-#define KC_MPLY KC_MEDIA_PLAY_PAUSE
-#define KC_MSEL KC_MEDIA_SELECT
-#define KC_EJCT KC_MEDIA_EJECT
-#define KC_CALC KC_CALCULATOR
-#define KC_MYCM KC_MY_COMPUTER
-#define KC_WSCH KC_WWW_SEARCH
-#define KC_WHOM KC_WWW_HOME
-#define KC_WBAK KC_WWW_BACK
-#define KC_WFWD KC_WWW_FORWARD
-#define KC_WSTP KC_WWW_STOP
-#define KC_WREF KC_WWW_REFRESH
-#define KC_WFAV KC_WWW_FAVORITES
-#define KC_MFFD KC_MEDIA_FAST_FORWARD
-#define KC_MRWD KC_MEDIA_REWIND
-#define KC_BRIU KC_BRIGHTNESS_UP
-#define KC_BRID KC_BRIGHTNESS_DOWN
-
-/* System Specific */
-#define KC_BRMU KC_PAUSE
-#define KC_BRMD KC_SCROLLLOCK
-
-/* Mouse Keys */
-#define KC_MS_U KC_MS_UP
-#define KC_MS_D KC_MS_DOWN
-#define KC_MS_L KC_MS_LEFT
-#define KC_MS_R KC_MS_RIGHT
-#define KC_BTN1 KC_MS_BTN1
-#define KC_BTN2 KC_MS_BTN2
-#define KC_BTN3 KC_MS_BTN3
-#define KC_BTN4 KC_MS_BTN4
-#define KC_BTN5 KC_MS_BTN5
-#define KC_BTN6 KC_MS_BTN6
-#define KC_BTN7 KC_MS_BTN7
-#define KC_BTN8 KC_MS_BTN8
-#define KC_WH_U KC_MS_WH_UP
-#define KC_WH_D KC_MS_WH_DOWN
-#define KC_WH_L KC_MS_WH_LEFT
-#define KC_WH_R KC_MS_WH_RIGHT
-#define KC_ACL0 KC_MS_ACCEL0
-#define KC_ACL1 KC_MS_ACCEL1
-#define KC_ACL2 KC_MS_ACCEL2
-
-/* Keyboard/Keypad Page (0x07) */
-enum hid_keyboard_keypad_usage {
-    KC_NO = 0x00,
-    KC_ROLL_OVER,
-    KC_POST_FAIL,
-    KC_UNDEFINED,
-    KC_A,
-    KC_B,
-    KC_C,
-    KC_D,
-    KC_E,
-    KC_F,
-    KC_G,
-    KC_H,
-    KC_I,
-    KC_J,
-    KC_K,
-    KC_L,
-    KC_M,  // 0x10
-    KC_N,
-    KC_O,
-    KC_P,
-    KC_Q,
-    KC_R,
-    KC_S,
-    KC_T,
-    KC_U,
-    KC_V,
-    KC_W,
-    KC_X,
-    KC_Y,
-    KC_Z,
-    KC_1,
-    KC_2,
-    KC_3,  // 0x20
-    KC_4,
-    KC_5,
-    KC_6,
-    KC_7,
-    KC_8,
-    KC_9,
-    KC_0,
-    KC_ENTER,
-    KC_ESCAPE,
-    KC_BSPACE,
-    KC_TAB,
-    KC_SPACE,
-    KC_MINUS,
-    KC_EQUAL,
-    KC_LBRACKET,
-    KC_RBRACKET,  // 0x30
-    KC_BSLASH,
-    KC_NONUS_HASH,
-    KC_SCOLON,
-    KC_QUOTE,
-    KC_GRAVE,
-    KC_COMMA,
-    KC_DOT,
-    KC_SLASH,
-    KC_CAPSLOCK,
-    KC_F1,
-    KC_F2,
-    KC_F3,
-    KC_F4,
-    KC_F5,
-    KC_F6,
-    KC_F7,  // 0x40
-    KC_F8,
-    KC_F9,
-    KC_F10,
-    KC_F11,
-    KC_F12,
-    KC_PSCREEN,
-    KC_SCROLLLOCK,
-    KC_PAUSE,
-    KC_INSERT,
-    KC_HOME,
-    KC_PGUP,
-    KC_DELETE,
-    KC_END,
-    KC_PGDOWN,
-    KC_RIGHT,
-    KC_LEFT,  // 0x50
-    KC_DOWN,
-    KC_UP,
-    KC_NUMLOCK,
-    KC_KP_SLASH,
-    KC_KP_ASTERISK,
-    KC_KP_MINUS,
-    KC_KP_PLUS,
-    KC_KP_ENTER,
-    KC_KP_1,
-    KC_KP_2,
-    KC_KP_3,
-    KC_KP_4,
-    KC_KP_5,
-    KC_KP_6,
-    KC_KP_7,
-    KC_KP_8,  // 0x60
-    KC_KP_9,
-    KC_KP_0,
-    KC_KP_DOT,
-    KC_NONUS_BSLASH,
-    KC_APPLICATION,
-    KC_POWER,
-    KC_KP_EQUAL,
-    KC_F13,
-    KC_F14,
-    KC_F15,
-    KC_F16,
-    KC_F17,
-    KC_F18,
-    KC_F19,
-    KC_F20,
-    KC_F21,  // 0x70
-    KC_F22,
-    KC_F23,
-    KC_F24,
-    KC_EXECUTE,
-    KC_HELP,
-    KC_MENU,
-    KC_SELECT,
-    KC_STOP,
-    KC_AGAIN,
-    KC_UNDO,
-    KC_CUT,
-    KC_COPY,
-    KC_PASTE,
-    KC_FIND,
-    KC__MUTE,
-    KC__VOLUP,  // 0x80
-    KC__VOLDOWN,
-    KC_LOCKING_CAPS,
-    KC_LOCKING_NUM,
-    KC_LOCKING_SCROLL,
-    KC_KP_COMMA,
-    KC_KP_EQUAL_AS400,
-    KC_INT1,
-    KC_INT2,
-    KC_INT3,
-    KC_INT4,
-    KC_INT5,
-    KC_INT6,
-    KC_INT7,
-    KC_INT8,
-    KC_INT9,
-    KC_LANG1,  // 0x90
-    KC_LANG2,
-    KC_LANG3,
-    KC_LANG4,
-    KC_LANG5,
-    KC_LANG6,
-    KC_LANG7,
-    KC_LANG8,
-    KC_LANG9,
-    KC_ALT_ERASE,
-    KC_SYSREQ,
-    KC_CANCEL,
-    KC_CLEAR,
-    KC_PRIOR,
-    KC_RETURN,
-    KC_SEPARATOR,
-    KC_OUT,  // 0xA0
-    KC_OPER,
-    KC_CLEAR_AGAIN,
-    KC_CRSEL,
-    KC_EXSEL,
-
-#if 0
-  // ***************************************************************
-  // These keycodes are present in the HID spec, but are           *
-  // nonfunctional on modern OSes. QMK uses this range (0xA5-0xDF) *
-  // for the media and function keys instead - see below.          *
-  // ***************************************************************
-
-  KC_KP_00                = 0xB0,
-  KC_KP_000,
-  KC_THOUSANDS_SEPARATOR,
-  KC_DECIMAL_SEPARATOR,
-  KC_CURRENCY_UNIT,
-  KC_CURRENCY_SUB_UNIT,
-  KC_KP_LPAREN,
-  KC_KP_RPAREN,
-  KC_KP_LCBRACKET,
-  KC_KP_RCBRACKET,
-  KC_KP_TAB,
-  KC_KP_BSPACE,
-  KC_KP_A,
-  KC_KP_B,
-  KC_KP_C,
-  KC_KP_D,
-  KC_KP_E,                //0xC0
-  KC_KP_F,
-  KC_KP_XOR,
-  KC_KP_HAT,
-  KC_KP_PERC,
-  KC_KP_LT,
-  KC_KP_GT,
-  KC_KP_AND,
-  KC_KP_LAZYAND,
-  KC_KP_OR,
-  KC_KP_LAZYOR,
-  KC_KP_COLON,
-  KC_KP_HASH,
-  KC_KP_SPACE,
-  KC_KP_ATMARK,
-  KC_KP_EXCLAMATION,
-  KC_KP_MEM_STORE,        //0xD0
-  KC_KP_MEM_RECALL,
-  KC_KP_MEM_CLEAR,
-  KC_KP_MEM_ADD,
-  KC_KP_MEM_SUB,
-  KC_KP_MEM_MUL,
-  KC_KP_MEM_DIV,
-  KC_KP_PLUS_MINUS,
-  KC_KP_CLEAR,
-  KC_KP_CLEAR_ENTRY,
-  KC_KP_BINARY,
-  KC_KP_OCTAL,
-  KC_KP_DECIMAL,
-  KC_KP_HEXADECIMAL,
-#endif
-
-    /* Modifiers */
-    KC_LCTRL = 0xE0,
-    KC_LSHIFT,
-    KC_LALT,
-    KC_LGUI,
-    KC_RCTRL,
-    KC_RSHIFT,
-    KC_RALT,
-    KC_RGUI
-
-    // **********************************************
-    // * 0xF0-0xFF are unallocated in the HID spec. *
-    // * QMK uses these for Mouse Keys - see below. *
-    // **********************************************
-};
-
-/* Media and Function keys */
-enum internal_special_keycodes {
-    /* Generic Desktop Page (0x01) */
-    KC_SYSTEM_POWER = 0xA5,
-    KC_SYSTEM_SLEEP,
-    KC_SYSTEM_WAKE,
-
-    /* Consumer Page (0x0C) */
-    KC_AUDIO_MUTE,
-    KC_AUDIO_VOL_UP,
-    KC_AUDIO_VOL_DOWN,
-    KC_MEDIA_NEXT_TRACK,
-    KC_MEDIA_PREV_TRACK,
-    KC_MEDIA_STOP,
-    KC_MEDIA_PLAY_PAUSE,
-    KC_MEDIA_SELECT,
-    KC_MEDIA_EJECT,  // 0xB0
-    KC_MAIL,
-    KC_CALCULATOR,
-    KC_MY_COMPUTER,
-    KC_WWW_SEARCH,
-    KC_WWW_HOME,
-    KC_WWW_BACK,
-    KC_WWW_FORWARD,
-    KC_WWW_STOP,
-    KC_WWW_REFRESH,
-    KC_WWW_FAVORITES,
-    KC_MEDIA_FAST_FORWARD,
-    KC_MEDIA_REWIND,
-    KC_BRIGHTNESS_UP,
-    KC_BRIGHTNESS_DOWN,
-
-    /* Fn keys */
-    KC_FN0 = 0xC0,
-    KC_FN1,
-    KC_FN2,
-    KC_FN3,
-    KC_FN4,
-    KC_FN5,
-    KC_FN6,
-    KC_FN7,
-    KC_FN8,
-    KC_FN9,
-    KC_FN10,
-    KC_FN11,
-    KC_FN12,
-    KC_FN13,
-    KC_FN14,
-    KC_FN15,
-    KC_FN16,  // 0xD0
-    KC_FN17,
-    KC_FN18,
-    KC_FN19,
-    KC_FN20,
-    KC_FN21,
-    KC_FN22,
-    KC_FN23,
-    KC_FN24,
-    KC_FN25,
-    KC_FN26,
-    KC_FN27,
-    KC_FN28,
-    KC_FN29,
-    KC_FN30,
-    KC_FN31
-};
-
-enum mouse_keys {
-/* Mouse Buttons */
-#ifdef VIA_ENABLE
-    KC_MS_UP = 0xF0,
-#else
-    KC_MS_UP = 0xED,
-#endif
-    KC_MS_DOWN,
-    KC_MS_LEFT,
-    KC_MS_RIGHT,  // 0xF0
-    KC_MS_BTN1,
-    KC_MS_BTN2,
-    KC_MS_BTN3,
-    KC_MS_BTN4,
-    KC_MS_BTN5,
-#ifdef VIA_ENABLE
-    KC_MS_BTN6 = KC_MS_BTN5,
-    KC_MS_BTN7 = KC_MS_BTN5,
-    KC_MS_BTN8 = KC_MS_BTN5,
-#else
-    KC_MS_BTN6,
-    KC_MS_BTN7,
-    KC_MS_BTN8,
-#endif
-
-    /* Mouse Wheel */
-    KC_MS_WH_UP,
-    KC_MS_WH_DOWN,
-    KC_MS_WH_LEFT,
-    KC_MS_WH_RIGHT,
-
-    /* Acceleration */
-    KC_MS_ACCEL0,
-    KC_MS_ACCEL1,
-    KC_MS_ACCEL2  // 0xFF
-};
diff --git a/tmk_core/common/lib_printf.mk b/tmk_core/common/lib_printf.mk
deleted file mode 100644
index 10d2d8468d..0000000000
--- a/tmk_core/common/lib_printf.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-PRINTF_PATH = $(LIB_PATH)/printf
-
-TMK_COMMON_SRC += $(PRINTF_PATH)/printf.c
-TMK_COMMON_SRC += $(COMMON_DIR)/printf.c
-TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_FLOAT
-TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_EXPONENTIAL
-TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_LONG_LONG
-TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_PTRDIFF_T
-VPATH += $(PRINTF_PATH)
diff --git a/tmk_core/common/nodebug.h b/tmk_core/common/nodebug.h
deleted file mode 100644
index 0b176684bd..0000000000
--- a/tmk_core/common/nodebug.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-Copyright 2013 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-#ifndef NO_DEBUG
-#    define NO_DEBUG
-#    include "debug.h"
-#    undef NO_DEBUG
-#else
-#    include "debug.h"
-#endif
diff --git a/tmk_core/common/print.h b/tmk_core/common/print.h
deleted file mode 100644
index 8c055f549e..0000000000
--- a/tmk_core/common/print.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/* Copyright 2012 Jun Wako <wakojun@gmail.com> */
-/* Very basic print functions, intended to be used with usb_debug_only.c
- * http://www.pjrc.com/teensy/
- * Copyright (c) 2008 PJRC.COM, LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "util.h"
-#include "sendchar.h"
-#include "progmem.h"
-
-void print_set_sendchar(sendchar_func_t func);
-
-#ifndef NO_PRINT
-#    if __has_include_next("_print.h")
-#        include_next "_print.h" /* Include the platforms print.h */
-#    else
-// Fall back to lib/printf
-#        include "printf.h"  // lib/printf/printf.h
-
-// Create user & normal print defines
-#        define print(s) printf(s)
-#        define println(s) printf(s "\r\n")
-#        define xprintf printf
-#        define uprint(s) printf(s)
-#        define uprintln(s) printf(s "\r\n")
-#        define uprintf printf
-
-#    endif /* __has_include_next("_print.h") */
-#else      /* NO_PRINT */
-#    undef xprintf
-// Remove print defines
-#    define print(s)
-#    define println(s)
-#    define xprintf(fmt, ...)
-#    define uprintf(fmt, ...)
-#    define uprint(s)
-#    define uprintln(s)
-
-#endif /* NO_PRINT */
-
-#ifdef USER_PRINT
-// Remove normal print defines
-#    undef print
-#    undef println
-#    undef xprintf
-#    define print(s)
-#    define println(s)
-#    define xprintf(fmt, ...)
-#endif
-
-#define print_dec(i) xprintf("%u", i)
-#define print_decs(i) xprintf("%d", i)
-/* hex */
-#define print_hex4(i) xprintf("%X", i)
-#define print_hex8(i) xprintf("%02X", i)
-#define print_hex16(i) xprintf("%04X", i)
-#define print_hex32(i) xprintf("%08lX", i)
-/* binary */
-#define print_bin4(i) xprintf("%04b", i)
-#define print_bin8(i) xprintf("%08b", i)
-#define print_bin16(i) xprintf("%016b", i)
-#define print_bin32(i) xprintf("%032lb", i)
-#define print_bin_reverse8(i) xprintf("%08b", bitrev(i))
-#define print_bin_reverse16(i) xprintf("%016b", bitrev16(i))
-#define print_bin_reverse32(i) xprintf("%032lb", bitrev32(i))
-/* print value utility */
-#define print_val_dec(v) xprintf(#v ": %u\n", v)
-#define print_val_decs(v) xprintf(#v ": %d\n", v)
-#define print_val_hex8(v) xprintf(#v ": %X\n", v)
-#define print_val_hex16(v) xprintf(#v ": %02X\n", v)
-#define print_val_hex32(v) xprintf(#v ": %04lX\n", v)
-#define print_val_bin8(v) xprintf(#v ": %08b\n", v)
-#define print_val_bin16(v) xprintf(#v ": %016b\n", v)
-#define print_val_bin32(v) xprintf(#v ": %032lb\n", v)
-#define print_val_bin_reverse8(v) xprintf(#v ": %08b\n", bitrev(v))
-#define print_val_bin_reverse16(v) xprintf(#v ": %016b\n", bitrev16(v))
-#define print_val_bin_reverse32(v) xprintf(#v ": %032lb\n", bitrev32(v))
-
-// User print disables the normal print messages in the body of QMK/TMK code and
-// is meant as a lightweight alternative to NOPRINT. Use it when you only want to do
-// a spot of debugging but lack flash resources for allowing all of the codebase to
-// print (and store their wasteful strings).
-//
-// !!! DO NOT USE USER PRINT CALLS IN THE BODY OF QMK/TMK !!!
-
-/* decimal */
-#define uprint_dec(i) uprintf("%u", i)
-#define uprint_decs(i) uprintf("%d", i)
-/* hex */
-#define uprint_hex4(i) uprintf("%X", i)
-#define uprint_hex8(i) uprintf("%02X", i)
-#define uprint_hex16(i) uprintf("%04X", i)
-#define uprint_hex32(i) uprintf("%08lX", i)
-/* binary */
-#define uprint_bin4(i) uprintf("%04b", i)
-#define uprint_bin8(i) uprintf("%08b", i)
-#define uprint_bin16(i) uprintf("%016b", i)
-#define uprint_bin32(i) uprintf("%032lb", i)
-#define uprint_bin_reverse8(i) uprintf("%08b", bitrev(i))
-#define uprint_bin_reverse16(i) uprintf("%016b", bitrev16(i))
-#define uprint_bin_reverse32(i) uprintf("%032lb", bitrev32(i))
-/* print value utility */
-#define uprint_val_dec(v) uprintf(#v ": %u\n", v)
-#define uprint_val_decs(v) uprintf(#v ": %d\n", v)
-#define uprint_val_hex8(v) uprintf(#v ": %X\n", v)
-#define uprint_val_hex16(v) uprintf(#v ": %02X\n", v)
-#define uprint_val_hex32(v) uprintf(#v ": %04lX\n", v)
-#define uprint_val_bin8(v) uprintf(#v ": %08b\n", v)
-#define uprint_val_bin16(v) uprintf(#v ": %016b\n", v)
-#define uprint_val_bin32(v) uprintf(#v ": %032lb\n", v)
-#define uprint_val_bin_reverse8(v) uprintf(#v ": %08b\n", bitrev(v))
-#define uprint_val_bin_reverse16(v) uprintf(#v ": %016b\n", bitrev16(v))
-#define uprint_val_bin_reverse32(v) uprintf(#v ": %032lb\n", bitrev32(v))
diff --git a/tmk_core/common/printf.c b/tmk_core/common/printf.c
deleted file mode 100644
index e8440e55ee..0000000000
--- a/tmk_core/common/printf.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-Copyright 2011 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-#include <stddef.h>
-#include "sendchar.h"
-
-// bind lib/printf to console interface - sendchar
-
-static int8_t          null_sendchar_func(uint8_t c) { return 0; }
-static sendchar_func_t func = null_sendchar_func;
-
-void print_set_sendchar(sendchar_func_t send) { func = send; }
-
-void _putchar(char character) { func(character); }
diff --git a/tmk_core/common/progmem.h b/tmk_core/common/progmem.h
index 4e4771e523..a70d8e299f 100644
--- a/tmk_core/common/progmem.h
+++ b/tmk_core/common/progmem.h
@@ -3,7 +3,9 @@
 #if defined(__AVR__)
 #    include <avr/pgmspace.h>
 #else
+#    include <string.h>
 #    define PROGMEM
+#    define __flash
 #    define PSTR(x) x
 #    define PGM_P const char*
 #    define memcpy_P(dest, src, n) memcpy(dest, src, n)
diff --git a/tmk_core/common/report.h b/tmk_core/common/report.h
index db6370657d..f2223e8063 100644
--- a/tmk_core/common/report.h
+++ b/tmk_core/common/report.h
@@ -30,7 +30,8 @@ enum hid_report_ids {
     REPORT_ID_SYSTEM,
     REPORT_ID_CONSUMER,
     REPORT_ID_NKRO,
-    REPORT_ID_JOYSTICK
+    REPORT_ID_JOYSTICK,
+    REPORT_ID_DIGITIZER
 };
 
 /* Mouse buttons */
@@ -206,6 +207,17 @@ typedef struct {
 } __attribute__((packed)) report_mouse_t;
 
 typedef struct {
+#ifdef DIGITIZER_SHARED_EP
+    uint8_t report_id;
+#endif
+    uint8_t  tip : 1;
+    uint8_t  inrange : 1;
+    uint8_t  pad2 : 6;
+    uint16_t x;
+    uint16_t y;
+} __attribute__((packed)) report_digitizer_t;
+
+typedef struct {
 #if JOYSTICK_AXES_COUNT > 0
 #    if JOYSTICK_AXES_RESOLUTION > 8
     int16_t axes[JOYSTICK_AXES_COUNT];
diff --git a/tmk_core/common/sendchar.h b/tmk_core/common/sendchar.h
deleted file mode 100644
index edcddaa6bb..0000000000
--- a/tmk_core/common/sendchar.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-Copyright 2011 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef int8_t (*sendchar_func_t)(uint8_t c);
-
-/* transmit a character.  return 0 on success, -1 on error. */
-int8_t sendchar(uint8_t c);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/tmk_core/common/sendchar_null.c b/tmk_core/common/sendchar_null.c
deleted file mode 100644
index fb67f70866..0000000000
--- a/tmk_core/common/sendchar_null.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-Copyright 2011 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-#include "sendchar.h"
-
-__attribute__((weak)) int8_t sendchar(uint8_t c) { return 0; }
diff --git a/tmk_core/common/sendchar_uart.c b/tmk_core/common/sendchar_uart.c
deleted file mode 100644
index 2fc48bafff..0000000000
--- a/tmk_core/common/sendchar_uart.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-Copyright 2011 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-#include "uart.h"
-#include "sendchar.h"
-
-int8_t sendchar(uint8_t c) {
-    uart_putchar(c);
-    return 0;
-}
diff --git a/tmk_core/common/sync_timer.c b/tmk_core/common/sync_timer.c
index de24b463b6..68b92d8b43 100644
--- a/tmk_core/common/sync_timer.c
+++ b/tmk_core/common/sync_timer.c
@@ -26,7 +26,7 @@ SOFTWARE.
 #include "sync_timer.h"
 #include "keyboard.h"
 
-#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER)
+#if (defined(SPLIT_KEYBOARD) || defined(SERIAL_LINK_ENABLE)) && !defined(DISABLE_SYNC_TIMER)
 volatile int32_t sync_timer_ms;
 
 void sync_timer_init(void) { sync_timer_ms = 0; }
diff --git a/tmk_core/common/sync_timer.h b/tmk_core/common/sync_timer.h
index 9ddef45bb2..744e2b50d5 100644
--- a/tmk_core/common/sync_timer.h
+++ b/tmk_core/common/sync_timer.h
@@ -32,7 +32,7 @@ SOFTWARE.
 extern "C" {
 #endif
 
-#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER)
+#if (defined(SPLIT_KEYBOARD) || defined(SERIAL_LINK_ENABLE)) && !defined(DISABLE_SYNC_TIMER)
 void     sync_timer_init(void);
 void     sync_timer_update(uint32_t time);
 uint16_t sync_timer_read(void);
diff --git a/tmk_core/common/test/eeprom_stm32_tests.cpp b/tmk_core/common/test/eeprom_stm32_tests.cpp
new file mode 100644
index 0000000000..aa84492b87
--- /dev/null
+++ b/tmk_core/common/test/eeprom_stm32_tests.cpp
@@ -0,0 +1,438 @@
+/* Copyright 2021 by Don Kjer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtest/gtest.h"
+
+extern "C" {
+#include "flash_stm32.h"
+#include "eeprom_stm32.h"
+#include "eeprom.h"
+}
+
+/* Mock Flash Parameters:
+ *
+ * === Large Layout ===
+ * flash size: 65536
+ * page size: 2048
+ * density pages: 16
+ * Simulated EEPROM size: 16384
+ *
+ * FlashBuf Layout:
+ * [Unused | Compact |  Write Log  ]
+ * [0......|32768......|49152......65535]
+ *
+ * === Tiny Layout ===
+ * flash size: 1024
+ * page size: 512
+ * density pages: 1
+ * Simulated EEPROM size: 256
+ *
+ * FlashBuf Layout:
+ * [Unused | Compact |  Write Log  ]
+ * [0......|512......|768......1023]
+ *
+ */
+
+#define EEPROM_SIZE (FEE_PAGE_SIZE * FEE_DENSITY_PAGES / 2)
+#define LOG_SIZE EEPROM_SIZE
+#define LOG_BASE (MOCK_FLASH_SIZE - LOG_SIZE)
+#define EEPROM_BASE (LOG_BASE - EEPROM_SIZE)
+
+/* Log encoding helpers */
+#define BYTE_VALUE(addr, value) (((addr) << 8) | (value))
+#define WORD_ZERO(addr) (0x8000 | ((addr) >> 1))
+#define WORD_ONE(addr) (0xA000 | ((addr) >> 1))
+#define WORD_NEXT(addr) (0xE000 | (((addr)-0x80) >> 1))
+
+class EepromStm32Test : public testing::Test {
+   public:
+    EepromStm32Test() {}
+    ~EepromStm32Test() {}
+
+   protected:
+    void SetUp() override { EEPROM_Erase(); }
+
+    void TearDown() override {
+#ifdef EEPROM_DEBUG
+        dumpEepromDataBuf();
+#endif
+    }
+};
+
+TEST_F(EepromStm32Test, TestErase) {
+    EEPROM_WriteDataByte(0, 0x42);
+    EEPROM_Erase();
+    EXPECT_EQ(EEPROM_ReadDataByte(0), 0);
+    EXPECT_EQ(EEPROM_ReadDataByte(1), 0);
+}
+
+TEST_F(EepromStm32Test, TestReadGarbage) {
+    uint8_t garbage = 0x3c;
+    for (int i = 0; i < MOCK_FLASH_SIZE; ++i) {
+        garbage ^= 0xa3;
+        garbage += i;
+        FlashBuf[i] = garbage;
+    }
+    EEPROM_Init();  // Just verify we don't crash
+}
+
+TEST_F(EepromStm32Test, TestWriteBadAddress) {
+    EXPECT_EQ(EEPROM_WriteDataByte(EEPROM_SIZE, 0x42), FLASH_BAD_ADDRESS);
+    EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE - 1, 0xbeef), FLASH_BAD_ADDRESS);
+    EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE, 0xbeef), FLASH_BAD_ADDRESS);
+}
+
+TEST_F(EepromStm32Test, TestReadBadAddress) {
+    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE), 0xFF);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 1), 0xFFFF);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE), 0xFFFF);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 3)), 0xFF000000);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)EEPROM_SIZE), 0xFFFFFFFF);
+}
+
+TEST_F(EepromStm32Test, TestReadByte) {
+    /* Direct compacted-area baseline: Address < 0x80 */
+    FlashBuf[EEPROM_BASE + 2] = ~0xef;
+    FlashBuf[EEPROM_BASE + 3] = ~0xbe;
+    /* Direct compacted-area baseline: Address >= 0x80 */
+    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78;
+    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56;
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef);
+    EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe);
+    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78);
+    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56);
+    /* Write Log byte value */
+    FlashBuf[LOG_BASE]     = 0x65;
+    FlashBuf[LOG_BASE + 1] = 3;
+    /* Write Log word value */
+    *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_NEXT(EEPROM_SIZE - 2);
+    *(uint16_t*)&FlashBuf[LOG_BASE + 4] = ~0x9abc;
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef);
+    EXPECT_EQ(EEPROM_ReadDataByte(3), 0x65);
+    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0xbc);
+    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x9a);
+}
+
+TEST_F(EepromStm32Test, TestWriteByte) {
+    /* Direct compacted-area baseline: Address < 0x80 */
+    EEPROM_WriteDataByte(2, 0xef);
+    EEPROM_WriteDataByte(3, 0xbe);
+    /* Direct compacted-area baseline: Address >= 0x80 */
+    EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78);
+    EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56);
+    /* Check values */
+    /* First write in each aligned word should have been direct */
+    EXPECT_EQ(FlashBuf[EEPROM_BASE + 2], (uint8_t)~0xef);
+    EXPECT_EQ(FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint8_t)~0x78);
+
+    /* Second write per aligned word requires a log entry */
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(3, 0xbe));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(EEPROM_SIZE - 1));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0x5678);
+}
+
+TEST_F(EepromStm32Test, TestByteRoundTrip) {
+    /* Direct compacted-area: Address < 0x80 */
+    EEPROM_WriteDataWord(0, 0xdead);
+    EEPROM_WriteDataByte(2, 0xef);
+    EEPROM_WriteDataByte(3, 0xbe);
+    /* Direct compacted-area: Address >= 0x80 */
+    EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78);
+    EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56);
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataByte(0), 0xad);
+    EXPECT_EQ(EEPROM_ReadDataByte(1), 0xde);
+    EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef);
+    EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe);
+    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78);
+    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56);
+    /* Write log entries */
+    EEPROM_WriteDataByte(2, 0x80);
+    EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x3c);
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataByte(2), 0x80);
+    EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe);
+    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x3c);
+    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56);
+}
+
+TEST_F(EepromStm32Test, TestReadWord) {
+    /* Direct compacted-area baseline: Address < 0x80 */
+    FlashBuf[EEPROM_BASE + 0] = ~0xad;
+    FlashBuf[EEPROM_BASE + 1] = ~0xde;
+    /* Direct compacted-area baseline: Address >= 0x80 */
+    FlashBuf[EEPROM_BASE + 200]             = ~0xcd;
+    FlashBuf[EEPROM_BASE + 201]             = ~0xab;
+    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4] = ~0x34;
+    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 3] = ~0x12;
+    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78;
+    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56;
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead);
+    EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678);
+    /* Write Log word zero-encoded */
+    *(uint16_t*)&FlashBuf[LOG_BASE] = WORD_ZERO(200);
+    /* Write Log word one-encoded */
+    *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_ONE(EEPROM_SIZE - 4);
+    /* Write Log word value */
+    *(uint16_t*)&FlashBuf[LOG_BASE + 4] = WORD_NEXT(EEPROM_SIZE - 2);
+    *(uint16_t*)&FlashBuf[LOG_BASE + 6] = ~0x9abc;
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataWord(200), 0);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 1);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x9abc);
+}
+
+TEST_F(EepromStm32Test, TestWriteWord) {
+    /* Direct compacted-area: Address < 0x80 */
+    EEPROM_WriteDataWord(0, 0xdead);  // Aligned
+    EEPROM_WriteDataWord(3, 0xbeef);  // Unaligned
+    /* Direct compacted-area: Address >= 0x80 */
+    EEPROM_WriteDataWord(200, 0xabcd);  // Aligned
+    EEPROM_WriteDataWord(203, 0x9876);  // Unaligned
+    EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234);
+    EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678);
+    /* Write Log word zero-encoded */
+    EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0);
+    /* Write Log word one-encoded */
+    EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1);
+    /* Write Log word value aligned */
+    EEPROM_WriteDataWord(200, 0x4321);  // Aligned
+    /* Write Log word value unaligned */
+    EEPROM_WriteDataByte(202, 0x3c);    // Set neighboring byte
+    EEPROM_WriteDataWord(203, 0xcdef);  // Unaligned
+    /* Check values */
+    /* Direct compacted-area */
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE], (uint16_t)~0xdead);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 3], (uint16_t)~0xbeef);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 200], (uint16_t)~0xabcd);
+    EXPECT_EQ(FlashBuf[EEPROM_BASE + 203], (uint8_t)~0x76);
+    EXPECT_EQ(FlashBuf[EEPROM_BASE + 204], (uint8_t)~0x98);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4], (uint16_t)~0x1234);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint16_t)~0x5678);
+    /* Write Log word zero-encoded */
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], WORD_ZERO(EEPROM_SIZE - 4));
+    /* Write Log word one-encoded */
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_ONE(EEPROM_SIZE - 2));
+    /* Write Log word value aligned */
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], WORD_NEXT(200));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], (uint16_t)~0x4321);
+    /* Write Log word value unaligned */
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], WORD_NEXT(202));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], (uint16_t)~0x763c);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(202));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xef3c);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(204));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0x00cd);
+}
+
+TEST_F(EepromStm32Test, TestWordRoundTrip) {
+    /* Direct compacted-area: Address < 0x80 */
+    EEPROM_WriteDataWord(0, 0xdead);  // Aligned
+    EEPROM_WriteDataWord(3, 0xbeef);  // Unaligned
+    /* Direct compacted-area: Address >= 0x80 */
+    EEPROM_WriteDataWord(200, 0xabcd);  // Aligned
+    EEPROM_WriteDataWord(203, 0x9876);  // Unaligned
+    EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234);
+    EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678);
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead);
+    EXPECT_EQ(EEPROM_ReadDataWord(3), 0xbeef);
+    EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd);
+    EXPECT_EQ(EEPROM_ReadDataWord(203), 0x9876);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678);
+
+    /* Write Log word zero-encoded */
+    EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0);
+    /* Write Log word one-encoded */
+    EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1);
+    /* Write Log word value aligned */
+    EEPROM_WriteDataWord(200, 0x4321);  // Aligned
+    /* Write Log word value unaligned */
+    EEPROM_WriteDataByte(202, 0x3c);    // Set neighboring byte
+    EEPROM_WriteDataWord(203, 0xcdef);  // Unaligned
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataWord(200), 0x4321);
+    EXPECT_EQ(EEPROM_ReadDataByte(202), 0x3c);
+    EXPECT_EQ(EEPROM_ReadDataWord(203), 0xcdef);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0);
+    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 1);
+}
+
+TEST_F(EepromStm32Test, TestByteWordBoundary) {
+    /* Direct compacted-area write */
+    EEPROM_WriteDataWord(0x7e, 0xdead);
+    EEPROM_WriteDataWord(0x80, 0xbeef);
+    /* Byte log entry */
+    EEPROM_WriteDataByte(0x7f, 0x3c);
+    /* Word log entry */
+    EEPROM_WriteDataByte(0x80, 0x18);
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0x3cad);
+    EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xbe18);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(0x7f, 0x3c));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(0x80));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0xbe18);
+    /* Byte log entries */
+    EEPROM_WriteDataWord(0x7e, 0xcafe);
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0xcafe);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], BYTE_VALUE(0x7e, 0xfe));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], BYTE_VALUE(0x7f, 0xca));
+    /* Byte and Word log entries */
+    EEPROM_WriteDataWord(0x7f, 0xba5e);
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataWord(0x7f), 0xba5e);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], BYTE_VALUE(0x7f, 0x5e));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(0x80));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xbeba);
+    /* Word log entry */
+    EEPROM_WriteDataWord(0x80, 0xf00d);
+    /* Check values */
+    EEPROM_Init();
+    EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xf00d);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(0x80));
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0xf00d);
+}
+
+TEST_F(EepromStm32Test, TestDWordRoundTrip) {
+    /* Direct compacted-area: Address < 0x80 */
+    eeprom_write_dword((uint32_t*)0, 0xdeadbeef);  // Aligned
+    eeprom_write_dword((uint32_t*)9, 0x12345678);  // Unaligned
+    /* Direct compacted-area: Address >= 0x80 */
+    eeprom_write_dword((uint32_t*)200, 0xfacef00d);
+    eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xba5eba11);  // Aligned
+    eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0xcafed00d);  // Unaligned
+    /* Check direct values */
+    EEPROM_Init();
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x12345678);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 0xfacef00d);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xba5eba11);  // Aligned
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0xcafed00d);  // Unaligned
+    /* Write Log byte encoded */
+    eeprom_write_dword((uint32_t*)0, 0xdecafbad);
+    eeprom_write_dword((uint32_t*)9, 0x87654321);
+    /* Write Log word encoded */
+    eeprom_write_dword((uint32_t*)200, 1);
+    /* Write Log word value aligned */
+    eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xdeadc0de);  // Aligned
+    eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0x6789abcd);  // Unaligned
+    /* Check log values */
+    EEPROM_Init();
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdecafbad);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x87654321);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 1);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xdeadc0de);  // Aligned
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0x6789abcd);  // Unaligned
+}
+
+TEST_F(EepromStm32Test, TestBlockRoundTrip) {
+    char  src0[] = "0123456789abcdef";
+    void* src1   = (void*)&src0[1];
+    /* Various alignments of src & dst, Address < 0x80 */
+    eeprom_write_block(src0, (void*)0, sizeof(src0));
+    eeprom_write_block(src0, (void*)21, sizeof(src0));
+    eeprom_write_block(src1, (void*)40, sizeof(src0) - 1);
+    eeprom_write_block(src1, (void*)61, sizeof(src0) - 1);
+    /* Various alignments of src & dst, Address >= 0x80 */
+    eeprom_write_block(src0, (void*)140, sizeof(src0));
+    eeprom_write_block(src0, (void*)161, sizeof(src0));
+    eeprom_write_block(src1, (void*)180, sizeof(src0) - 1);
+    eeprom_write_block(src1, (void*)201, sizeof(src0) - 1);
+
+    /* Check values */
+    EEPROM_Init();
+
+    char  dstBuf[256] = {0};
+    char* dst0a       = (char*)dstBuf;
+    char* dst0b       = (char*)&dstBuf[20];
+    char* dst1a       = (char*)&dstBuf[41];
+    char* dst1b       = (char*)&dstBuf[61];
+    char* dst0c       = (char*)&dstBuf[80];
+    char* dst0d       = (char*)&dstBuf[100];
+    char* dst1c       = (char*)&dstBuf[121];
+    char* dst1d       = (char*)&dstBuf[141];
+    eeprom_read_block((void*)dst0a, (void*)0, sizeof(src0));
+    eeprom_read_block((void*)dst0b, (void*)21, sizeof(src0));
+    eeprom_read_block((void*)dst1a, (void*)40, sizeof(src0) - 1);
+    eeprom_read_block((void*)dst1b, (void*)61, sizeof(src0) - 1);
+    eeprom_read_block((void*)dst0c, (void*)140, sizeof(src0));
+    eeprom_read_block((void*)dst0d, (void*)161, sizeof(src0));
+    eeprom_read_block((void*)dst1c, (void*)180, sizeof(src0) - 1);
+    eeprom_read_block((void*)dst1d, (void*)201, sizeof(src0) - 1);
+    EXPECT_EQ(strcmp((char*)src0, dst0a), 0);
+    EXPECT_EQ(strcmp((char*)src0, dst0b), 0);
+    EXPECT_EQ(strcmp((char*)src0, dst0c), 0);
+    EXPECT_EQ(strcmp((char*)src0, dst0d), 0);
+    EXPECT_EQ(strcmp((char*)src1, dst1a), 0);
+    EXPECT_EQ(strcmp((char*)src1, dst1b), 0);
+    EXPECT_EQ(strcmp((char*)src1, dst1c), 0);
+    EXPECT_EQ(strcmp((char*)src1, dst1d), 0);
+}
+
+TEST_F(EepromStm32Test, TestCompaction) {
+    /* Direct writes */
+    eeprom_write_dword((uint32_t*)0, 0xdeadbeef);
+    eeprom_write_byte((uint8_t*)4, 0x3c);
+    eeprom_write_word((uint16_t*)6, 0xd00d);
+    eeprom_write_dword((uint32_t*)150, 0xcafef00d);
+    eeprom_write_dword((uint32_t*)200, 0x12345678);
+    /* Fill write log entries */
+    uint32_t i;
+    uint32_t val = 0xd8453c6b;
+    for (i = 0; i < (LOG_SIZE / (sizeof(uint32_t) * 2)); i++) {
+        val ^= 0x593ca5b3;
+        val += i;
+        eeprom_write_dword((uint32_t*)200, val);
+    }
+    /* Check values pre-compaction */
+    EEPROM_Init();
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef);
+    EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x3c);
+    EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val);
+    EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF);
+    EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF);
+    /* Run compaction */
+    eeprom_write_byte((uint8_t*)4, 0x1f);
+    EEPROM_Init();
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef);
+    EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x1f);
+    EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d);
+    EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF);
+    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF);
+}
diff --git a/tmk_core/common/test/flash_stm32_mock.c b/tmk_core/common/test/flash_stm32_mock.c
new file mode 100644
index 0000000000..1b81d81f9a
--- /dev/null
+++ b/tmk_core/common/test/flash_stm32_mock.c
@@ -0,0 +1,50 @@
+/* Copyright 2021 by Don Kjer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdbool.h>
+#include "flash_stm32.h"
+
+uint8_t FlashBuf[MOCK_FLASH_SIZE] = {0};
+
+static bool flash_locked = true;
+
+FLASH_Status FLASH_ErasePage(uint32_t Page_Address) {
+    if (flash_locked) return FLASH_ERROR_WRP;
+    Page_Address -= (uintptr_t)FlashBuf;
+    Page_Address -= (Page_Address % FEE_PAGE_SIZE);
+    if (Page_Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS;
+    memset(&FlashBuf[Page_Address], '\xff', FEE_PAGE_SIZE);
+    return FLASH_COMPLETE;
+}
+
+FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
+    if (flash_locked) return FLASH_ERROR_WRP;
+    Address -= (uintptr_t)FlashBuf;
+    if (Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS;
+    uint16_t oldData = *(uint16_t*)&FlashBuf[Address];
+    if (oldData == 0xFFFF || Data == 0) {
+        *(uint16_t*)&FlashBuf[Address] = Data;
+        return FLASH_COMPLETE;
+    } else {
+        return FLASH_ERROR_PG;
+    }
+}
+
+FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) { return FLASH_COMPLETE; }
+void         FLASH_Unlock(void) { flash_locked = false; }
+void         FLASH_Lock(void) { flash_locked = true; }
+void         FLASH_ClearFlag(uint32_t FLASH_FLAG) {}
diff --git a/tmk_core/common/test/platform.c b/tmk_core/common/test/platform.c
new file mode 100644
index 0000000000..8ddceeda8f
--- /dev/null
+++ b/tmk_core/common/test/platform.c
@@ -0,0 +1,21 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "platform_deps.h"
+
+void platform_setup(void) {
+    // do nothing
+}
\ No newline at end of file
diff --git a/tmk_core/common/test/rules.mk b/tmk_core/common/test/rules.mk
new file mode 100644
index 0000000000..e47e5880c5
--- /dev/null
+++ b/tmk_core/common/test/rules.mk
@@ -0,0 +1,23 @@
+eeprom_stm32_DEFS  := -DFLASH_STM32_MOCKED -DNO_PRINT -DFEE_FLASH_BASE=FlashBuf
+eeprom_stm32_tiny_DEFS := $(eeprom_stm32_DEFS) \
+	-DFEE_MCU_FLASH_SIZE=1 \
+	-DMOCK_FLASH_SIZE=1024 \
+	-DFEE_PAGE_SIZE=512 \
+	-DFEE_DENSITY_PAGES=1
+eeprom_stm32_large_DEFS := $(eeprom_stm32_DEFS) \
+	-DFEE_MCU_FLASH_SIZE=64 \
+	-DMOCK_FLASH_SIZE=65536 \
+	-DFEE_PAGE_SIZE=2048 \
+	-DFEE_DENSITY_PAGES=16
+
+eeprom_stm32_INC := \
+	$(TMK_PATH)/common/chibios/
+eeprom_stm32_tiny_INC := $(eeprom_stm32_INC)
+eeprom_stm32_large_INC := $(eeprom_stm32_INC)
+
+eeprom_stm32_SRC := \
+	$(TMK_PATH)/common/test/eeprom_stm32_tests.cpp \
+	$(TMK_PATH)/common/test/flash_stm32_mock.c \
+	$(TMK_PATH)/common/chibios/eeprom_stm32.c
+eeprom_stm32_tiny_SRC := $(eeprom_stm32_SRC)
+eeprom_stm32_large_SRC := $(eeprom_stm32_SRC)
diff --git a/tmk_core/common/test/testlist.mk b/tmk_core/common/test/testlist.mk
new file mode 100644
index 0000000000..51a9638bb9
--- /dev/null
+++ b/tmk_core/common/test/testlist.mk
@@ -0,0 +1 @@
+TEST_LIST += eeprom_stm32_tiny eeprom_stm32_large
diff --git a/tmk_core/common/timer.h b/tmk_core/common/timer.h
index abddcea857..02e39e79e7 100644
--- a/tmk_core/common/timer.h
+++ b/tmk_core/common/timer.h
@@ -1,5 +1,6 @@
 /*
 Copyright 2011 Jun Wako <wakojun@gmail.com>
+Copyright 2021 Simon Arlott
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -17,6 +18,10 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #pragma once
 
+#if __has_include_next("_timer.h")
+#    include_next "_timer.h" /* Include the platform's _timer.h */
+#endif
+
 #include <stdint.h>
 
 #define TIMER_DIFF(a, b, max) ((max == UINT8_MAX) ? ((uint8_t)((a) - (b))) : ((max == UINT16_MAX) ? ((uint16_t)((a) - (b))) : ((max == UINT32_MAX) ? ((uint32_t)((a) - (b))) : ((a) >= (b) ? (a) - (b) : (max) + 1 - (b) + (a)))))
@@ -42,6 +47,21 @@ uint32_t timer_elapsed32(uint32_t last);
 #define timer_expired(current, future) ((uint16_t)(current - future) < UINT16_MAX / 2)
 #define timer_expired32(current, future) ((uint32_t)(current - future) < UINT32_MAX / 2)
 
+// Use an appropriate timer integer size based on architecture (16-bit will overflow sooner)
+#if FAST_TIMER_T_SIZE < 32
+#    define TIMER_DIFF_FAST(a, b) TIMER_DIFF_16(a, b)
+#    define timer_expired_fast(current, future) timer_expired(current, future)
+typedef uint16_t fast_timer_t;
+fast_timer_t inline timer_read_fast(void) { return timer_read(); }
+fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed(last); }
+#else
+#    define TIMER_DIFF_FAST(a, b) TIMER_DIFF_32(a, b)
+#    define timer_expired_fast(current, future) timer_expired32(current, future)
+typedef uint32_t fast_timer_t;
+fast_timer_t inline timer_read_fast(void) { return timer_read32(); }
+fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed32(last); }
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/tmk_core/common/usb_util.c b/tmk_core/common/usb_util.c
index d4134a0446..dd1deeaa11 100644
--- a/tmk_core/common/usb_util.c
+++ b/tmk_core/common/usb_util.c
@@ -16,7 +16,7 @@
 #include "quantum.h"
 #include "usb_util.h"
 
-__attribute__((weak)) void usb_disable(void) {}
+__attribute__((weak)) void usb_disconnect(void) {}
 __attribute__((weak)) bool usb_connected_state(void) { return true; }
 __attribute__((weak)) bool usb_vbus_state(void) {
 #ifdef USB_VBUS_PIN
diff --git a/tmk_core/common/usb_util.h b/tmk_core/common/usb_util.h
index 4ebedb1e71..13db9fbfbd 100644
--- a/tmk_core/common/usb_util.h
+++ b/tmk_core/common/usb_util.h
@@ -17,6 +17,6 @@
 
 #include <stdbool.h>
 
-void usb_disable(void);
+void usb_disconnect(void);
 bool usb_connected_state(void);
 bool usb_vbus_state(void);