Browse Source
Add driver for F75303 temperature sensor IC. Signed-off-by: Paweł Anikiel <pan@semihalf.com>pull/62351/head
10 changed files with 465 additions and 0 deletions
@ -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) |
@ -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. |
@ -0,0 +1,198 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Google LLC |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#define DT_DRV_COMPAT fintek_f75303 |
||||||
|
|
||||||
|
#include <zephyr/device.h> |
||||||
|
#include <zephyr/drivers/i2c.h> |
||||||
|
#include <zephyr/drivers/sensor.h> |
||||||
|
#include <zephyr/pm/device.h> |
||||||
|
#include <zephyr/pm/device_runtime.h> |
||||||
|
#include <zephyr/logging/log.h> |
||||||
|
#include <zephyr/drivers/sensor/f75303.h> |
||||||
|
#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) |
@ -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 <zephyr/device.h> |
||||||
|
#include <zephyr/sys/util.h> |
||||||
|
|
||||||
|
#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 |
@ -0,0 +1,176 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Google LLC |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#define DT_DRV_COMPAT fintek_f75303 |
||||||
|
|
||||||
|
#include <zephyr/device.h> |
||||||
|
#include <zephyr/drivers/emul.h> |
||||||
|
#include <zephyr/drivers/emul_sensor.h> |
||||||
|
#include <zephyr/drivers/i2c.h> |
||||||
|
#include <zephyr/drivers/i2c_emul.h> |
||||||
|
#include <zephyr/logging/log.h> |
||||||
|
#include <zephyr/drivers/sensor/f75303.h> |
||||||
|
#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) |
@ -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] |
@ -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 <zephyr/drivers/sensor.h> |
||||||
|
|
||||||
|
/* F75303 specific channels */ |
||||||
|
enum sensor_channel_f75303 { |
||||||
|
SENSOR_CHAN_F75303_REMOTE1 = SENSOR_CHAN_PRIV_START, |
||||||
|
SENSOR_CHAN_F75303_REMOTE2, |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
Loading…
Reference in new issue