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, ®);
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