diff --git a/app/boards/arm/nrf52840_m2/nrf52840_m2.dts b/app/boards/arm/nrf52840_m2/nrf52840_m2.dts index 39569f0b760..a9fcef24a8a 100644 --- a/app/boards/arm/nrf52840_m2/nrf52840_m2.dts +++ b/app/boards/arm/nrf52840_m2/nrf52840_m2.dts @@ -15,7 +15,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zmk,battery = &vbatt; }; leds { @@ -31,13 +30,6 @@ }; }; - vbatt: vbatt { - compatible = "zmk,battery-voltage-divider"; - io-channels = <&adc 0>; - output-ohms = <1000000>; - full-ohms = <(1000000 + 1000000)>; - }; - }; &adc { diff --git a/app/boards/shields/m60/CMakeLists.txt b/app/boards/shields/m60/CMakeLists.txt new file mode 100644 index 00000000000..05214a680e9 --- /dev/null +++ b/app/boards/shields/m60/CMakeLists.txt @@ -0,0 +1,3 @@ +zephyr_library() +zephyr_library_sources(pinmux.c) +zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) \ No newline at end of file diff --git a/app/boards/shields/m60/m60.overlay b/app/boards/shields/m60/m60.overlay index 15690f52406..1de17f9565f 100644 --- a/app/boards/shields/m60/m60.overlay +++ b/app/boards/shields/m60/m60.overlay @@ -12,6 +12,7 @@ chosen { zmk,kscan = &kscan0; zmk,physical-layout = &layout_60_ansi; + zmk,battery = &vbatt; }; kscan0: kscan { @@ -58,6 +59,13 @@ RC(6,4) RC(6,3) RC(6,2) RC(6,1) RC(6,0) RC(5,7) RC(5,6) RC(5,5) RC(5,4) RC RC(6,5) RC(6,6) RC(6,7) RC(7,0) RC(7,1) RC(7,2) RC(7,3) RC(7,4) >; }; + + vbatt: vbatt { + compatible = "zmk,battery-voltage-divider"; + io-channels = <&adc 0>; + output-ohms = <1000000>; + full-ohms = <(1000000 + 1000000)>; + }; }; &layout_60_ansi { diff --git a/app/boards/shields/m60/pinmux.c b/app/boards/shields/m60/pinmux.c new file mode 100644 index 00000000000..74d64540f18 --- /dev/null +++ b/app/boards/shields/m60/pinmux.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include + +/* The PMU(BQ24075) enable pin(LOW active) on M60 is controlled by a NAND gate. + * Partial reverse engineering shows: + * P0.28 affects the NAND gate. + * The button affects P0.27 the NAND gate. + * P0.03 is detection pin for charging state(possibly attached to PMU LED pin). + */ + +static const struct device *p0 = DEVICE_DT_GET(DT_NODELABEL(gpio0)); + +static inline void power_off(void) { + gpio_pin_set(p0, 28, 0); // turn off the PMU battery path +} + +static inline bool is_charging(void) { + // 0: charging + return gpio_pin_get_raw(p0, 3) == 0; +} + +static void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { + // wait the button stable at 0 so the keyboard won't be powered up accidentally + k_sleep(K_SECONDS(1)); + + if (!is_charging()) { + power_off(); + } +} + +static int pinmux_nrf52840_m2_init(void) { + // back button + // NOTE: if used to power off the keyboard, make sure the action is done + // AFTER the button is released. + // + // To wake up the keyboard from sleep with this button, must use interrupt. + // To avoid the callback triggered after keyboard woke up(powering off the + // keyboard), must use GPIO_INT_EDGE_FALLING(trigger on button pressed). + gpio_pin_interrupt_configure(p0, 27, GPIO_INT_EDGE_FALLING); + + // GPIO 0.28(LDO control) is already configured by bootloader, + // so configuring it here is just an ensurance. + gpio_pin_configure(p0, 28, GPIO_OUTPUT_ACTIVE | GPIO_PULL_UP | GPIO_OPEN_DRAIN); + + // this is the pin for charging detection + gpio_pin_configure(p0, 3, GPIO_INPUT | GPIO_PULL_UP); + + // setup the interrupt handler to poweroff the keyboard + static struct gpio_callback button_cb; + gpio_init_callback(&button_cb, button_pressed, BIT(27)); + gpio_add_callback(p0, &button_cb); + + return 0; +} + +SYS_INIT(pinmux_nrf52840_m2_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);