forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapds9306.c
377 lines (312 loc) · 10.5 KB
/
apds9306.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/* Copyright (c) 2024 Daniel Kampert
* Author: Daniel Kampert <DanielKampert@kampis-Elektroecke.de>
*/
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#define APDS9306_REGISTER_MAIN_CTRL 0x00
#define APDS9306_REGISTER_ALS_MEAS_RATE 0x04
#define APDS9306_REGISTER_ALS_GAIN 0x05
#define APDS9306_REGISTER_PART_ID 0x06
#define APDS9306_REGISTER_MAIN_STATUS 0x07
#define APDS9306_REGISTER_CLEAR_DATA_0 0x0A
#define APDS9306_REGISTER_CLEAR_DATA_1 0x0B
#define APDS9306_REGISTER_CLEAR_DATA_2 0x0C
#define APDS9306_REGISTER_ALS_DATA_0 0x0D
#define APDS9306_REGISTER_ALS_DATA_1 0x0E
#define APDS9306_REGISTER_ALS_DATA_2 0x0F
#define APDS9306_REGISTER_INT_CFG 0x19
#define APDS9306_REGISTER_INT_PERSISTENCE 0x1A
#define APDS9306_REGISTER_ALS_THRES_UP_0 0x21
#define APDS9306_REGISTER_ALS_THRES_UP_1 0x22
#define APDS9306_REGISTER_ALS_THRES_UP_2 0x23
#define APDS9306_REGISTER_ALS_THRES_LOW_0 0x24
#define APDS9306_REGISTER_ALS_THRES_LOW_1 0x25
#define APDS9306_REGISTER_ALS_THRES_LOW_2 0x26
#define APDS9306_REGISTER_ALS_THRES_VAR 0x27
#define ADPS9306_BIT_ALS_EN BIT(0x01)
#define ADPS9306_BIT_ALS_DATA_STATUS BIT(0x03)
#define APDS9306_BIT_SW_RESET BIT(0x04)
#define ADPS9306_BIT_ALS_INTERRUPT_STATUS BIT(0x03)
#define APDS9306_BIT_POWER_ON_STATUS BIT(0x05)
#define APDS_9306_065_CHIP_ID 0xB3
#define APDS_9306_CHIP_ID 0xB1
#define DT_DRV_COMPAT avago_apds9306
LOG_MODULE_REGISTER(avago_apds9306, CONFIG_SENSOR_LOG_LEVEL);
struct apds9306_data {
uint32_t light;
};
struct apds9306_config {
struct i2c_dt_spec i2c;
uint8_t resolution;
uint16_t frequency;
uint8_t gain;
};
struct apds9306_worker_item_t {
struct k_work_delayable dwork;
const struct device *dev;
} apds9306_worker_item;
static uint32_t apds9306_get_time_for_resolution(uint8_t value)
{
switch (value) {
case 0:
return 400;
case 1:
return 200;
case 2:
return 100;
case 3:
return 50;
case 4:
return 25;
case 5:
return 4;
default:
return 100;
}
}
static int apds9306_enable(const struct device *dev)
{
const struct apds9306_config *config = dev->config;
return i2c_reg_update_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_CTRL,
ADPS9306_BIT_ALS_EN, ADPS9306_BIT_ALS_EN);
}
static int apds9306_standby(const struct device *dev)
{
const struct apds9306_config *config = dev->config;
return i2c_reg_update_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_CTRL,
ADPS9306_BIT_ALS_EN, 0x00);
}
static void apds9306_worker(struct k_work *p_work)
{
uint8_t buffer[3];
uint8_t reg;
struct k_work_delayable *dwork = k_work_delayable_from_work(p_work);
struct apds9306_worker_item_t *item =
CONTAINER_OF(dwork, struct apds9306_worker_item_t, dwork);
struct apds9306_data *data = item->dev->data;
const struct apds9306_config *config = item->dev->config;
if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_STATUS, &buffer[0])) {
LOG_ERR("Failed to read ALS status!");
return;
}
if (!(buffer[0] & ADPS9306_BIT_ALS_DATA_STATUS)) {
LOG_DBG("No data ready!");
return;
}
if (apds9306_standby(item->dev) != 0) {
LOG_ERR("Can not disable ALS!");
return;
}
reg = APDS9306_REGISTER_ALS_DATA_0;
if (i2c_write_read_dt(&config->i2c, ®, sizeof(reg), &buffer, sizeof(buffer)) < 0) {
return;
}
data->light = sys_get_le24(buffer);
LOG_DBG("Last measurement: %u", data->light);
}
static int apds9306_attr_set(const struct device *dev, enum sensor_channel channel,
enum sensor_attribute attribute, const struct sensor_value *value)
{
uint8_t reg;
uint8_t mask;
uint8_t temp;
const struct apds9306_config *config = dev->config;
if (channel != SENSOR_CHAN_LIGHT) {
return -ENOTSUP;
}
if (attribute == SENSOR_ATTR_SAMPLING_FREQUENCY) {
reg = APDS9306_REGISTER_ALS_MEAS_RATE;
mask = GENMASK(2, 0);
temp = FIELD_PREP(0x07, value->val1);
} else if (attribute == SENSOR_ATTR_GAIN) {
reg = APDS9306_REGISTER_ALS_GAIN;
mask = GENMASK(2, 0);
temp = FIELD_PREP(0x07, value->val1);
} else if (attribute == SENSOR_ATTR_RESOLUTION) {
reg = APDS9306_REGISTER_ALS_MEAS_RATE;
mask = GENMASK(7, 4);
temp = FIELD_PREP(0x07, value->val1) << 0x04;
} else {
return -ENOTSUP;
}
if (i2c_reg_update_byte_dt(&config->i2c, reg, mask, temp)) {
LOG_ERR("Failed to set sensor attribute!");
return -EFAULT;
}
return 0;
}
static int apds9306_attr_get(const struct device *dev, enum sensor_channel channel,
enum sensor_attribute attribute, struct sensor_value *value)
{
uint8_t mask;
uint8_t temp;
uint8_t reg;
const struct apds9306_config *config = dev->config;
if (channel != SENSOR_CHAN_LIGHT) {
return -ENOTSUP;
}
if (attribute == SENSOR_ATTR_SAMPLING_FREQUENCY) {
reg = APDS9306_REGISTER_ALS_MEAS_RATE;
mask = 0x00;
} else if (attribute == SENSOR_ATTR_GAIN) {
reg = APDS9306_REGISTER_ALS_GAIN;
mask = 0x00;
} else if (attribute == SENSOR_ATTR_RESOLUTION) {
reg = APDS9306_REGISTER_ALS_MEAS_RATE;
mask = 0x04;
} else {
return -ENOTSUP;
}
if (i2c_reg_read_byte_dt(&config->i2c, reg, &temp)) {
LOG_ERR("Failed to read sensor attribute!");
return -EFAULT;
}
value->val1 = (temp >> mask) & 0x07;
value->val2 = 0;
return 0;
}
static int apds9306_sample_fetch(const struct device *dev, enum sensor_channel channel)
{
uint8_t buffer;
uint8_t resolution;
uint16_t delay;
const struct apds9306_config *config = dev->config;
if ((channel != SENSOR_CHAN_ALL) && (channel != SENSOR_CHAN_LIGHT)) {
return -ENOTSUP;
}
LOG_DBG("Start a new measurement...");
if (apds9306_enable(dev) != 0) {
LOG_ERR("Can not enable ALS!");
return -EFAULT;
}
/* Get the measurement resolution. */
if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_MEAS_RATE, &buffer)) {
LOG_ERR("Failed reading resolution");
return -EFAULT;
}
/* Convert the resolution into a delay time and wait for the result. */
resolution = (buffer >> 4) & 0x07;
delay = apds9306_get_time_for_resolution(resolution);
LOG_DBG("Measurement resolution: %u", resolution);
LOG_DBG("Wait for %u ms", delay);
/* We add a bit more delay to cover the startup time etc. */
if (!k_work_delayable_is_pending(&apds9306_worker_item.dwork)) {
LOG_DBG("Schedule new work");
apds9306_worker_item.dev = dev;
k_work_init_delayable(&apds9306_worker_item.dwork, apds9306_worker);
k_work_schedule(&apds9306_worker_item.dwork, K_MSEC(delay + 100));
} else {
LOG_DBG("Work pending. Wait for completion.");
}
return 0;
}
static int apds9306_channel_get(const struct device *dev, enum sensor_channel channel,
struct sensor_value *value)
{
struct apds9306_data *data = dev->data;
if (channel != SENSOR_CHAN_LIGHT) {
return -ENOTSUP;
}
value->val1 = data->light;
value->val2 = 0;
return 0;
}
static int apds9306_sensor_setup(const struct device *dev)
{
uint32_t now;
uint8_t temp;
const struct apds9306_config *config = dev->config;
/* Wait for the device to become ready after a possible power cycle. */
now = k_uptime_get_32();
do {
if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_STATUS, &temp)) {
LOG_ERR("Failed reading sensor status!");
return -EFAULT;
}
/* We wait 100 ms maximum for the device to become ready. */
if ((k_uptime_get_32() - now) > 100) {
LOG_ERR("Sensor timeout!");
return -EFAULT;
}
k_msleep(10);
} while (temp & APDS9306_BIT_POWER_ON_STATUS);
if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_PART_ID, &temp)) {
LOG_ERR("Failed reading chip id!");
return -EFAULT;
}
if ((temp != APDS_9306_CHIP_ID) && (temp != APDS_9306_065_CHIP_ID)) {
LOG_ERR("Invalid chip id! Found 0x%X!", temp);
return -EFAULT;
}
if (temp == APDS_9306_CHIP_ID) {
LOG_DBG("APDS-9306 found!");
} else if (temp == APDS_9306_065_CHIP_ID) {
LOG_DBG("APDS-9306-065 found!");
}
/* Reset the sensor. */
if (i2c_reg_write_byte_dt(&config->i2c, APDS9306_REGISTER_MAIN_CTRL,
APDS9306_BIT_SW_RESET)) {
LOG_ERR("Can not reset the sensor!");
return -EFAULT;
}
k_msleep(10);
/* Perform a dummy read to avoid bus errors after the reset. See */
/* https://lore.kernel.org/lkml/ab1d9746-4d23-efcc-0ee1-d2b8c634becd@tweaklogic.com/ */
if (i2c_reg_read_byte_dt(&config->i2c, APDS9306_REGISTER_PART_ID, &temp)) {
LOG_ERR("Failed reading chip id!");
return -EFAULT;
}
return 0;
}
static int apds9306_init(const struct device *dev)
{
uint8_t value;
const struct apds9306_config *config = dev->config;
LOG_DBG("Start to initialize APDS9306...");
if (!i2c_is_ready_dt(&config->i2c)) {
LOG_ERR("Bus device is not ready!");
return -EINVAL;
}
if (apds9306_sensor_setup(dev)) {
LOG_ERR("Failed to setup device!");
return -EFAULT;
}
value = ((config->resolution & 0x07) << 4) | (config->frequency & 0x0F);
LOG_DBG("Write configuration 0x%x to register 0x%x", value,
APDS9306_REGISTER_ALS_MEAS_RATE);
if (i2c_reg_write_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_MEAS_RATE, value)) {
return -EFAULT;
}
value = config->gain;
LOG_DBG("Write configuration 0x%x to register 0x%x", value, APDS9306_REGISTER_ALS_GAIN);
if (i2c_reg_write_byte_dt(&config->i2c, APDS9306_REGISTER_ALS_GAIN, value)) {
return -EFAULT;
}
LOG_DBG("APDS9306 initialization successful!");
return 0;
}
static DEVICE_API(sensor, apds9306_driver_api) = {
.attr_set = apds9306_attr_set,
.attr_get = apds9306_attr_get,
.sample_fetch = apds9306_sample_fetch,
.channel_get = apds9306_channel_get,
};
#define APDS9306(inst) \
static struct apds9306_data apds9306_data_##inst; \
static const struct apds9306_config apds9306_config_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
.resolution = DT_INST_PROP(inst, resolution), \
.gain = DT_INST_PROP(inst, gain), \
.frequency = DT_INST_PROP(inst, frequency), \
}; \
\
SENSOR_DEVICE_DT_INST_DEFINE(inst, apds9306_init, NULL, &apds9306_data_##inst, \
&apds9306_config_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &apds9306_driver_api);
DT_INST_FOREACH_STATUS_OKAY(APDS9306)