Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: sensor: Add driver for SB-TSI #60818

Merged
merged 1 commit into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/sensor/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_subdirectory_ifdef(CONFIG_ADXL362 adxl362)
add_subdirectory_ifdef(CONFIG_ADXL372 adxl372)
add_subdirectory_ifdef(CONFIG_AK8975 ak8975)
add_subdirectory_ifdef(CONFIG_AKM09918C akm09918c)
add_subdirectory_ifdef(CONFIG_AMD_SB_TSI amd_sb_tsi)
add_subdirectory_ifdef(CONFIG_AMG88XX amg88xx)
add_subdirectory_ifdef(CONFIG_AMS_AS5600 ams_as5600)
add_subdirectory_ifdef(CONFIG_AMS_IAQ_CORE ams_iAQcore)
Expand Down
1 change: 1 addition & 0 deletions drivers/sensor/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ source "drivers/sensor/adxl362/Kconfig"
source "drivers/sensor/adxl372/Kconfig"
source "drivers/sensor/ak8975/Kconfig"
source "drivers/sensor/akm09918c/Kconfig"
source "drivers/sensor/amd_sb_tsi/Kconfig"
source "drivers/sensor/amg88xx/Kconfig"
source "drivers/sensor/ams_as5600/Kconfig"
source "drivers/sensor/ams_iAQcore/Kconfig"
Expand Down
5 changes: 5 additions & 0 deletions drivers/sensor/amd_sb_tsi/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0

zephyr_library()
zephyr_library_sources(sb_tsi.c)
zephyr_library_sources_ifdef(CONFIG_EMUL_AMD_SB_TSI sb_tsi_emul.c)
21 changes: 21 additions & 0 deletions drivers/sensor/amd_sb_tsi/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# SB-TSI configuration options

# Copyright (c) 2023 Google LLC
# SPDX-License-Identifier: Apache-2.0

config AMD_SB_TSI
bool "AMD SB Temperature Sensor Interface"
default y
depends on DT_HAS_AMD_SB_TSI_ENABLED
select I2C
help
Enable the driver for SB Temperature Sensor Interface. This
is an I2C temperature sensor on AMD SoCs.

config EMUL_AMD_SB_TSI
bool "Emulator for AMD SB-TSI"
default y
depends on AMD_SB_TSI
depends on EMUL
help
Enable the hardware emulator for the AMD SB-TSI.
131 changes: 131 additions & 0 deletions drivers/sensor/amd_sb_tsi/sb_tsi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2023 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT amd_sb_tsi

#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 "sb_tsi.h"

LOG_MODULE_REGISTER(AMD_SB_TSI, CONFIG_SENSOR_LOG_LEVEL);

struct sb_tsi_data {
uint8_t sample_int;
uint8_t sample_dec;
};

struct sb_tsi_config {
struct i2c_dt_spec i2c;
};

static int sb_tsi_sample_fetch(const struct device *dev,
enum sensor_channel chan)
{
struct sb_tsi_data *data = dev->data;
const struct sb_tsi_config *config = dev->config;
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;
}

if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
return -ENOTSUP;
}

/*
* ReadOrder specifies the order for atomically reading the temp.
* The reset value is 0, which means reading Int latches Dec.
*/
res = i2c_reg_read_byte_dt(&config->i2c, SB_TSI_TEMP_INT, &data->sample_int);
if (res) {
return res;
}

res = i2c_reg_read_byte_dt(&config->i2c, SB_TSI_TEMP_DEC, &data->sample_dec);
if (res) {
return res;
}

return 0;
}

static int sb_tsi_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct sb_tsi_data *data = dev->data;

if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
return -ENOTSUP;
}

val->val1 = data->sample_int;
val->val2 = (data->sample_dec >> SB_TSI_TEMP_DEC_SHIFT) *
(1000000 / SB_TSI_TEMP_DEC_SCALE);

return 0;
}

static const struct sensor_driver_api sb_tsi_driver_api = {
.sample_fetch = sb_tsi_sample_fetch,
.channel_get = sb_tsi_channel_get,
};

