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