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