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