From fd5b8a16dd209176fb848bcf088479e595a017a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Anikiel?= Date: Tue, 1 Aug 2023 12:09:27 +0000 Subject: [PATCH] drivers: sensor: Add F75303 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add driver for F75303 temperature sensor IC. Signed-off-by: Paweł Anikiel --- drivers/sensor/CMakeLists.txt | 1 + drivers/sensor/Kconfig | 1 + drivers/sensor/f75303/CMakeLists.txt | 5 + drivers/sensor/f75303/Kconfig | 22 +++ drivers/sensor/f75303/f75303.c | 198 ++++++++++++++++++++++++ drivers/sensor/f75303/f75303.h | 30 ++++ drivers/sensor/f75303/f75303_emul.c | 176 +++++++++++++++++++++ dts/bindings/sensor/fintek,f75303.yaml | 10 ++ include/zephyr/drivers/sensor/f75303.h | 17 ++ tests/drivers/build_all/sensor/i2c.dtsi | 5 + 10 files changed, 465 insertions(+) create mode 100644 drivers/sensor/f75303/CMakeLists.txt create mode 100644 drivers/sensor/f75303/Kconfig create mode 100644 drivers/sensor/f75303/f75303.c create mode 100644 drivers/sensor/f75303/f75303.h create mode 100644 drivers/sensor/f75303/f75303_emul.c create mode 100644 dts/bindings/sensor/fintek,f75303.yaml create mode 100644 include/zephyr/drivers/sensor/f75303.h diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 00cbc655e35b5d..f7b706795d8778 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -32,6 +32,7 @@ add_subdirectory_ifdef(CONFIG_DPS310 dps310) add_subdirectory_ifdef(CONFIG_DS18B20 ds18b20) add_subdirectory_ifdef(CONFIG_ENS210 ens210) add_subdirectory_ifdef(CONFIG_ESP32_TEMP esp32_temp) +add_subdirectory_ifdef(CONFIG_F75303 f75303) add_subdirectory_ifdef(CONFIG_FDC2X1X fdc2x1x) add_subdirectory_ifdef(CONFIG_FXAS21002 fxas21002) add_subdirectory_ifdef(CONFIG_FXOS8700 fxos8700) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 26787acf4e50a2..07f2dc917ff881 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -88,6 +88,7 @@ source "drivers/sensor/dps310/Kconfig" source "drivers/sensor/ds18b20/Kconfig" source "drivers/sensor/ens210/Kconfig" source "drivers/sensor/esp32_temp/Kconfig" +source "drivers/sensor/f75303/Kconfig" source "drivers/sensor/fdc2x1x/Kconfig" source "drivers/sensor/fxas21002/Kconfig" source "drivers/sensor/fxos8700/Kconfig" diff --git a/drivers/sensor/f75303/CMakeLists.txt b/drivers/sensor/f75303/CMakeLists.txt new file mode 100644 index 00000000000000..9441b13f0549b5 --- /dev/null +++ b/drivers/sensor/f75303/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(f75303.c) +zephyr_library_sources_ifdef(CONFIG_EMUL_F75303 f75303_emul.c) diff --git a/drivers/sensor/f75303/Kconfig b/drivers/sensor/f75303/Kconfig new file mode 100644 index 00000000000000..31c3865275d928 --- /dev/null +++ b/drivers/sensor/f75303/Kconfig @@ -0,0 +1,22 @@ +# F75303 temperature sensor configuration options + +# Copyright (c) 2023 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +config F75303 + bool "F75303 Temperature Sensor" + default y + depends on DT_HAS_FINTEK_F75303_ENABLED + select I2C + help + Enable the driver for Fintek F75303 Temperature Sensor. + This device has three temperature channels - one local (on-chip), + and two remote. + +config EMUL_F75303 + bool "Emulator for F75303" + default y + depends on F75303 + depends on EMUL + help + Enable the hardware emulator for F75303 Temperature Sensor. diff --git a/drivers/sensor/f75303/f75303.c b/drivers/sensor/f75303/f75303.c new file mode 100644 index 00000000000000..dd656fc26e062c --- /dev/null +++ b/drivers/sensor/f75303/f75303.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2023 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT fintek_f75303 + +#include +#include +#include +#include +#include +#include +#include +#include "f75303.h" + +#define F75303_SAMPLE_INT_SHIFT 3 +#define F75303_SAMPLE_FRAC_MASK GENMASK(2, 0) +#define F75303_SAMPLE_MICROCELSIUS_PER_BIT 125000 + +LOG_MODULE_REGISTER(F75303, CONFIG_SENSOR_LOG_LEVEL); + +static int f75303_fetch(const struct i2c_dt_spec *i2c, + uint8_t off_h, uint8_t off_l, uint16_t *sample) +{ + uint8_t val_h; + uint8_t val_l; + int res; + + res = i2c_reg_read_byte_dt(i2c, off_h, &val_h); + if (res) { + return res; + } + + res = i2c_reg_read_byte_dt(i2c, off_l, &val_l); + if (res) { + return res; + } + + *sample = val_h << 3 | val_l >> 5; + + return 0; +} + +static int f75303_fetch_local(const struct device *dev) +{ + struct f75303_data *data = dev->data; + const struct f75303_config *config = dev->config; + + return f75303_fetch(&config->i2c, + F75303_LOCAL_TEMP_H, + F75303_LOCAL_TEMP_L, + &data->sample_local); +} + +static int f75303_fetch_remote1(const struct device *dev) +{ + struct f75303_data *data = dev->data; + const struct f75303_config *config = dev->config; + + return f75303_fetch(&config->i2c, + F75303_REMOTE1_TEMP_H, + F75303_REMOTE1_TEMP_L, + &data->sample_remote1); +} + +static int f75303_fetch_remote2(const struct device *dev) +{ + struct f75303_data *data = dev->data; + const struct f75303_config *config = dev->config; + + return f75303_fetch(&config->i2c, + F75303_REMOTE2_TEMP_H, + F75303_REMOTE2_TEMP_L, + &data->sample_remote2); +} + +static int f75303_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + enum pm_device_state pm_state; + int res; + + (void)pm_device_state_get(dev, &pm_state); + if (pm_state != PM_DEVICE_STATE_ACTIVE) { + return -EIO; + } + + switch ((uint32_t)chan) { + case SENSOR_CHAN_ALL: + res = f75303_fetch_local(dev); + if (res) { + break; + } + res = f75303_fetch_remote1(dev); + if (res) { + break; + } + res = f75303_fetch_remote2(dev); + break; + case SENSOR_CHAN_AMBIENT_TEMP: + return f75303_fetch_local(dev); + case SENSOR_CHAN_F75303_REMOTE1: + return f75303_fetch_remote1(dev); + case SENSOR_CHAN_F75303_REMOTE2: + return f75303_fetch_remote2(dev); + default: + return -ENOTSUP; + } + + return res; +} + +static int f75303_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct f75303_data *data = dev->data; + uint16_t sample; + + switch ((uint32_t)chan) { + case SENSOR_CHAN_AMBIENT_TEMP: + sample = data->sample_local; + break; + case SENSOR_CHAN_F75303_REMOTE1: + sample = data->sample_remote1; + break; + case SENSOR_CHAN_F75303_REMOTE2: + sample = data->sample_remote2; + break; + default: + return -ENOTSUP; + } + + /* + * The reading is given in steps of 0.125 degrees celsius, i.e. the + * temperature in degrees celsius is equal to sample / 8. + */ + val->val1 = sample >> F75303_SAMPLE_INT_SHIFT; + val->val2 = (sample & F75303_SAMPLE_FRAC_MASK) * F75303_SAMPLE_MICROCELSIUS_PER_BIT; + + return 0; +} + +static const struct sensor_driver_api f75303_driver_api = { + .sample_fetch = f75303_sample_fetch, + .channel_get = f75303_channel_get, +}; + +static int f75303_init(const struct device *dev) +{ + const struct f75303_config *config = dev->config; + int res = 0; + + if (!i2c_is_ready_dt(&config->i2c)) { + LOG_ERR("I2C device not ready"); + return -ENODEV; + } + +#ifdef CONFIG_PM_DEVICE_RUNTIME + pm_device_init_suspended(dev); + + res = pm_device_runtime_enable(dev); + if (res) { + LOG_ERR("Failed to enable runtime power management"); + } +#endif + + return res; +} + +#ifdef CONFIG_PM_DEVICE +static int f75303_pm_action(const struct device *dev, enum pm_device_action action) +{ + switch (action) { + case PM_DEVICE_ACTION_TURN_ON: + case PM_DEVICE_ACTION_RESUME: + case PM_DEVICE_ACTION_TURN_OFF: + case PM_DEVICE_ACTION_SUSPEND: + return 0; + default: + return -ENOTSUP; + } +} +#endif + +#define F75303_INST(inst) \ + static struct f75303_data f75303_data_##inst; \ + static const struct f75303_config f75303_config_##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + }; \ + PM_DEVICE_DT_INST_DEFINE(inst, f75303_pm_action); \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, f75303_init, PM_DEVICE_DT_INST_GET(inst), \ + &f75303_data_##inst, &f75303_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &f75303_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(F75303_INST) diff --git a/drivers/sensor/f75303/f75303.h b/drivers/sensor/f75303/f75303.h new file mode 100644 index 00000000000000..8b7ceb647484f9 --- /dev/null +++ b/drivers/sensor/f75303/f75303.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_F75303_F75303_H_ +#define ZEPHYR_DRIVERS_SENSOR_F75303_F75303_H_ + +#include +#include + +#define F75303_LOCAL_TEMP_H 0x00 +#define F75303_REMOTE1_TEMP_H 0x01 +#define F75303_REMOTE1_TEMP_L 0x10 +#define F75303_REMOTE2_TEMP_H 0x23 +#define F75303_REMOTE2_TEMP_L 0x24 +#define F75303_LOCAL_TEMP_L 0x29 + +struct f75303_data { + uint16_t sample_local; + uint16_t sample_remote1; + uint16_t sample_remote2; +}; + +struct f75303_config { + struct i2c_dt_spec i2c; +}; + +#endif diff --git a/drivers/sensor/f75303/f75303_emul.c b/drivers/sensor/f75303/f75303_emul.c new file mode 100644 index 00000000000000..7b55e46bbab3ce --- /dev/null +++ b/drivers/sensor/f75303/f75303_emul.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT fintek_f75303 + +#include +#include +#include +#include +#include +#include +#include +#include "f75303.h" + +LOG_MODULE_DECLARE(F75303, CONFIG_SENSOR_LOG_LEVEL); + +#define NUM_REGS 128 + +struct f75303_emul_data { + uint8_t reg[NUM_REGS]; +}; + +struct f75303_emul_cfg { +}; + +static void f75303_emul_set_reg(const struct emul *target, uint8_t reg, uint8_t val) +{ + struct f75303_emul_data *data = target->data; + + __ASSERT_NO_MSG(reg < NUM_REGS); + data->reg[reg] = val; +} + +static uint8_t f75303_emul_get_reg(const struct emul *target, uint8_t reg) +{ + struct f75303_emul_data *data = target->data; + + __ASSERT_NO_MSG(reg < NUM_REGS); + return data->reg[reg]; +} + +static void f75303_emul_reset(const struct emul *target) +{ + struct f75303_emul_data *data = target->data; + + memset(data->reg, 0, NUM_REGS); +} + +static int f75303_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs, + int num_msgs, int addr) +{ + /* Largely copied from emul_bmi160.c */ + unsigned int val; + int reg; + + __ASSERT_NO_MSG(msgs && num_msgs); + + i2c_dump_msgs_rw("emul", msgs, num_msgs, addr, false); + switch (num_msgs) { + case 2: + if (msgs->flags & I2C_MSG_READ) { + LOG_ERR("Unexpected read"); + return -EIO; + } + if (msgs->len != 1) { + LOG_ERR("Unexpected msg0 length %d", msgs->len); + return -EIO; + } + reg = msgs->buf[0]; + + /* Now process the 'read' part of the message */ + msgs++; + if (msgs->flags & I2C_MSG_READ) { + switch (msgs->len) { + case 1: + val = f75303_emul_get_reg(target, reg); + msgs->buf[0] = val; + break; + default: + LOG_ERR("Unexpected msg1 length %d", msgs->len); + return -EIO; + } + } else { + if (msgs->len != 1) { + LOG_ERR("Unexpected msg1 length %d", msgs->len); + } + f75303_emul_set_reg(target, reg, msgs->buf[0]); + } + break; + default: + LOG_ERR("Invalid number of messages: %d", num_msgs); + return -EIO; + } + + return 0; +} + +static int f75303_emul_init(const struct emul *target, const struct device *parent) +{ + f75303_emul_reset(target); + return 0; +} + +static int f75303_emul_set_channel(const struct emul *target, enum sensor_channel chan, + q31_t value, int8_t shift) +{ + struct f75303_emul_data *data = target->data; + int64_t scaled_value; + int32_t millicelsius; + int32_t reg_value; + uint8_t reg_h, reg_l; + + switch ((int32_t)chan) { + case SENSOR_CHAN_AMBIENT_TEMP: + reg_h = F75303_LOCAL_TEMP_H; + reg_l = F75303_LOCAL_TEMP_L; + break; + case SENSOR_CHAN_F75303_REMOTE1: + reg_h = F75303_REMOTE1_TEMP_H; + reg_l = F75303_REMOTE1_TEMP_L; + break; + case SENSOR_CHAN_F75303_REMOTE2: + reg_h = F75303_REMOTE2_TEMP_H; + reg_l = F75303_REMOTE2_TEMP_L; + break; + default: + return -ENOTSUP; + } + + scaled_value = (int64_t)value << shift; + millicelsius = scaled_value * 1000 / ((int64_t)INT32_MAX + 1); + reg_value = CLAMP(millicelsius / 125, 0, 0x7ff); + + data->reg[reg_h] = reg_value >> 3; + data->reg[reg_l] = (reg_value & 0x7) << 5; + + return 0; +} + +static int f75303_emul_get_sample_range(const struct emul *target, enum sensor_channel chan, + q31_t *lower, q31_t *upper, q31_t *epsilon, int8_t *shift) +{ + if (chan != SENSOR_CHAN_AMBIENT_TEMP && + chan != (enum sensor_channel)SENSOR_CHAN_F75303_REMOTE1 && + chan != (enum sensor_channel)SENSOR_CHAN_F75303_REMOTE2) { + return -ENOTSUP; + } + + *shift = 8; + *lower = 0; + *upper = (int64_t)(255.875 * ((int64_t)INT32_MAX + 1)) >> *shift; + *epsilon = (int64_t)(0.125 * ((int64_t)INT32_MAX + 1)) >> *shift; + + return 0; +} + +static const struct i2c_emul_api f75303_emul_api_i2c = { + .transfer = f75303_emul_transfer_i2c, +}; + +static const struct emul_sensor_backend_api f75303_emul_api_sensor = { + .set_channel = f75303_emul_set_channel, + .get_sample_range = f75303_emul_get_sample_range, +}; + + +#define F75303_EMUL(n) \ + const struct f75303_emul_cfg f75303_emul_cfg_##n; \ + struct f75303_emul_data f75303_emul_data_##n; \ + EMUL_DT_INST_DEFINE(n, f75303_emul_init, &f75303_emul_data_##n, \ + &f75303_emul_cfg_##n, &f75303_emul_api_i2c, \ + &f75303_emul_api_sensor); + +DT_INST_FOREACH_STATUS_OKAY(F75303_EMUL) diff --git a/dts/bindings/sensor/fintek,f75303.yaml b/dts/bindings/sensor/fintek,f75303.yaml new file mode 100644 index 00000000000000..842aa25bd1804a --- /dev/null +++ b/dts/bindings/sensor/fintek,f75303.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2023 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +description: | + F75303 temperature sensor IC. This device has three temperature + channels - one local (on-chip), and two remote. + +compatible: "fintek,f75303" + +include: [sensor-device.yaml, i2c-device.yaml] diff --git a/include/zephyr/drivers/sensor/f75303.h b/include/zephyr/drivers/sensor/f75303.h new file mode 100644 index 00000000000000..3865e8f7e72115 --- /dev/null +++ b/include/zephyr/drivers/sensor/f75303.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_F75303_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_F75303_H_ + +#include + +/* F75303 specific channels */ +enum sensor_channel_f75303 { + SENSOR_CHAN_F75303_REMOTE1 = SENSOR_CHAN_PRIV_START, + SENSOR_CHAN_F75303_REMOTE2, +}; + +#endif diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index c45f47c492d2af..8f121177a6129c 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -744,3 +744,8 @@ test_i2c_ist8310@6f { reg = <0x6f>; status = "okay"; }; + +test_i2c_f75303: f75303@70 { + compatible = "fintek,f75303"; + reg = <0x70>; +};