static int sb_tsi_init(const struct device *dev)
{
const struct sb_tsi_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 sb_tsi_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 SB_TSI_INST(inst) \
static struct sb_tsi_data sb_tsi_data_##inst; \
static const struct sb_tsi_config sb_tsi_config_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
}; \
PM_DEVICE_DT_INST_DEFINE(inst, sb_tsi_pm_action); \
SENSOR_DEVICE_DT_INST_DEFINE(inst, sb_tsi_init, PM_DEVICE_DT_INST_GET(inst), \
&sb_tsi_data_##inst, &sb_tsi_config_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &sb_tsi_driver_api);

DT_INST_FOREACH_STATUS_OKAY(SB_TSI_INST)
15 changes: 15 additions & 0 deletions drivers/sensor/amd_sb_tsi/sb_tsi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright (c) 2023 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_DRIVERS_SENSOR_SB_TSI_SB_TSI_H_
#define ZEPHYR_DRIVERS_SENSOR_SB_TSI_SB_TSI_H_

#define SB_TSI_TEMP_INT 0x01
#define SB_TSI_TEMP_DEC 0x10
#define SB_TSI_TEMP_DEC_SHIFT 5
#define SB_TSI_TEMP_DEC_SCALE 8

#endif
153 changes: 153 additions & 0 deletions drivers/sensor/amd_sb_tsi/sb_tsi_emul.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT amd_sb_tsi

#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 "sb_tsi.h"

LOG_MODULE_DECLARE(AMD_SB_TSI, CONFIG_SENSOR_LOG_LEVEL);

#define NUM_REGS 128

struct sb_tsi_emul_data {
uint8_t reg[NUM_REGS];
};

static void sb_tsi_emul_set_reg(const struct emul *target, uint8_t reg, uint8_t val)
{
struct sb_tsi_emul_data *data = target->data;

__ASSERT_NO_MSG(reg < NUM_REGS);
data->reg[reg] = val;
}

static uint8_t sb_tsi_emul_get_reg(const struct emul *target, uint8_t reg)
{
struct sb_tsi_emul_data *data = target->data;

__ASSERT_NO_MSG(reg < NUM_REGS);
return data->reg[reg];
}

static void sb_tsi_emul_reset(const struct emul *target)
{
struct sb_tsi_emul_data *data = target->data;

memset(data->reg, 0, NUM_REGS);
}

static int sb_tsi_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(target->dev, 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 = sb_tsi_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);
}
sb_tsi_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 sb_tsi_emul_init(const struct emul *target, const struct device *parent)
{
sb_tsi_emul_reset(target);
return 0;
}

static int sb_tsi_emul_set_channel(const struct emul *target, enum sensor_channel chan,
q31_t value, int8_t shift)
{
struct sb_tsi_emul_data *data = target->data;
int64_t scaled_value;
int32_t millicelsius;
int32_t reg_value;

if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
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[SB_TSI_TEMP_INT] = reg_value >> 3;
data->reg[SB_TSI_TEMP_DEC] = (reg_value & 0x7) << 5;

return 0;
}

static int sb_tsi_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) {
return -ENOTSUP;
}

*shift = 8;
*lower = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you're using these fields to pass around a raw register representation of the temperature rather than degrees Celsius in fixed-point format, which is the encoding the new sensors API as well as the backend emul API uses in place of the traditional struct sensor_val.

Based on what I could find this sensor runs a range of 0.125 C to 255.875 C. This will fit within the range of shift value 8 (Q9.23 in DSP notation), which is -256 to 255.9999998807907. The sensor's lower bound (0.125 C), for example, would be encoded as 0.125 * 2^31 >> 8 = 0x100000 (but I would just write the full expression instead of pasting the hex value for readability's sake: *lower = (int64_t)(0.125 * ((int64_t)INT32_MAX + 1)) >> *shift;). In this case, epsilon would be the same value because we go up in increments of 0.125 C. Epsilon is used as a tolerance when the generic test compares actual to expected to allow for rounding / precision errors. The value of *upper would be 0x7ff00000 (same math as above)

Let me know if you have any other questions, this can be hard to wrap one's head around at first. I found https://chummersone.github.io/qformat.html very useful. (For this case you'd plug in 32 as word size and 23 as the number of fractional bits)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for the explanation. I applied the changes, except for the lower bound being 0C, not 0.125C

*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 sb_tsi_emul_api_i2c = {
.transfer = sb_tsi_emul_transfer_i2c,
};

static const struct emul_sensor_backend_api sb_tsi_emul_api_sensor = {
.set_channel = sb_tsi_emul_set_channel,
.get_sample_range = sb_tsi_emul_get_sample_range,
};

#define SB_TSI_EMUL(n) \
struct sb_tsi_emul_data sb_tsi_emul_data_##n; \
EMUL_DT_INST_DEFINE(n, sb_tsi_emul_init, &sb_tsi_emul_data_##n, NULL, \
&sb_tsi_emul_api_i2c, &sb_tsi_emul_api_sensor)

DT_INST_FOREACH_STATUS_OKAY(SB_TSI_EMUL)
8 changes: 8 additions & 0 deletions dts/bindings/sensor/amd,sb-tsi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2023 Google LLC
# SPDX-License-Identifier: Apache-2.0

description: AMD SB Temperature Sensor Interface.

compatible: "amd,sb-tsi"

include: [sensor-device.yaml, i2c-device.yaml]
5 changes: 5 additions & 0 deletions tests/drivers/build_all/sensor/i2c.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -771,3 +771,8 @@ test_i2c_hm330x@73 {
reg = <0x73>;
status = "okay";
};

test_i2c_amd_sb_tsi: amd_sb_tsi@74 {
compatible = "amd,sb-tsi";
reg = <0x74>;
};