zmk driver for PIXART PAW3222 optical sensor
0

Configure Feed

Select the types of activity you want to include in your feed.

first commit

author
sekigon-gonnoc
date (Mar 30, 2025, 12:22 AM +0900) commit 9e5239d1
+757
+5
CMakeLists.txt
··· 1 + if(CONFIG_PAW3222) 2 + zephyr_library() 3 + zephyr_library_sources(src/paw3222.c) 4 + zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) 5 + endif()
+6
Kconfig
··· 1 + 2 + menuconfig PAW3222 3 + bool "PAW3222 mouse optical sensor" 4 + select SPI 5 + help 6 + Enable PAW3222 mouse optical sensor.
+16
LICENSE
··· 1 + Copyright 2024 Google LLC 2 + Modifiactions Copyright 2025 sekigon-gonnoc 3 + 4 + Original source code: https://github.com/zephyrproject-rtos/zephyr/blob/19c6240b6865bcb28e1d786d4dcadfb3a02067a0/drivers/input/input_paw32xx.c 5 + 6 + Licensed under the Apache License, Version 2.0 (the “License”); 7 + you may not use this file except in compliance with the License. 8 + You may obtain a copy of the License at 9 + 10 + http://www.apache.org/licenses/LICENSE-2.0 11 + 12 + Unless required by applicable law or agreed to in writing, software 13 + distributed under the License is distributed on an “AS IS” BASIS, 14 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + See the License for the specific language governing permissions and 16 + limitations under the License.
+190
README.md
··· 1 + # ZMK PAW3222 Driver 2 + 3 + This driver enables the use of the PIXART PAW3222 optical sensor with the ZMK framework. 4 + 5 + ## Overview 6 + 7 + The PAW3222 is a low-power optical mouse sensor suitable for tracking applications such as mice and trackballs. This driver communicates with the PAW3222 sensor via SPI interface. 8 + 9 + ## Installation 10 + 11 + 1. Add as a ZMK module in your west.yml: 12 + 13 + ``` 14 + manifest: 15 + remotes: 16 + - name: zmkfirmware 17 + url-base: https://github.com/zmkfirmware 18 + - name: sekigon-gonnoc 19 + url-base: https://github.com/sekigon-gonnoc 20 + projects: 21 + - name: zmk-driver-paw3222 22 + remote: sekigon-gonnoc 23 + ``` 24 + 25 + ## Device Tree Configuration 26 + 27 + Configure in your shield or board config file (.overlay or .dtsi): 28 + 29 + ```dts 30 + &pinctrl { 31 + spi0_default: spi0_default { 32 + group1 { 33 + psels = <NRF_PSEL(SPIM_SCK, 0, 12)>, 34 + <NRF_PSEL(SPIM_MOSI, 1, 9)>, 35 + <NRF_PSEL(SPIM_MISO, 1, 9)>; 36 + }; 37 + }; 38 + 39 + spi0_sleep: spi0_sleep { 40 + group1 { 41 + psels = <NRF_PSEL(SPIM_SCK, 0, 12)>, 42 + <NRF_PSEL(SPIM_MOSI, 1, 9)>, 43 + <NRF_PSEL(SPIM_MISO, 1, 9)>; 44 + low-power-enable; 45 + }; 46 + }; 47 + }; 48 + 49 + &spi0 { 50 + status = "okay"; 51 + compatible = "nordic,nrf-spim"; 52 + pinctrl-0 = <&spi0_default>; 53 + pinctrl-1 = <&spi0_sleep>; 54 + pinctrl-names = "default", "sleep"; 55 + cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; 56 + 57 + trackball: trackball@0 { 58 + status = "okay"; 59 + compatible = "pixart,paw3222"; 60 + reg = <0>; 61 + spi-max-frequency = <2000000>; 62 + irq-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; 63 + }; 64 + }; 65 + ``` 66 + 67 + ## Enable the module in your keyboard's Kconfig file 68 + 69 + Add the following to your keyboard's `Kconfig.defconfig`: 70 + 71 + ```kconfig 72 + if ZMK_KEYBOARD_YOUR_KEYBOARD 73 + 74 + config SPI 75 + default y 76 + 77 + config INPUT 78 + default y 79 + 80 + config ZMK_MOUSE 81 + default y 82 + 83 + config PAW3222 84 + default y 85 + 86 + endif 87 + ``` 88 + 89 + ## Properties 90 + 91 + - `irq-gpios`: GPIO connected to the motion pin (required) 92 + - `res-cpi`: CPI resolution for the sensor (optional) 93 + - `force-awake`: Initialize the sensor in "force awake" mode (optional, boolean) 94 + 95 + --- 96 + 97 + # ZMK PAW3222 ドライバ 98 + 99 + このドライバは、PIXART PAW3222光学センサーをZMKフレームワークで使用できるようにします。 100 + 101 + ## 概要 102 + 103 + PAW3222は、マウスやトラックボールなどのトラッキングアプリケーションに適した低消費電力の光学マウスセンサーです。このドライバはSPIインターフェースを介してPAW3222センサーと通信します。 104 + 105 + ## インストール 106 + 107 + 1. ZMKモジュールとして追加: 108 + 109 + ``` 110 + # west.yml に追加 111 + manifest: 112 + remotes: 113 + - name: zmkfirmware 114 + url-base: https://github.com/zmkfirmware 115 + - name: sekigon-gonnoc 116 + url-base: https://github.com/sekigon-gonnoc 117 + projects: 118 + - name: zmk-driver-paw3222 119 + remote: sekigon-gonnoc 120 + ``` 121 + 122 + ## デバイスツリー設定 123 + 124 + シールドまたはボード設定ファイル(.overlayまたは.dtsi)で設定: 125 + 126 + ```dts 127 + &pinctrl { 128 + spi0_default: spi0_default { 129 + group1 { 130 + psels = <NRF_PSEL(SPIM_SCK, 0, 12)>, 131 + <NRF_PSEL(SPIM_MOSI, 1, 9)>, 132 + <NRF_PSEL(SPIM_MISO, 1, 9)>; 133 + }; 134 + }; 135 + 136 + spi0_sleep: spi0_sleep { 137 + group1 { 138 + psels = <NRF_PSEL(SPIM_SCK, 0, 12)>, 139 + <NRF_PSEL(SPIM_MOSI, 1, 9)>, 140 + <NRF_PSEL(SPIM_MISO, 1, 9)>; 141 + low-power-enable; 142 + }; 143 + }; 144 + }; 145 + 146 + &spi0 { 147 + status = "okay"; 148 + compatible = "nordic,nrf-spim"; 149 + pinctrl-0 = <&spi0_default>; 150 + pinctrl-1 = <&spi0_sleep>; 151 + pinctrl-names = "default", "sleep"; 152 + cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; 153 + 154 + trackball: trackball@0 { 155 + status = "okay"; 156 + compatible = "pixart,paw3222"; 157 + reg = <0>; 158 + spi-max-frequency = <2000000>; 159 + irq-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; 160 + }; 161 + }; 162 + ``` 163 + 164 + ## キーボードのKconfigファイルでモジュールを有効化 165 + 166 + キーボードの `Kconfig.defconfig` に以下を追加: 167 + 168 + ```kconfig 169 + if ZMK_KEYBOARD_YOUR_KEYBOARD 170 + 171 + config SPI 172 + default y 173 + 174 + config INPUT 175 + default y 176 + 177 + config ZMK_MOUSE 178 + default y 179 + 180 + config PAW3222 181 + default y 182 + 183 + endif 184 + ``` 185 + 186 + ## プロパティ 187 + 188 + - `irq-gpios`: モーションピンに接続されたGPIO(必須) 189 + - `res-cpi`: センサーのCPI解像度(任意) 190 + - `force-awake`: センサーを「強制起動」モードで初期化(任意、ブール値)
+29
dts/bindings/pixart,paw3222.yaml
··· 1 + # Copyright 2024 Google LLC 2 + # Copyright 2025 sekigon-gonnoc 3 + # SPDX-License-Identifier: Apache-2.0 4 + 5 + description: PAW32xx ultra low power wireless mouse chip 6 + 7 + compatible: "pixart,paw3222" 8 + 9 + include: spi-device.yaml 10 + 11 + properties: 12 + irq-gpios: 13 + type: phandle-array 14 + required: true 15 + description: 16 + GPIO connected to the motion pin, active low. 17 + 18 + res-cpi: 19 + type: int 20 + description: | 21 + CPI resolution for the sensor. This can also be changed in runtime using 22 + the paw32xx_set_resolution() API. 23 + 24 + force-awake: 25 + type: boolean 26 + description: | 27 + Initialize the sensor in "force awake" mode. This can also be enabled or 28 + disabled in runtime by the application using the paw32xx_force_awake() 29 + API.
+28
include/paw3222.h
··· 1 + /* 2 + * Copyright 2024 Google LLC 3 + * Modifications Copyright 2025 sekigon-gonnoc 4 + * Original source code: https://github.com/zephyrproject-rtos/zephyr/blob/19c6240b6865bcb28e1d786d4dcadfb3a02067a0/include/zephyr/input/input_paw32xx.h 5 + * 6 + * SPDX-License-Identifier: Apache-2.0 7 + */ 8 + 9 + #ifndef ZEPHYR_INCLUDE_INPUT_PAW32XX_H_ 10 + #define ZEPHYR_INCLUDE_INPUT_PAW32XX_H_ 11 + 12 + /** 13 + * @brief Set resolution on a paw32xx device 14 + * 15 + * @param dev paw32xx device. 16 + * @param res_cpi CPI resolution, 200 to 3200. 17 + */ 18 + int paw32xx_set_resolution(const struct device *dev, uint16_t res_cpi); 19 + 20 + /** 21 + * @brief Set force awake mode on a paw32xx device 22 + * 23 + * @param dev paw32xx device. 24 + * @param enable whether to enable or disable force awake mode. 25 + */ 26 + int paw32xx_force_awake(const struct device *dev, bool enable); 27 + 28 + #endif /* ZEPHYR_INCLUDE_INPUT_PAW32XX_H_ */
+477
src/paw3222.c
··· 1 + /* 2 + * Copyright 2024 Google LLC 3 + * Modifications Copyright 2025 sekigon-gonnoc 4 + * 5 + * Original source code: https://github.com/zephyrproject-rtos/zephyr/blob/19c6240b6865bcb28e1d786d4dcadfb3a02067a0/drivers/input/input_paw32xx.c 6 + * 7 + * SPDX-License-Identifier: Apache-2.0 8 + */ 9 + 10 + 11 + #include <stdint.h> 12 + #include <stdlib.h> 13 + 14 + #include <zephyr/device.h> 15 + #include <zephyr/drivers/gpio.h> 16 + #include <zephyr/drivers/spi.h> 17 + #include <zephyr/input/input.h> 18 + #include <zephyr/kernel.h> 19 + #include <zephyr/logging/log.h> 20 + #include <zephyr/pm/device.h> 21 + #include <zephyr/pm/device_runtime.h> 22 + #include <zephyr/sys/util.h> 23 + 24 + #include "../include/paw3222.h" 25 + 26 + LOG_MODULE_REGISTER(paw32xx, CONFIG_ZMK_LOG_LEVEL); 27 + 28 + #define DT_DRV_COMPAT pixart_paw3222 29 + 30 + #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) 31 + 32 + #define PAW32XX_PRODUCT_ID1 0x00 33 + #define PAW32XX_PRODUCT_ID2 0x01 34 + #define PAW32XX_MOTION 0x02 35 + #define PAW32XX_DELTA_X 0x03 36 + #define PAW32XX_DELTA_Y 0x04 37 + #define PAW32XX_OPERATION_MODE 0x05 38 + #define PAW32XX_CONFIGURATION 0x06 39 + #define PAW32XX_WRITE_PROTECT 0x09 40 + #define PAW32XX_SLEEP1 0x0a 41 + #define PAW32XX_SLEEP2 0x0b 42 + #define PAW32XX_SLEEP3 0x0c 43 + #define PAW32XX_CPI_X 0x0d 44 + #define PAW32XX_CPI_Y 0x0e 45 + #define PAW32XX_DELTA_XY_HI 0x12 46 + #define PAW32XX_MOUSE_OPTION 0x19 47 + 48 + #define PRODUCT_ID_PAW32XX 0x30 49 + #define SPI_WRITE BIT(7) 50 + 51 + #define MOTION_STATUS_MOTION BIT(7) 52 + #define OPERATION_MODE_SLP_ENH BIT(4) 53 + #define OPERATION_MODE_SLP2_ENH BIT(3) 54 + #define OPERATION_MODE_SLP_MASK (OPERATION_MODE_SLP_ENH | OPERATION_MODE_SLP2_ENH) 55 + #define CONFIGURATION_PD_ENH BIT(3) 56 + #define CONFIGURATION_RESET BIT(7) 57 + #define WRITE_PROTECT_ENABLE 0x00 58 + #define WRITE_PROTECT_DISABLE 0x5a 59 + #define MOUSE_OPTION_MOVX_INV_BIT 3 60 + #define MOUSE_OPTION_MOVY_INV_BIT 4 61 + 62 + #define PAW32XX_DATA_SIZE_BITS 8 63 + 64 + #define RESET_DELAY_MS 2 65 + 66 + #define RES_STEP 38 67 + #define RES_MIN (16 * RES_STEP) 68 + #define RES_MAX (127 * RES_STEP) 69 + 70 + struct paw32xx_config { 71 + struct spi_dt_spec spi; 72 + struct gpio_dt_spec irq_gpio; 73 + int16_t res_cpi; 74 + bool force_awake; 75 + }; 76 + 77 + struct paw32xx_data { 78 + const struct device *dev; 79 + struct k_work motion_work; 80 + struct gpio_callback motion_cb; 81 + struct k_timer motion_timer; // Add timer for delayed motion checking 82 + }; 83 + 84 + static inline int32_t sign_extend(uint32_t value, uint8_t index) 85 + { 86 + __ASSERT_NO_MSG(index <= 31); 87 + 88 + uint8_t shift = 31 - index; 89 + 90 + return (int32_t)(value << shift) >> shift; 91 + } 92 + 93 + static int paw32xx_read_reg(const struct device *dev, uint8_t addr, uint8_t *value) 94 + { 95 + const struct paw32xx_config *cfg = dev->config; 96 + int ret; 97 + 98 + const struct spi_buf tx_buf = { 99 + .buf = &addr, 100 + .len = sizeof(addr), 101 + }; 102 + const struct spi_buf_set tx = { 103 + .buffers = &tx_buf, 104 + .count = 1, 105 + }; 106 + 107 + struct spi_buf rx_buf[] = { 108 + { 109 + .buf = NULL, 110 + .len = sizeof(addr), 111 + }, 112 + { 113 + .buf = value, 114 + .len = 1, 115 + }, 116 + }; 117 + const struct spi_buf_set rx = { 118 + .buffers = rx_buf, 119 + .count = ARRAY_SIZE(rx_buf), 120 + }; 121 + 122 + ret = spi_transceive_dt(&cfg->spi, &tx, &rx); 123 + 124 + return ret; 125 + } 126 + 127 + static int paw32xx_write_reg(const struct device *dev, uint8_t addr, uint8_t value) 128 + { 129 + const struct paw32xx_config *cfg = dev->config; 130 + 131 + uint8_t write_buf[] = {addr | SPI_WRITE, value}; 132 + const struct spi_buf tx_buf = { 133 + .buf = write_buf, 134 + .len = sizeof(write_buf), 135 + }; 136 + const struct spi_buf_set tx = { 137 + .buffers = &tx_buf, 138 + .count = 1, 139 + }; 140 + 141 + return spi_write_dt(&cfg->spi, &tx); 142 + } 143 + 144 + static int paw32xx_update_reg(const struct device *dev, uint8_t addr, uint8_t mask, uint8_t value) 145 + { 146 + uint8_t val; 147 + int ret; 148 + 149 + ret = paw32xx_read_reg(dev, addr, &val); 150 + if (ret < 0) { 151 + return ret; 152 + } 153 + 154 + val = (val & ~mask) | (value & mask); 155 + 156 + ret = paw32xx_write_reg(dev, addr, val); 157 + if (ret < 0) { 158 + return ret; 159 + } 160 + 161 + return 0; 162 + } 163 + 164 + static int paw32xx_read_xy(const struct device *dev, int16_t *x, int16_t *y) 165 + { 166 + const struct paw32xx_config *cfg = dev->config; 167 + int ret; 168 + 169 + uint8_t tx_data[] = { 170 + PAW32XX_DELTA_X, 171 + 0xff, 172 + PAW32XX_DELTA_Y, 173 + 0xff, 174 + }; 175 + uint8_t rx_data[sizeof(tx_data)]; 176 + 177 + const struct spi_buf tx_buf = { 178 + .buf = tx_data, 179 + .len = sizeof(tx_data), 180 + }; 181 + const struct spi_buf_set tx = { 182 + .buffers = &tx_buf, 183 + .count = 1, 184 + }; 185 + 186 + struct spi_buf rx_buf = { 187 + .buf = rx_data, 188 + .len = sizeof(rx_data), 189 + }; 190 + const struct spi_buf_set rx = { 191 + .buffers = &rx_buf, 192 + .count = 1, 193 + }; 194 + 195 + ret = spi_transceive_dt(&cfg->spi, &tx, &rx); 196 + if (ret < 0) { 197 + return ret; 198 + } 199 + 200 + *x = rx_data[1]; 201 + *y = rx_data[3]; 202 + 203 + *x = sign_extend(*x, PAW32XX_DATA_SIZE_BITS - 1); 204 + *y = sign_extend(*y, PAW32XX_DATA_SIZE_BITS - 1); 205 + 206 + return 0; 207 + } 208 + 209 + static void paw32xx_motion_timer_handler(struct k_timer *timer) 210 + { 211 + struct paw32xx_data *data = CONTAINER_OF(timer, struct paw32xx_data, motion_timer); 212 + k_work_submit(&data->motion_work); 213 + } 214 + 215 + static void paw32xx_motion_work_handler(struct k_work *work) 216 + { 217 + struct paw32xx_data *data = CONTAINER_OF( 218 + work, struct paw32xx_data, motion_work); 219 + const struct device *dev = data->dev; 220 + const struct paw32xx_config *cfg = dev->config; 221 + uint8_t val; 222 + int16_t x, y; 223 + int ret; 224 + 225 + ret = paw32xx_read_reg(dev, PAW32XX_MOTION, &val); 226 + if (ret < 0) { 227 + return; 228 + } 229 + 230 + if ((val & MOTION_STATUS_MOTION) == 0x00) { 231 + // No motion detected, re-enable interrupts and wait for next interrupt 232 + gpio_pin_interrupt_configure_dt(&cfg->irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); 233 + 234 + if (gpio_pin_get_dt(&cfg->irq_gpio) == 1) { 235 + k_timer_start(&data->motion_timer, K_MSEC(15), K_NO_WAIT); 236 + } 237 + return; 238 + } 239 + 240 + ret = paw32xx_read_xy(dev, &x, &y); 241 + if (ret < 0) { 242 + return; 243 + } 244 + 245 + LOG_DBG("x=%4d y=%4d", x, y); 246 + 247 + input_report_rel(data->dev, INPUT_REL_X, x, false, K_FOREVER); 248 + input_report_rel(data->dev, INPUT_REL_Y, y, true, K_FOREVER); 249 + 250 + // Schedule next check after 8ms without using interrupts 251 + k_timer_start(&data->motion_timer, K_MSEC(15), K_NO_WAIT); 252 + } 253 + 254 + static void paw32xx_motion_handler(const struct device *gpio_dev, 255 + struct gpio_callback *cb, 256 + uint32_t pins) 257 + { 258 + struct paw32xx_data *data = CONTAINER_OF( 259 + cb, struct paw32xx_data, motion_cb); 260 + const struct device *dev = data->dev; 261 + const struct paw32xx_config *cfg = dev->config; 262 + 263 + // Disable interrupts while timer is active 264 + gpio_pin_interrupt_configure_dt(&cfg->irq_gpio, GPIO_INT_DISABLE); 265 + 266 + // Cancel any pending timer 267 + k_timer_stop(&data->motion_timer); 268 + 269 + // Process motion 270 + k_work_submit(&data->motion_work); 271 + } 272 + 273 + int paw32xx_set_resolution(const struct device *dev, uint16_t res_cpi) 274 + { 275 + uint8_t val; 276 + int ret; 277 + 278 + if (!IN_RANGE(res_cpi, RES_MIN, RES_MAX)) { 279 + LOG_ERR("res_cpi out of range: %d", res_cpi); 280 + return -EINVAL; 281 + } 282 + 283 + val = res_cpi / RES_STEP; 284 + 285 + ret = paw32xx_write_reg(dev, PAW32XX_WRITE_PROTECT, WRITE_PROTECT_DISABLE); 286 + if (ret < 0) { 287 + return ret; 288 + } 289 + 290 + ret = paw32xx_write_reg(dev, PAW32XX_CPI_X, val); 291 + if (ret < 0) { 292 + return ret; 293 + } 294 + 295 + ret = paw32xx_write_reg(dev, PAW32XX_CPI_Y, val); 296 + if (ret < 0) { 297 + return ret; 298 + } 299 + 300 + ret = paw32xx_write_reg(dev, PAW32XX_WRITE_PROTECT, WRITE_PROTECT_ENABLE); 301 + if (ret < 0) { 302 + return ret; 303 + } 304 + 305 + return 0; 306 + } 307 + 308 + int paw32xx_force_awake(const struct device *dev, bool enable) 309 + { 310 + uint8_t val = enable ? 0 : OPERATION_MODE_SLP_MASK; 311 + int ret; 312 + 313 + ret = paw32xx_write_reg(dev, PAW32XX_WRITE_PROTECT, WRITE_PROTECT_DISABLE); 314 + if (ret < 0) { 315 + return ret; 316 + } 317 + 318 + ret = paw32xx_update_reg(dev, PAW32XX_OPERATION_MODE, 319 + OPERATION_MODE_SLP_MASK, val); 320 + if (ret < 0) { 321 + return ret; 322 + } 323 + 324 + ret = paw32xx_write_reg(dev, PAW32XX_WRITE_PROTECT, WRITE_PROTECT_ENABLE); 325 + if (ret < 0) { 326 + return ret; 327 + } 328 + 329 + return 0; 330 + } 331 + 332 + static int paw32xx_configure(const struct device *dev) 333 + { 334 + const struct paw32xx_config *cfg = dev->config; 335 + uint8_t val; 336 + int ret; 337 + 338 + ret = paw32xx_read_reg(dev, PAW32XX_PRODUCT_ID1, &val); 339 + if (ret < 0) { 340 + return ret; 341 + } 342 + 343 + if (val != PRODUCT_ID_PAW32XX) { 344 + LOG_ERR("Invalid product id: %02x", val); 345 + return -ENOTSUP; 346 + } 347 + 348 + ret = paw32xx_update_reg(dev, PAW32XX_CONFIGURATION, 349 + CONFIGURATION_RESET, CONFIGURATION_RESET); 350 + if (ret < 0) { 351 + return ret; 352 + } 353 + 354 + k_sleep(K_MSEC(RESET_DELAY_MS)); 355 + 356 + if (cfg->res_cpi > 0) { 357 + paw32xx_set_resolution(dev, cfg->res_cpi); 358 + } 359 + 360 + paw32xx_force_awake(dev, cfg->force_awake); 361 + 362 + return 0; 363 + } 364 + 365 + static int paw32xx_init(const struct device *dev) 366 + { 367 + const struct paw32xx_config *cfg = dev->config; 368 + struct paw32xx_data *data = dev->data; 369 + int ret; 370 + 371 + if (!spi_is_ready_dt(&cfg->spi)) { 372 + LOG_ERR("%s is not ready", cfg->spi.bus->name); 373 + return -ENODEV; 374 + } 375 + 376 + data->dev = dev; 377 + 378 + k_work_init(&data->motion_work, paw32xx_motion_work_handler); 379 + // Initialize the timer for delayed motion checks 380 + k_timer_init(&data->motion_timer, paw32xx_motion_timer_handler, NULL); 381 + 382 + if (!gpio_is_ready_dt(&cfg->irq_gpio)) { 383 + LOG_ERR("%s is not ready", cfg->irq_gpio.port->name); 384 + return -ENODEV; 385 + } 386 + 387 + ret = gpio_pin_configure_dt(&cfg->irq_gpio, GPIO_INPUT); 388 + if (ret != 0) { 389 + LOG_ERR("Motion pin configuration failed: %d", ret); 390 + return ret; 391 + } 392 + 393 + gpio_init_callback(&data->motion_cb, paw32xx_motion_handler, 394 + BIT(cfg->irq_gpio.pin)); 395 + 396 + ret = gpio_add_callback_dt(&cfg->irq_gpio, &data->motion_cb); 397 + if (ret < 0) { 398 + LOG_ERR("Could not set motion callback: %d", ret); 399 + return ret; 400 + } 401 + 402 + ret = paw32xx_configure(dev); 403 + if (ret != 0) { 404 + LOG_ERR("Device configuration failed: %d", ret); 405 + return ret; 406 + } 407 + 408 + ret = gpio_pin_interrupt_configure_dt(&cfg->irq_gpio, 409 + GPIO_INT_EDGE_TO_ACTIVE); 410 + if (ret != 0) { 411 + LOG_ERR("Motion interrupt configuration failed: %d", ret); 412 + return ret; 413 + } 414 + 415 + ret = pm_device_runtime_enable(dev); 416 + if (ret < 0) { 417 + LOG_ERR("Failed to enable runtime power management: %d", ret); 418 + return ret; 419 + } 420 + 421 + return 0; 422 + } 423 + 424 + #ifdef CONFIG_PM_DEVICE 425 + static int paw32xx_pm_action(const struct device *dev, 426 + enum pm_device_action action) 427 + { 428 + int ret; 429 + uint8_t val; 430 + 431 + switch (action) { 432 + case PM_DEVICE_ACTION_SUSPEND: 433 + val = CONFIGURATION_PD_ENH; 434 + break; 435 + case PM_DEVICE_ACTION_RESUME: 436 + val = 0; 437 + break; 438 + default: 439 + return -ENOTSUP; 440 + } 441 + 442 + ret = paw32xx_update_reg(dev, PAW32XX_CONFIGURATION, 443 + CONFIGURATION_PD_ENH, val); 444 + if (ret < 0) { 445 + return ret; 446 + } 447 + 448 + return 0; 449 + } 450 + #endif 451 + 452 + #define PAW32XX_SPI_MODE (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | \ 453 + SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_TRANSFER_MSB) 454 + 455 + #define PAW32XX_INIT(n) \ 456 + BUILD_ASSERT(IN_RANGE(DT_INST_PROP_OR(n, res_cpi, RES_MIN), \ 457 + RES_MIN, RES_MAX), "invalid res-cpi"); \ 458 + \ 459 + static const struct paw32xx_config paw32xx_cfg_##n = { \ 460 + .spi = SPI_DT_SPEC_INST_GET(n, PAW32XX_SPI_MODE, 0), \ 461 + .irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios), \ 462 + .res_cpi = DT_INST_PROP_OR(n, res_cpi, -1), \ 463 + .force_awake = DT_INST_PROP(n, force_awake), \ 464 + }; \ 465 + \ 466 + static struct paw32xx_data paw32xx_data_##n; \ 467 + \ 468 + PM_DEVICE_DT_INST_DEFINE(n, paw32xx_pm_action); \ 469 + \ 470 + DEVICE_DT_INST_DEFINE(n, paw32xx_init, PM_DEVICE_DT_INST_GET(n), \ 471 + &paw32xx_data_##n, &paw32xx_cfg_##n, \ 472 + POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \ 473 + NULL); 474 + 475 + DT_INST_FOREACH_STATUS_OKAY(PAW32XX_INIT) 476 + 477 + #endif // DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
+6
zephyr/module.yml
··· 1 + name: zmk-driver-paw3222 2 + build: 3 + cmake: . 4 + kconfig: Kconfig 5 + settings: 6 + dts_root: .