1 /*
2 * Copyright 2022 Google LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Emulator for Diodes PI3USB9201 Dual-Role USB Charging-Type Detector.
7 */
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/emul.h>
11 #include <zephyr/drivers/gpio/gpio_emul.h>
12 #include <zephyr/drivers/i2c.h>
13 #include <zephyr/drivers/i2c_emul.h>
14 #include <zephyr/drivers/usb/emul_bc12.h>
15 #include <zephyr/drivers/usb/usb_bc12.h>
16 #include <zephyr/logging/log.h>
17 #include <zephyr/ztest.h>
18
19 #include <bc12_pi3usb9201.h>
20
21 #define DT_DRV_COMPAT diodes_pi3usb9201
22
23 LOG_MODULE_REGISTER(emul_pi3usb9201, LOG_LEVEL_DBG);
24
25 #define IS_I2C_MSG_WRITE(flags) ((flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE)
26 #define IS_I2C_MSG_READ(flags) ((flags & I2C_MSG_RW_MASK) == I2C_MSG_READ)
27
28 #define EMUL_REG_COUNT (PI3USB9201_REG_HOST_STS + 1)
29 #define EMUL_REG_IS_VALID(reg) (reg >= 0 && reg < EMUL_REG_COUNT)
30
31 #define DCP_DETECTED BIT(7)
32 #define SDP_DETECTED BIT(6)
33 #define CDP_DETECTED BIT(5)
34 #define PROPRIETARY_1A_DETECTED BIT(3)
35 #define PROPRIETARY_2A_DETECTED BIT(2)
36 #define PROPRIETARY_2_4A_DETECTED BIT(1)
37
38 /** Run-time data used by the emulator */
39 struct pi3usb9201_emul_data {
40 /** pi3usb9201 device being emulated */
41 const struct device *i2c;
42 /** Configuration information */
43 const struct pi3usb9201_emul_cfg *cfg;
44 /** Current state of all emulated pi3usb9201 registers */
45 uint8_t reg[EMUL_REG_COUNT];
46 /** The current charging partner type requested by the test */
47 uint8_t test_client_status;
48 };
49
50 /** Static configuration for the emulator */
51 struct pi3usb9201_emul_cfg {
52 /** Pointer to run-time data */
53 struct pi3usb9201_emul_data *data;
54 /** Address of pi3usb9201 on i2c bus */
55 uint16_t addr;
56 /** GPIO connected to the INTB signal */
57 struct gpio_dt_spec intb_gpio;
58 };
59
pi3usb9201_emul_interrupt_is_pending(const struct emul * target)60 static bool pi3usb9201_emul_interrupt_is_pending(const struct emul *target)
61 {
62 struct pi3usb9201_emul_data *data = target->data;
63
64 if (data->reg[PI3USB9201_REG_CTRL_1] & PI3USB9201_REG_CTRL_1_INT_MASK) {
65 /* Interrupt is masked */
66 return false;
67 }
68
69 if ((data->reg[PI3USB9201_REG_CTRL_2] & PI3USB9201_REG_CTRL_2_START_DET) &&
70 data->reg[PI3USB9201_REG_CLIENT_STS]) {
71 /* Cient detection is enabled and there are bits set in the
72 * client status register
73 */
74 return true;
75 }
76
77 if (data->reg[PI3USB9201_REG_HOST_STS]) {
78 /* Any bits set in the host status register generate an interrupt */
79 return true;
80 }
81
82 return false;
83 }
84
pi3usb9201_emul_set_reg(const struct emul * target,int reg,uint8_t val)85 static int pi3usb9201_emul_set_reg(const struct emul *target, int reg, uint8_t val)
86 {
87 struct pi3usb9201_emul_data *data = target->data;
88 const struct pi3usb9201_emul_cfg *cfg = target->cfg;
89
90 if (!EMUL_REG_IS_VALID(reg)) {
91 return -EIO;
92 }
93
94 data->reg[reg] = val;
95
96 /* Once the driver sets the operating mode to client, update the
97 * client status register with tests requested charging partner type
98 */
99 if (reg == PI3USB9201_REG_CTRL_1) {
100 enum pi3usb9201_mode mode = val >> PI3USB9201_REG_CTRL_1_MODE_SHIFT;
101
102 mode &= PI3USB9201_REG_CTRL_1_MODE_MASK;
103 if (mode == PI3USB9201_CLIENT_MODE) {
104 data->reg[PI3USB9201_REG_CLIENT_STS] = data->test_client_status;
105 }
106 }
107
108 /* Check if an interrupt should be asserted */
109 if (pi3usb9201_emul_interrupt_is_pending(target)) {
110 gpio_emul_input_set(cfg->intb_gpio.port, cfg->intb_gpio.pin, 0);
111 }
112
113 return 0;
114 }
115
pi3usb9201_emul_get_reg(const struct emul * target,int reg,uint8_t * val)116 static int pi3usb9201_emul_get_reg(const struct emul *target, int reg, uint8_t *val)
117 {
118 struct pi3usb9201_emul_data *data = target->data;
119 const struct pi3usb9201_emul_cfg *cfg = target->cfg;
120
121 if (!EMUL_REG_IS_VALID(reg)) {
122 return -EIO;
123 }
124
125 *val = data->reg[reg];
126
127 /* Client/host status register clear on I2C read */
128 if ((reg == PI3USB9201_REG_CLIENT_STS) || (reg == PI3USB9201_REG_HOST_STS)) {
129 data->reg[reg] = 0;
130
131 /* Deassert the interrupt line when both client and host status are clear */
132 if (data->reg[PI3USB9201_REG_CLIENT_STS] == 0 &&
133 data->reg[PI3USB9201_REG_HOST_STS] == 0) {
134 gpio_emul_input_set(cfg->intb_gpio.port, cfg->intb_gpio.pin, 1);
135 }
136 }
137
138 return 0;
139 }
140
pi3usb9201_emul_reset(const struct emul * target)141 static void pi3usb9201_emul_reset(const struct emul *target)
142 {
143 struct pi3usb9201_emul_data *data = target->data;
144 const struct pi3usb9201_emul_cfg *cfg = target->cfg;
145
146 data->reg[PI3USB9201_REG_CTRL_1] = 0;
147 data->reg[PI3USB9201_REG_CTRL_2] = 0;
148 data->reg[PI3USB9201_REG_CLIENT_STS] = 0;
149 data->reg[PI3USB9201_REG_HOST_STS] = 0;
150
151 data->test_client_status = 0;
152
153 gpio_emul_input_set(cfg->intb_gpio.port, cfg->intb_gpio.pin, 1);
154 }
155
156 #define PI3USB9201_EMUL_RESET_RULE_BEFORE(inst) \
157 pi3usb9201_emul_reset(&EMUL_DT_NAME_GET(DT_DRV_INST(inst)));
158
emul_pi3usb9201_reset_before(const struct ztest_unit_test * test,void * data)159 static void emul_pi3usb9201_reset_before(const struct ztest_unit_test *test, void *data)
160 {
161 ARG_UNUSED(test);
162 ARG_UNUSED(data);
163
164 DT_INST_FOREACH_STATUS_OKAY(PI3USB9201_EMUL_RESET_RULE_BEFORE)
165 }
166 ZTEST_RULE(emul_isl923x_reset, emul_pi3usb9201_reset_before, NULL);
167
168 /**
169 * Emulate an I2C transfer to a pi3usb9201
170 *
171 * This handles simple reads and writes
172 *
173 * @param target I2C emulation information
174 * @param msgs List of messages to process
175 * @param num_msgs Number of messages to process
176 * @param addr Address of the I2C target device
177 *
178 * @retval 0 If successful
179 * @retval -EIO General input / output error
180 */
pi3usb9201_emul_transfer(const struct emul * target,struct i2c_msg * msgs,int num_msgs,int addr)181 static int pi3usb9201_emul_transfer(const struct emul *target, struct i2c_msg *msgs, int num_msgs,
182 int addr)
183 {
184 const struct pi3usb9201_emul_cfg *cfg;
185 struct pi3usb9201_emul_data *data;
186
187 data = target->data;
188 cfg = data->cfg;
189
190 if (cfg->addr != addr) {
191 LOG_ERR("Address mismatch, expected %02x, got %02x", cfg->addr, addr);
192 return -EIO;
193 }
194
195 i2c_dump_msgs(target->dev, msgs, num_msgs, addr);
196
197 /* Only single byte register access permitted. Write operations must
198 * consist of 2 write bytes: register offset, register data. Read
199 * operations must consist 1 write byte (register offset) and 1 read
200 * byte (register data).
201 */
202 if (num_msgs == 1) {
203 if (!(IS_I2C_MSG_WRITE(msgs[0].flags) && (msgs[0].len == 2))) {
204 LOG_ERR("Unexpected write msgs");
205 return -EIO;
206 }
207 return pi3usb9201_emul_set_reg(target, msgs[0].buf[0], msgs[0].buf[1]);
208 } else if (num_msgs == 2) {
209 if (!(IS_I2C_MSG_WRITE(msgs[0].flags) && (msgs[0].len == 1) &&
210 IS_I2C_MSG_READ(msgs[1].flags) && (msgs[1].len == 1))) {
211 LOG_ERR("Unexpected read msgs");
212 return -EIO;
213 }
214 return pi3usb9201_emul_get_reg(target, msgs[0].buf[0], &(msgs[1].buf[0]));
215 }
216
217 LOG_ERR("Unexpected num_msgs");
218 return -EIO;
219 }
220
pi3usb9201_emul_set_charging_partner(const struct emul * target,enum bc12_type partner_type)221 int pi3usb9201_emul_set_charging_partner(const struct emul *target, enum bc12_type partner_type)
222 {
223 struct pi3usb9201_emul_data *data = target->data;
224
225 /* Set the client status matching the requested partner type */
226 switch (partner_type) {
227 case BC12_TYPE_NONE:
228 data->test_client_status = 0;
229 break;
230 case BC12_TYPE_SDP:
231 data->test_client_status = SDP_DETECTED;
232 break;
233 case BC12_TYPE_DCP:
234 data->test_client_status = DCP_DETECTED;
235 break;
236 case BC12_TYPE_CDP:
237 data->test_client_status = CDP_DETECTED;
238 break;
239 case BC12_TYPE_PROPRIETARY:
240 data->test_client_status = PROPRIETARY_1A_DETECTED;
241 break;
242 default:
243 LOG_ERR("Unsupported partner mode");
244 return -EINVAL;
245 }
246
247 return 0;
248 }
249
pi3usb9201_emul_set_pd_partner_state(const struct emul * target,bool connected)250 static int pi3usb9201_emul_set_pd_partner_state(const struct emul *target, bool connected)
251 {
252 struct pi3usb9201_emul_data *data = target->data;
253 const struct pi3usb9201_emul_cfg *cfg = target->cfg;
254 uint8_t mode;
255
256 mode = data->reg[PI3USB9201_REG_CTRL_1];
257 mode >>= PI3USB9201_REG_CTRL_1_MODE_SHIFT;
258 mode &= PI3USB9201_REG_CTRL_1_MODE_MASK;
259
260 /* Driver must be configured for host mode SDP/CDP */
261 if (mode != PI3USB9201_SDP_HOST_MODE && mode != PI3USB9201_CDP_HOST_MODE) {
262 return -EACCES;
263 }
264
265 if (connected) {
266 data->reg[PI3USB9201_REG_HOST_STS] |= PI3USB9201_REG_HOST_STS_DEV_PLUG;
267 } else {
268 data->reg[PI3USB9201_REG_HOST_STS] |= PI3USB9201_REG_HOST_STS_DEV_UNPLUG;
269 }
270
271 /* Interrupt are enabled - assert the interrupt */
272 if (!(data->reg[PI3USB9201_REG_CTRL_1] & PI3USB9201_REG_CTRL_1_INT_MASK)) {
273 gpio_emul_input_set(cfg->intb_gpio.port, cfg->intb_gpio.pin, 0);
274 }
275
276 return 0;
277 }
278
279 /* Device instantiation */
280
281 static struct i2c_emul_api pi3usb9201_emul_bus_api = {
282 .transfer = pi3usb9201_emul_transfer,
283 };
284
285 static const struct bc12_emul_driver_api pi3usb9201_emul_backend_api = {
286 .set_charging_partner = pi3usb9201_emul_set_charging_partner,
287 .set_pd_partner = pi3usb9201_emul_set_pd_partner_state,
288 };
289
290 /**
291 * @brief Set up a new pi3usb9201 emulator
292 *
293 * This should be called for each pi3usb9201 device that needs to be
294 * emulated. It registers it with the I2C emulation controller.
295 *
296 * @param target Emulation information
297 * @param parent Device to emulate
298 *
299 * @return 0 indicating success (always)
300 */
pi3usb9201_emul_init(const struct emul * target,const struct device * parent)301 static int pi3usb9201_emul_init(const struct emul *target, const struct device *parent)
302 {
303 const struct pi3usb9201_emul_cfg *cfg = target->cfg;
304 struct pi3usb9201_emul_data *data = cfg->data;
305
306 data->i2c = parent;
307 data->cfg = cfg;
308
309 LOG_INF("init");
310 pi3usb9201_emul_reset(target);
311
312 return 0;
313 }
314
315 #define PI3USB9201_EMUL(n) \
316 static struct pi3usb9201_emul_data pi3usb9201_emul_data_##n = {}; \
317 static const struct pi3usb9201_emul_cfg pi3usb9201_emul_cfg_##n = { \
318 .data = &pi3usb9201_emul_data_##n, \
319 .addr = DT_INST_REG_ADDR(n), \
320 .intb_gpio = GPIO_DT_SPEC_INST_GET_OR(n, intb_gpios, {0}), \
321 }; \
322 EMUL_DT_INST_DEFINE(n, pi3usb9201_emul_init, &pi3usb9201_emul_data_##n, \
323 &pi3usb9201_emul_cfg_##n, &pi3usb9201_emul_bus_api, \
324 &pi3usb9201_emul_backend_api)
325
326 DT_INST_FOREACH_STATUS_OKAY(PI3USB9201_EMUL)
327