1 /*
2 * Copyright (c) 2023 Google LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT amd_sb_tsi
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/pm/device.h>
13 #include <zephyr/pm/device_runtime.h>
14 #include <zephyr/logging/log.h>
15 #include "sb_tsi.h"
16
17 LOG_MODULE_REGISTER(AMD_SB_TSI, CONFIG_SENSOR_LOG_LEVEL);
18
19 struct sb_tsi_data {
20 uint8_t sample_int;
21 uint8_t sample_dec;
22 };
23
24 struct sb_tsi_config {
25 struct i2c_dt_spec i2c;
26 };
27
sb_tsi_sample_fetch(const struct device * dev,enum sensor_channel chan)28 static int sb_tsi_sample_fetch(const struct device *dev,
29 enum sensor_channel chan)
30 {
31 struct sb_tsi_data *data = dev->data;
32 const struct sb_tsi_config *config = dev->config;
33 enum pm_device_state pm_state;
34 int res;
35
36 (void)pm_device_state_get(dev, &pm_state);
37 if (pm_state != PM_DEVICE_STATE_ACTIVE) {
38 return -EIO;
39 }
40
41 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
42 return -ENOTSUP;
43 }
44
45 /*
46 * ReadOrder specifies the order for atomically reading the temp.
47 * The reset value is 0, which means reading Int latches Dec.
48 */
49 res = i2c_reg_read_byte_dt(&config->i2c, SB_TSI_TEMP_INT, &data->sample_int);
50 if (res) {
51 return res;
52 }
53
54 res = i2c_reg_read_byte_dt(&config->i2c, SB_TSI_TEMP_DEC, &data->sample_dec);
55 if (res) {
56 return res;
57 }
58
59 return 0;
60 }
61
sb_tsi_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)62 static int sb_tsi_channel_get(const struct device *dev,
63 enum sensor_channel chan,
64 struct sensor_value *val)
65 {
66 struct sb_tsi_data *data = dev->data;
67
68 if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
69 return -ENOTSUP;
70 }
71
72 val->val1 = data->sample_int;
73 val->val2 = (data->sample_dec >> SB_TSI_TEMP_DEC_SHIFT) *
74 (1000000 / SB_TSI_TEMP_DEC_SCALE);
75
76 return 0;
77 }
78
79 static DEVICE_API(sensor, sb_tsi_driver_api) = {
80 .sample_fetch = sb_tsi_sample_fetch,
81 .channel_get = sb_tsi_channel_get,
82 };
83
sb_tsi_init(const struct device * dev)84 static int sb_tsi_init(const struct device *dev)
85 {
86 const struct sb_tsi_config *config = dev->config;
87 int res = 0;
88
89 if (!i2c_is_ready_dt(&config->i2c)) {
90 LOG_ERR("I2C device not ready");
91 return -ENODEV;
92 }
93
94 #ifdef CONFIG_PM_DEVICE_RUNTIME
95 pm_device_init_suspended(dev);
96
97 res = pm_device_runtime_enable(dev);
98 if (res) {
99 LOG_ERR("Failed to enable runtime power management");
100 }
101 #endif
102
103 return res;
104 }
105
106 #ifdef CONFIG_PM_DEVICE
sb_tsi_pm_action(const struct device * dev,enum pm_device_action action)107 static int sb_tsi_pm_action(const struct device *dev, enum pm_device_action action)
108 {
109 switch (action) {
110 case PM_DEVICE_ACTION_TURN_ON:
111 case PM_DEVICE_ACTION_RESUME:
112 case PM_DEVICE_ACTION_TURN_OFF:
113 case PM_DEVICE_ACTION_SUSPEND:
114 return 0;
115 default:
116 return -ENOTSUP;
117 }
118 }
119 #endif
120
121 #define SB_TSI_INST(inst) \
122 static struct sb_tsi_data sb_tsi_data_##inst; \
123 static const struct sb_tsi_config sb_tsi_config_##inst = { \
124 .i2c = I2C_DT_SPEC_INST_GET(inst), \
125 }; \
126 PM_DEVICE_DT_INST_DEFINE(inst, sb_tsi_pm_action); \
127 SENSOR_DEVICE_DT_INST_DEFINE(inst, sb_tsi_init, PM_DEVICE_DT_INST_GET(inst), \
128 &sb_tsi_data_##inst, &sb_tsi_config_##inst, POST_KERNEL, \
129 CONFIG_SENSOR_INIT_PRIORITY, &sb_tsi_driver_api);
130
131 DT_INST_FOREACH_STATUS_OKAY(SB_TSI_INST)
132