1 /*
2  * Copyright 2023 Google LLC
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <zephyr/device.h>
7 #include <zephyr/logging/log.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/shell/shell.h>
11 #include <zephyr/drivers/usb_c/usbc_ppc.h>
12 
13 #include "nxp_nx20p3483_priv.h"
14 
15 #define DT_DRV_COMPAT nxp_nx20p3483
16 LOG_MODULE_REGISTER(nxp_nx20p3483, CONFIG_USBC_PPC_LOG_LEVEL);
17 
18 #ifdef CONFIG_USBC_PPC_NX20P3483_DUMP_FULL_REG_NAMES
19 static const char *const nx20p3483_reg_names[] = {
20 	"Device ID           ", "Device Status       ", "Switch Control      ",
21 	"Switch Status       ", "Interrupt 1         ", "Interrupt 2         ",
22 	"Interrupt 1 Mask    ", "Interrupt 2 Mask    ", "OVLO Threshold      ",
23 	"HV SRC OCP Threshold", "5V SRC OCP Threshold", "Device Control      ",
24 };
25 #endif
26 
27 /* Driver structures */
28 
29 struct nx20p3483_cfg {
30 	/** Device address on I2C bus */
31 	const struct i2c_dt_spec bus;
32 	/** GPIO used as interrupt request */
33 	const struct gpio_dt_spec irq_gpio;
34 
35 	/** Overvoltage protection threshold for sink role */
36 	int snk_ovp_thresh;
37 	/** Boolean value whether to use high-voltage source if true or 5V source if false */
38 	bool src_use_hv;
39 	/** Overcurrent protection threshold for 5V source role */
40 	int src_5v_ocp_thresh;
41 	/** Overcurrent protection threshold for HV source role */
42 	int src_hv_ocp_thresh;
43 };
44 
45 struct nx20p3483_data {
46 	/** Device structure to get from data structure */
47 	const struct device *dev;
48 	/** Interrupt request callback object */
49 	struct gpio_callback irq_cb;
50 	/** Workqueue object for handling interrupts */
51 	struct k_work irq_work;
52 
53 	/** Callback used to notify about PPC events, like overcurrent or short */
54 	usbc_ppc_event_cb_t event_cb;
55 	/** Data sent as parameter to the callback */
56 	void *event_cb_data;
57 };
58 
59 /* Helper functions */
60 
read_reg(const struct device * dev,uint8_t reg,uint8_t * value)61 static int read_reg(const struct device *dev, uint8_t reg, uint8_t *value)
62 {
63 	const struct nx20p3483_cfg *cfg = dev->config;
64 	int ret;
65 
66 	ret = i2c_reg_read_byte(cfg->bus.bus, cfg->bus.addr, reg, value);
67 	if (ret != 0) {
68 		LOG_ERR("Error reading reg %02x: %d", reg, ret);
69 		return ret;
70 	}
71 
72 	return 0;
73 }
74 
write_reg(const struct device * dev,uint8_t reg,uint8_t value)75 static int write_reg(const struct device *dev, uint8_t reg, uint8_t value)
76 {
77 	const struct nx20p3483_cfg *cfg = dev->config;
78 	int ret;
79 
80 	ret = i2c_reg_write_byte(cfg->bus.bus, cfg->bus.addr, reg, value);
81 	if (ret != 0) {
82 		LOG_ERR("Error writing reg %02x: %d", reg, ret);
83 		return ret;
84 	}
85 
86 	return 0;
87 }
88 
nx20p3483_set_snk_ovp_limit(const struct device * dev,uint8_t u_thresh)89 static int nx20p3483_set_snk_ovp_limit(const struct device *dev, uint8_t u_thresh)
90 {
91 	int ret;
92 
93 	if (u_thresh < NX20P3483_I_THRESHOLD_0_400 || u_thresh > NX20P3483_I_THRESHOLD_3_400) {
94 		return -EINVAL;
95 	}
96 
97 	ret = write_reg(dev, NX20P3483_REG_OVLO_THRESHOLD, u_thresh);
98 	if (ret != 0) {
99 		LOG_ERR("Couldn't set SNK OVP: %d", ret);
100 		return ret;
101 	}
102 
103 	LOG_DBG("Set SNK OVP: %d", u_thresh);
104 	return 0;
105 }
106 
107 /* API functions */
108 
nx20p3483_is_dead_battery_mode(const struct device * dev)109 int nx20p3483_is_dead_battery_mode(const struct device *dev)
110 {
111 	uint8_t sts_reg;
112 	int ret;
113 
114 	ret = read_reg(dev, NX20P3483_REG_DEVICE_STATUS, &sts_reg);
115 	if (ret != 0) {
116 		return ret;
117 	}
118 
119 	return ((sts_reg & NX20P3483_REG_DEVICE_STATUS_MODE_MASK) == NX20P3483_MODE_DEAD_BATTERY);
120 }
121 
nx20p3483_exit_dead_battery_mode(const struct device * dev)122 int nx20p3483_exit_dead_battery_mode(const struct device *dev)
123 {
124 	uint8_t ctrl_reg;
125 	int ret;
126 
127 	ret = read_reg(dev, NX20P3483_REG_DEVICE_CTRL, &ctrl_reg);
128 	if (ret != 0) {
129 		return ret;
130 	}
131 
132 	ctrl_reg |= NX20P3483_REG_DEVICE_CTRL_DB_EXIT;
133 	ret = write_reg(dev, NX20P3483_REG_DEVICE_CTRL, ctrl_reg);
134 	if (ret != 0) {
135 		return ret;
136 	}
137 
138 	return 0;
139 }
140 
nx20p3483_is_vbus_source(const struct device * dev)141 static int nx20p3483_is_vbus_source(const struct device *dev)
142 {
143 	uint8_t sts_reg;
144 	int ret;
145 
146 	ret = read_reg(dev, NX20P3483_REG_SWITCH_STATUS, &sts_reg);
147 	if (ret != 0) {
148 		return ret;
149 	}
150 
151 	return !!(sts_reg &
152 		  (NX20P3483_REG_SWITCH_STATUS_5VSRC | NX20P3483_REG_SWITCH_STATUS_HVSRC));
153 }
154 
nx20p3483_is_vbus_sink(const struct device * dev)155 static int nx20p3483_is_vbus_sink(const struct device *dev)
156 {
157 	uint8_t sts_reg;
158 	int ret;
159 
160 	ret = read_reg(dev, NX20P3483_REG_SWITCH_STATUS, &sts_reg);
161 	if (ret != 0) {
162 		return ret;
163 	}
164 
165 	return !!(sts_reg & NX20P3483_REG_SWITCH_STATUS_HVSNK);
166 }
167 
nx20p3483_set_vbus_sink(const struct device * dev,bool enable)168 static int nx20p3483_set_vbus_sink(const struct device *dev, bool enable)
169 {
170 	const struct nx20p3483_cfg *cfg = dev->config;
171 
172 	/*
173 	 * The nx20p3483 is enabled by external GPIO signal, however enabling it sets the
174 	 * overvoltage threshold to the highest possible value. Due to that, the threshold has
175 	 * to be set here again. Must be called after enabling the path by the external signal.
176 	 */
177 	return nx20p3483_set_snk_ovp_limit(dev, cfg->snk_ovp_thresh);
178 }
179 
nx20p3483_set_vbus_discharge(const struct device * dev,bool enable)180 static int nx20p3483_set_vbus_discharge(const struct device *dev, bool enable)
181 {
182 	uint8_t ctrl_reg;
183 	int ret;
184 
185 	ret = read_reg(dev, NX20P3483_REG_DEVICE_CTRL, &ctrl_reg);
186 	if (ret != 0) {
187 		return ret;
188 	}
189 
190 	if (enable) {
191 		ctrl_reg |= NX20P3483_REG_DEVICE_CTRL_VBUSDIS_EN;
192 	} else {
193 		ctrl_reg &= ~NX20P3483_REG_DEVICE_CTRL_VBUSDIS_EN;
194 	}
195 
196 	ret = write_reg(dev, NX20P3483_REG_DEVICE_CTRL, ctrl_reg);
197 
198 	return ret;
199 }
200 
nx20p3483_set_event_handler(const struct device * dev,usbc_ppc_event_cb_t handler,void * handler_data)201 static int nx20p3483_set_event_handler(const struct device *dev, usbc_ppc_event_cb_t handler,
202 				       void *handler_data)
203 {
204 	struct nx20p3483_data *data = dev->data;
205 
206 	data->event_cb = handler;
207 	data->event_cb_data = handler_data;
208 
209 	return 0;
210 }
211 
nx20p3483_dump_regs(const struct device * dev)212 static int nx20p3483_dump_regs(const struct device *dev)
213 {
214 	const struct nx20p3483_cfg *cfg = dev->config;
215 	uint8_t val;
216 
217 	LOG_INF("NX20P alert: %d", gpio_pin_get(cfg->irq_gpio.port, cfg->irq_gpio.pin));
218 	LOG_INF("PPC %s:%s registers:", cfg->bus.bus->name, dev->name);
219 	for (int a = 0; a <= NX20P3483_REG_DEVICE_CTRL; a++) {
220 		i2c_reg_read_byte(cfg->bus.bus, cfg->bus.addr, a, &val);
221 
222 #ifdef CONFIG_USBC_PPC_NX20P3483_DUMP_FULL_REG_NAMES
223 		LOG_INF("- [%s] = 0x%02x", nx20p3483_reg_names[a], val);
224 #else
225 		LOG_INF("- [%02x] = 0x%02x", a, val);
226 #endif
227 	}
228 
229 	return 0;
230 }
231 
232 static DEVICE_API(usbc_ppc, nx20p3483_driver_api) = {
233 	.is_dead_battery_mode = nx20p3483_is_dead_battery_mode,
234 	.exit_dead_battery_mode = nx20p3483_exit_dead_battery_mode,
235 	.is_vbus_source = nx20p3483_is_vbus_source,
236 	.is_vbus_sink = nx20p3483_is_vbus_sink,
237 	.set_snk_ctrl = nx20p3483_set_vbus_sink,
238 	.set_vbus_discharge = nx20p3483_set_vbus_discharge,
239 	.set_event_handler = nx20p3483_set_event_handler,
240 	.dump_regs = nx20p3483_dump_regs,
241 };
242 
nx20p3483_set_src_ovc_limit(const struct device * dev,uint8_t i_thresh_5v,uint8_t i_thresh_hv)243 static int nx20p3483_set_src_ovc_limit(const struct device *dev, uint8_t i_thresh_5v,
244 				       uint8_t i_thresh_hv)
245 {
246 	int ret;
247 
248 	if (i_thresh_5v < NX20P3483_I_THRESHOLD_0_400 ||
249 	    i_thresh_5v > NX20P3483_I_THRESHOLD_3_400) {
250 		LOG_ERR("Invalid SRC 5V ovc threshold: %d", i_thresh_5v);
251 		return -EINVAL;
252 	}
253 
254 	if (i_thresh_hv < NX20P3483_I_THRESHOLD_0_400 ||
255 	    i_thresh_hv > NX20P3483_I_THRESHOLD_3_400) {
256 		LOG_ERR("Invalid SRC HV ovc threshold: %d", i_thresh_hv);
257 		return -EINVAL;
258 	}
259 
260 	ret = write_reg(dev, NX20P3483_REG_5V_SRC_OCP_THRESHOLD, i_thresh_5v);
261 	if (ret != 0) {
262 		return ret;
263 	}
264 
265 	ret = write_reg(dev, NX20P3483_REG_HV_SRC_OCP_THRESHOLD, i_thresh_hv);
266 	if (ret != 0) {
267 		return ret;
268 	}
269 
270 	LOG_DBG("Set SRC OVC 5V: %d, HV: %d", i_thresh_5v, i_thresh_hv);
271 	return 0;
272 }
273 
nx20p3483_send_event(const struct device * dev,enum usbc_ppc_event ev)274 static void nx20p3483_send_event(const struct device *dev, enum usbc_ppc_event ev)
275 {
276 	struct nx20p3483_data *data = dev->data;
277 
278 	if (data->event_cb != NULL) {
279 		data->event_cb(dev, data->event_cb_data, ev);
280 	}
281 }
282 
nx20p3483_irq_handler(const struct device * port,struct gpio_callback * cb,gpio_port_pins_t pins)283 static void nx20p3483_irq_handler(const struct device *port, struct gpio_callback *cb,
284 				  gpio_port_pins_t pins)
285 {
286 	struct nx20p3483_data *data = CONTAINER_OF(cb, struct nx20p3483_data, irq_cb);
287 
288 	k_work_submit(&data->irq_work);
289 }
290 
nx20p3483_irq_worker(struct k_work * work)291 static void nx20p3483_irq_worker(struct k_work *work)
292 {
293 	struct nx20p3483_data *data = CONTAINER_OF(work, struct nx20p3483_data, irq_work);
294 	const struct device *dev = data->dev;
295 	uint8_t irq1, irq2;
296 	int ret;
297 
298 	ret = read_reg(dev, NX20P3483_REG_INT1, &irq1);
299 	if (ret != 0) {
300 		LOG_ERR("Couldn't read irq1");
301 		return;
302 	}
303 
304 	ret = read_reg(dev, NX20P3483_REG_INT2, &irq2);
305 	if (ret != 0) {
306 		LOG_ERR("Couldn't read irq2");
307 		return;
308 	}
309 
310 	if (data->event_cb == NULL) {
311 		LOG_DBG("No callback set: %02x %02x", irq1, irq1);
312 	}
313 
314 	/* Generic alerts */
315 	if (irq1 & NX20P3483_REG_INT1_DBEXIT_ERR) {
316 		LOG_INF("PPC dead battery exit failed");
317 		nx20p3483_send_event(dev, USBC_PPC_EVENT_DEAD_BATTERY_ERROR);
318 	}
319 
320 	if (irq1 & NX20P3483_REG_INT1_OTP) {
321 		LOG_INF("PPC over temperature");
322 		nx20p3483_send_event(dev, USBC_PPC_EVENT_OVER_TEMPERATURE);
323 	}
324 
325 	if (irq1 & NX20P3483_REG_INT2_EN_ERR) {
326 		LOG_INF("PPC source and sink enabled");
327 		nx20p3483_send_event(dev, USBC_PPC_EVENT_BOTH_SNKSRC_ENABLED);
328 	}
329 
330 	/* Source */
331 	if (irq1 & NX20P3483_REG_INT1_OV_5VSRC || irq2 & NX20P3483_REG_INT2_OV_HVSRC) {
332 		LOG_INF("PPC source overvoltage");
333 		nx20p3483_send_event(dev, USBC_PPC_EVENT_SRC_OVERVOLTAGE);
334 	}
335 
336 	if (irq1 & NX20P3483_REG_INT1_RCP_5VSRC || irq2 & NX20P3483_REG_INT2_RCP_HVSRC) {
337 		LOG_INF("PPC source reverse current");
338 		nx20p3483_send_event(dev, USBC_PPC_EVENT_SRC_REVERSE_CURRENT);
339 	}
340 
341 	if (irq1 & NX20P3483_REG_INT1_OC_5VSRC || irq2 & NX20P3483_REG_INT2_OC_HVSRC) {
342 		LOG_INF("PPC source overcurrent");
343 		nx20p3483_send_event(dev, USBC_PPC_EVENT_SRC_OVERCURRENT);
344 	}
345 
346 	if (irq1 & NX20P3483_REG_INT1_SC_5VSRC || irq2 & NX20P3483_REG_INT2_SC_HVSRC) {
347 		LOG_INF("PPC source short");
348 		nx20p3483_send_event(dev, USBC_PPC_EVENT_SRC_SHORT);
349 	}
350 
351 	/* Sink */
352 	if (irq2 & NX20P3483_REG_INT2_RCP_HVSNK) {
353 		LOG_INF("PPC sink reverse current");
354 		nx20p3483_send_event(dev, USBC_PPC_EVENT_SNK_REVERSE_CURRENT);
355 	}
356 
357 	if (irq2 & NX20P3483_REG_INT2_SC_HVSNK) {
358 		LOG_INF("PPC sink short");
359 		nx20p3483_send_event(dev, USBC_PPC_EVENT_SNK_SHORT);
360 	}
361 
362 	if (irq2 & NX20P3483_REG_INT2_OV_HVSNK) {
363 		LOG_INF("PPC sink overvoltage");
364 		nx20p3483_send_event(dev, USBC_PPC_EVENT_SNK_OVERVOLTAGE);
365 	}
366 }
367 
nx20p3483_dev_init(const struct device * dev)368 static int nx20p3483_dev_init(const struct device *dev)
369 {
370 	const struct nx20p3483_cfg *cfg = dev->config;
371 	struct nx20p3483_data *data = dev->data;
372 	uint8_t reg;
373 	int ret;
374 
375 	LOG_INF("Initializing PPC");
376 
377 	/* Initialize irq */
378 	ret = gpio_pin_configure(cfg->irq_gpio.port, cfg->irq_gpio.pin, GPIO_INPUT | GPIO_PULL_UP);
379 	if (ret != 0) {
380 		return ret;
381 	}
382 
383 	ret = gpio_pin_interrupt_configure(cfg->irq_gpio.port, cfg->irq_gpio.pin,
384 					   GPIO_INT_EDGE_FALLING);
385 	if (ret != 0) {
386 		return ret;
387 	}
388 
389 	gpio_init_callback(&data->irq_cb, nx20p3483_irq_handler, BIT(cfg->irq_gpio.pin));
390 	ret = gpio_add_callback(cfg->irq_gpio.port, &data->irq_cb);
391 	if (ret != 0) {
392 		return ret;
393 	}
394 
395 	/* Initialize work_q */
396 	k_work_init(&data->irq_work, nx20p3483_irq_worker);
397 	k_work_submit(&data->irq_work);
398 
399 	/* If src_use_hv, select the HV src path but do not enable it yet */
400 	read_reg(dev, NX20P3483_REG_SWITCH_CTRL, &reg);
401 	if (cfg->src_use_hv) {
402 		reg |= NX20P3483_REG_SWITCH_CTRL_SRC;
403 	} else {
404 		reg &= ~NX20P3483_REG_SWITCH_CTRL_SRC;
405 	}
406 
407 	write_reg(dev, NX20P3483_REG_SWITCH_CTRL, reg);
408 
409 	/* Set limits */
410 	ret = nx20p3483_set_snk_ovp_limit(dev, cfg->snk_ovp_thresh);
411 	if (ret != 0) {
412 		return ret;
413 	}
414 
415 	ret = nx20p3483_set_src_ovc_limit(dev, cfg->src_5v_ocp_thresh, cfg->src_hv_ocp_thresh);
416 	if (ret != 0) {
417 		return ret;
418 	}
419 
420 	return 0;
421 }
422 
423 #define NX20P3483_DRIVER_CFG_INIT(node)                                                            \
424 	{                                                                                          \
425 		.bus = I2C_DT_SPEC_GET(node), .irq_gpio = GPIO_DT_SPEC_GET(node, irq_gpios),       \
426 		.snk_ovp_thresh = DT_PROP(node, snk_ovp), .src_use_hv = DT_PROP(node, src_hv),     \
427 		.src_5v_ocp_thresh = DT_PROP(node, src_5v_ocp),                                    \
428 		.src_hv_ocp_thresh = DT_PROP(node, src_hv_ocp),                                    \
429 	}
430 
431 #define NX20P3483_DRIVER_CFG_ASSERTS(node)                                                         \
432 	BUILD_ASSERT(DT_PROP(node, snk_ovp) >= NX20P3483_U_THRESHOLD_6_0 &&                        \
433 			     DT_PROP(node, snk_ovp) <= NX20P3483_U_THRESHOLD_23_0,                 \
434 		     "Invalid overvoltage threshold");                                             \
435 	BUILD_ASSERT(DT_PROP(node, src_5v_ocp) >= NX20P3483_I_THRESHOLD_0_400 &&                   \
436 			     DT_PROP(node, src_5v_ocp) <= NX20P3483_I_THRESHOLD_3_400,             \
437 		     "Invalid overcurrent threshold");                                             \
438 	BUILD_ASSERT(DT_PROP(node, src_hv_ocp) >= NX20P3483_I_THRESHOLD_0_400 &&                   \
439 			     DT_PROP(node, src_hv_ocp) <= NX20P3483_I_THRESHOLD_3_400,             \
440 		     "Invalid overcurrent threshold");
441 
442 #define NX20P3483_DRIVER_DATA_INIT(node)                                                           \
443 	{                                                                                          \
444 		.dev = DEVICE_DT_GET(node),                                                        \
445 	}
446 
447 #define NX20P3483_DRIVER_INIT(inst)                                                                \
448 	static struct nx20p3483_data drv_data_nx20p3483##inst =                                    \
449 		NX20P3483_DRIVER_DATA_INIT(DT_DRV_INST(inst));                                     \
450 	NX20P3483_DRIVER_CFG_ASSERTS(DT_DRV_INST(inst));                                           \
451 	static struct nx20p3483_cfg drv_cfg_nx20p3483##inst =                                      \
452 		NX20P3483_DRIVER_CFG_INIT(DT_DRV_INST(inst));                                      \
453 	DEVICE_DT_INST_DEFINE(inst, &nx20p3483_dev_init, NULL, &drv_data_nx20p3483##inst,          \
454 			      &drv_cfg_nx20p3483##inst, POST_KERNEL,                               \
455 			      CONFIG_USBC_PPC_INIT_PRIORITY, &nx20p3483_driver_api);
456 
457 DT_INST_FOREACH_STATUS_OKAY(NX20P3483_DRIVER_INIT)
458