-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
*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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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
to255.9999998807907
. The sensor's lower bound (0.125 C), for example, would be encoded as0.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 be0x7ff00000
(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)
There was a problem hiding this comment.
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