1 /*
2  * Copyright (c) 2023 Google LLC
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT amd_sb_tsi
7 
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/emul.h>
10 #include <zephyr/drivers/emul_sensor.h>
11 #include <zephyr/drivers/i2c.h>
12 #include <zephyr/drivers/i2c_emul.h>
13 #include <zephyr/logging/log.h>
14 #include "sb_tsi.h"
15 
16 LOG_MODULE_DECLARE(AMD_SB_TSI, CONFIG_SENSOR_LOG_LEVEL);
17 
18 #define NUM_REGS 128
19 
20 struct sb_tsi_emul_data {
21 	uint8_t reg[NUM_REGS];
22 };
23 
sb_tsi_emul_set_reg(const struct emul * target,uint8_t reg,uint8_t val)24 static void sb_tsi_emul_set_reg(const struct emul *target, uint8_t reg, uint8_t val)
25 {
26 	struct sb_tsi_emul_data *data = target->data;
27 
28 	__ASSERT_NO_MSG(reg < NUM_REGS);
29 	data->reg[reg] = val;
30 }
31 
sb_tsi_emul_get_reg(const struct emul * target,uint8_t reg)32 static uint8_t sb_tsi_emul_get_reg(const struct emul *target, uint8_t reg)
33 {
34 	struct sb_tsi_emul_data *data = target->data;
35 
36 	__ASSERT_NO_MSG(reg < NUM_REGS);
37 	return data->reg[reg];
38 }
39 
sb_tsi_emul_reset(const struct emul * target)40 static void sb_tsi_emul_reset(const struct emul *target)
41 {
42 	struct sb_tsi_emul_data *data = target->data;
43 
44 	memset(data->reg, 0, NUM_REGS);
45 }
46 
sb_tsi_emul_transfer_i2c(const struct emul * target,struct i2c_msg * msgs,int num_msgs,int addr)47 static int sb_tsi_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
48 				    int num_msgs, int addr)
49 {
50 	/* Largely copied from emul_bmi160.c */
51 	unsigned int val;
52 	int reg;
53 
54 	__ASSERT_NO_MSG(msgs && num_msgs);
55 
56 	i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false);
57 	switch (num_msgs) {
58 	case 2:
59 		if (msgs->flags & I2C_MSG_READ) {
60 			LOG_ERR("Unexpected read");
61 			return -EIO;
62 		}
63 		if (msgs->len != 1) {
64 			LOG_ERR("Unexpected msg0 length %d", msgs->len);
65 			return -EIO;
66 		}
67 		reg = msgs->buf[0];
68 
69 		/* Now process the 'read' part of the message */
70 		msgs++;
71 		if (msgs->flags & I2C_MSG_READ) {
72 			switch (msgs->len) {
73 			case 1:
74 				val = sb_tsi_emul_get_reg(target, reg);
75 				msgs->buf[0] = val;
76 				break;
77 			default:
78 				LOG_ERR("Unexpected msg1 length %d", msgs->len);
79 				return -EIO;
80 			}
81 		} else {
82 			if (msgs->len != 1) {
83 				LOG_ERR("Unexpected msg1 length %d", msgs->len);
84 			}
85 			sb_tsi_emul_set_reg(target, reg, msgs->buf[0]);
86 		}
87 		break;
88 	default:
89 		LOG_ERR("Invalid number of messages: %d", num_msgs);
90 		return -EIO;
91 	}
92 
93 	return 0;
94 }
95 
sb_tsi_emul_init(const struct emul * target,const struct device * parent)96 static int sb_tsi_emul_init(const struct emul *target, const struct device *parent)
97 {
98 	sb_tsi_emul_reset(target);
99 	return 0;
100 }
101 
sb_tsi_emul_set_channel(const struct emul * target,struct sensor_chan_spec ch,const q31_t * value,int8_t shift)102 static int sb_tsi_emul_set_channel(const struct emul *target, struct sensor_chan_spec ch,
103 				   const q31_t *value, int8_t shift)
104 {
105 	struct sb_tsi_emul_data *data = target->data;
106 	int64_t scaled_value;
107 	int32_t millicelsius;
108 	int32_t reg_value;
109 
110 	if (ch.chan_type != SENSOR_CHAN_AMBIENT_TEMP && ch.chan_idx != 0) {
111 		return -ENOTSUP;
112 	}
113 
114 	scaled_value = (int64_t)*value << shift;
115 	millicelsius = scaled_value * 1000 / ((int64_t)INT32_MAX + 1);
116 	reg_value = CLAMP(millicelsius / 125, 0, 0x7ff);
117 
118 	data->reg[SB_TSI_TEMP_INT] = reg_value >> 3;
119 	data->reg[SB_TSI_TEMP_DEC] = (reg_value & 0x7) << 5;
120 
121 	return 0;
122 }
123 
sb_tsi_emul_get_sample_range(const struct emul * target,struct sensor_chan_spec ch,q31_t * lower,q31_t * upper,q31_t * epsilon,int8_t * shift)124 static int sb_tsi_emul_get_sample_range(const struct emul *target, struct sensor_chan_spec ch,
125 					q31_t *lower, q31_t *upper, q31_t *epsilon, int8_t *shift)
126 {
127 	if (ch.chan_type != SENSOR_CHAN_AMBIENT_TEMP || ch.chan_idx != 0) {
128 		return -ENOTSUP;
129 	}
130 
131 	*shift = 8;
132 	*lower = 0;
133 	*upper = (int64_t)(255.875 * ((int64_t)INT32_MAX + 1)) >> *shift;
134 	*epsilon = (int64_t)(0.125 * ((int64_t)INT32_MAX + 1)) >> *shift;
135 
136 	return 0;
137 }
138 
139 static const struct i2c_emul_api sb_tsi_emul_api_i2c = {
140 	.transfer = sb_tsi_emul_transfer_i2c,
141 };
142 
143 static const struct emul_sensor_driver_api sb_tsi_emul_api_sensor = {
144 	.set_channel = sb_tsi_emul_set_channel,
145 	.get_sample_range = sb_tsi_emul_get_sample_range,
146 };
147 
148 #define SB_TSI_EMUL(n)								\
149 	struct sb_tsi_emul_data sb_tsi_emul_data_##n;				\
150 	EMUL_DT_INST_DEFINE(n, sb_tsi_emul_init, &sb_tsi_emul_data_##n,	NULL,	\
151 			    &sb_tsi_emul_api_i2c, &sb_tsi_emul_api_sensor)
152 
153 DT_INST_FOREACH_STATUS_OKAY(SB_TSI_EMUL)
154