blob: b544b640ef9c93b7a0a4da20c3ee18d2c53f5a5e [file] [log] [blame]
/*
* Copyright (c) 2022, Hiroki Tada
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://datasheetspdf.com/pdf/1323325/Hamamatsu/S11059-02DT/1
*/
#define DT_DRV_COMPAT hamamatsu_s11059
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
LOG_MODULE_REGISTER(S11059, CONFIG_SENSOR_LOG_LEVEL);
/* register address */
#define S11059_REG_ADDR_CONTROL 0x00
#define S11059_REG_ADDR_MANUAL_TIMING 0x01
#define S11059_REG_ADDR_DATA 0x03
/* control bit */
#define S11059_CONTROL_GAIN 3
#define S11059_CONTROL_STANDBY_MONITOR 5
#define S11059_CONTROL_STADBY 6
#define S11059_CONTROL_ADC_RESET 7
/* bit mask for control */
#define S11059_BIT_MASK_INTEGRATION_TIME 0x03
#define S11059_BIT_MASK_CONTROL_STANDBY_MONITOR 0x20
/* factors for converting sensor samples to Lux */
#define S11059_CONVERT_FACTOR_LOW_RED (112)
#define S11059_CONVERT_FACTOR_LOW_GREEN (83)
#define S11059_CONVERT_FACTOR_LOW_BLUE (44)
#define S11059_CONVERT_FACTOR_LOW_IR (3 * 10)
#define S11059_CONVERT_FACTOR_HIGH_RED (117 * 10)
#define S11059_CONVERT_FACTOR_HIGH_GREEN (85 * 10)
#define S11059_CONVERT_FACTOR_HIGH_BLUE (448)
#define S11059_CONVERT_FACTOR_HIGH_IR (30 * 10)
#define S11059_INTEGRATION_TIME_MODE_00 175
#define S11059_INTEGRATION_TIME_MODE_01 2800
#define S11059_INTEGRATION_TIME_MODE_10 44800
#define S11059_INTEGRATION_TIME_MODE_11 358400
#define S11059_WAIT_PER_LOOP 400
#define S11059_INITIAL_CONTROL 0x04
#define S11059_MAX_MANUAL_TIMING UINT16_MAX
#define S11059_CARRY_UP 10000
#define S11059_NUM_GAIN_MODE 2
enum s11059_channel {
RED,
GREEN,
BLUE,
IR,
NUM_OF_COLOR_CHANNELS
};
struct s11059_dev_config {
struct i2c_dt_spec bus;
uint8_t gain;
int64_t integration_time; /* integration period (unit: us) */
};
struct s11059_data {
uint16_t samples[NUM_OF_COLOR_CHANNELS];
};
static const uint16_t convert_factors[S11059_NUM_GAIN_MODE][NUM_OF_COLOR_CHANNELS] = {
{S11059_CONVERT_FACTOR_LOW_RED, S11059_CONVERT_FACTOR_LOW_GREEN,
S11059_CONVERT_FACTOR_LOW_BLUE, S11059_CONVERT_FACTOR_LOW_IR},
{S11059_CONVERT_FACTOR_HIGH_RED, S11059_CONVERT_FACTOR_HIGH_GREEN,
S11059_CONVERT_FACTOR_HIGH_BLUE, S11059_CONVERT_FACTOR_HIGH_IR}};
/* Integration timing in Manual integration mode */
static const uint32_t integ_time_factor[] = {
S11059_INTEGRATION_TIME_MODE_00, S11059_INTEGRATION_TIME_MODE_01,
S11059_INTEGRATION_TIME_MODE_10, S11059_INTEGRATION_TIME_MODE_11};
static int s11059_convert_channel_to_index(enum sensor_channel chan)
{
switch (chan) {
case SENSOR_CHAN_RED:
return RED;
case SENSOR_CHAN_GREEN:
return GREEN;
case SENSOR_CHAN_BLUE:
return BLUE;
default:
return IR;
}
}
static int s11059_samples_read(const struct device *dev, uint8_t addr, uint16_t *val, uint32_t size)
{
const struct s11059_dev_config *cfg = dev->config;
int rc;
if (size < NUM_OF_COLOR_CHANNELS * 2) {
return -EINVAL;
}
rc = i2c_burst_read_dt(&cfg->bus, addr, (uint8_t *)val, size);
if (rc < 0) {
return rc;
}
for (size_t i = 0; i < NUM_OF_COLOR_CHANNELS; i++) {
val[i] = sys_be16_to_cpu(val[i]);
}
return 0;
}
static int s11059_control_write(const struct device *dev, uint8_t control)
{
const struct s11059_dev_config *cfg = dev->config;
const uint8_t opcode[] = {S11059_REG_ADDR_CONTROL, control};
return i2c_write_dt(&cfg->bus, opcode, sizeof(opcode));
}
static int s11059_manual_timing_write(const struct device *dev, uint16_t manual_time)
{
const struct s11059_dev_config *cfg = dev->config;
const uint8_t opcode[] = {S11059_REG_ADDR_MANUAL_TIMING, manual_time >> 8,
manual_time & 0xFF};
return i2c_write_dt(&cfg->bus, opcode, sizeof(opcode));
}
static int s11059_start_measurement(const struct device *dev)
{
const struct s11059_dev_config *cfg = dev->config;
uint8_t control;
int rc;
/* read current control */
rc = i2c_reg_read_byte_dt(&cfg->bus, S11059_REG_ADDR_CONTROL, &control);
if (rc < 0) {
LOG_ERR("%s, Failed to read current control.", dev->name);
return rc;
}
/* reset adc block */
WRITE_BIT(control, S11059_CONTROL_ADC_RESET, 1);
WRITE_BIT(control, S11059_CONTROL_STADBY, 0);
rc = s11059_control_write(dev, control);
if (rc < 0) {
LOG_ERR("%s, Failed to reset adc.", dev->name);
return rc;
}
/* start device */
WRITE_BIT(control, S11059_CONTROL_ADC_RESET, 0);
rc = s11059_control_write(dev, control);
if (rc < 0) {
LOG_ERR("%s, Failed to start device.", dev->name);
return rc;
}
return 0;
}
static int s11059_integ_time_calculate(const struct device *dev, uint16_t *manual_time,
uint8_t *mode)
{
const struct s11059_dev_config *cfg = dev->config;
int64_t tmp;
if (cfg->integration_time < integ_time_factor[0]) {
*mode = 0;
*manual_time = 1;
} else {
*manual_time = S11059_MAX_MANUAL_TIMING;
for (uint8_t i = 0; i < ARRAY_SIZE(integ_time_factor); i++) {
*mode = i;
tmp = cfg->integration_time / integ_time_factor[i];
if (tmp < S11059_MAX_MANUAL_TIMING) {
*manual_time = (uint16_t)tmp;
break;
}
}
}
return 0;
}
static int s11059_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
const struct s11059_dev_config *cfg = dev->config;
struct s11059_data *drv_data = dev->data;
uint16_t values[NUM_OF_COLOR_CHANNELS];
uint8_t control;
int rc;
if (chan != SENSOR_CHAN_ALL) {
LOG_ERR("%s, Unsupported sensor channel", dev->name);
return -ENOTSUP;
}
rc = s11059_start_measurement(dev);
if (rc < 0) {
LOG_ERR("%s, Failed to start measurement.", dev->name);
return rc;
}
do {
rc = i2c_reg_read_byte_dt(&cfg->bus, S11059_REG_ADDR_CONTROL, &control);
if (rc < 0) {
LOG_ERR("%s, Failed to read control.", dev->name);
return rc;
}
k_usleep(S11059_WAIT_PER_LOOP);
} while (!(control & S11059_BIT_MASK_CONTROL_STANDBY_MONITOR));
rc = s11059_samples_read(dev, S11059_REG_ADDR_DATA, values, sizeof(values));
if (rc < 0) {
LOG_ERR("%s, Failed to get sample.", dev->name);
return rc;
}
for (size_t i = 0; i < NUM_OF_COLOR_CHANNELS; i++) {
drv_data->samples[i] = values[i];
}
return 0;
}
static int s11059_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
const struct s11059_dev_config *cfg = dev->config;
struct s11059_data *drv_data = dev->data;
const uint8_t index = s11059_convert_channel_to_index(chan);
const uint16_t factor = convert_factors[cfg->gain][index];
uint32_t meas_value;
meas_value = drv_data->samples[index] * S11059_CARRY_UP / factor;
val->val1 = meas_value / (S11059_CARRY_UP / 10);
val->val2 = meas_value % (S11059_CARRY_UP / 10);
return 0;
}
static int s11059_init(const struct device *dev)
{
const struct s11059_dev_config *cfg = dev->config;
uint8_t control = S11059_INITIAL_CONTROL;
uint16_t manual_time;
uint8_t timing_mode;
int rc;
/* device set */
if (!i2c_is_ready_dt(&cfg->bus)) {
LOG_ERR("%s, device is not ready.", dev->name);
return -ENODEV;
}
rc = s11059_integ_time_calculate(dev, &manual_time, &timing_mode);
if (rc < 0) {
LOG_ERR("%s, Failed to calculate manual timing.", dev->name);
return rc;
}
rc = s11059_manual_timing_write(dev, manual_time);
if (rc < 0) {
LOG_ERR("%s, Failed to set manual timing.", dev->name);
return rc;
}
/* set integration time mode and gain*/
control |= timing_mode & S11059_BIT_MASK_INTEGRATION_TIME;
WRITE_BIT(control, S11059_CONTROL_GAIN, cfg->gain);
rc = s11059_control_write(dev, control);
if (rc < 0) {
LOG_ERR("%s, Failed to set gain and integration time.", dev->name);
return rc;
}
return 0;
}
static const struct sensor_driver_api s11059_driver_api = {
.sample_fetch = s11059_sample_fetch,
.channel_get = s11059_channel_get,
};
#define S11059_INST(inst) \
static struct s11059_data s11059_data_##inst; \
static const struct s11059_dev_config s11059_config_##inst = { \
.bus = I2C_DT_SPEC_INST_GET(inst), \
.gain = DT_INST_PROP(inst, high_gain), \
.integration_time = DT_INST_PROP(inst, integration_time)}; \
SENSOR_DEVICE_DT_INST_DEFINE(inst, s11059_init, NULL, &s11059_data_##inst, \
&s11059_config_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &s11059_driver_api);
DT_INST_FOREACH_STATUS_OKAY(S11059_INST)