1 /*
2  * Copyright (c) 2024 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_numaker_ppc
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/usb_c/usbc_ppc.h>
11 #include <zephyr/drivers/clock_control.h>
12 #include <zephyr/drivers/clock_control/clock_control_numaker.h>
13 #include <zephyr/drivers/reset.h>
14 #include <zephyr/drivers/pinctrl.h>
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(ppc_numaker, CONFIG_USBC_LOG_LEVEL);
18 
19 #include <soc.h>
20 #include <NuMicro.h>
21 
22 #include "../tcpc/ucpd_numaker.h"
23 
24 /* Implementation notes on NuMaker TCPC/PPC/VBUS
25  *
26  * PPC and VBUS rely on TCPC/UTCPD and are just pseudo. They are completely
27  * implemented in TCPC/UTCPD.
28  */
29 
30 /**
31  * @brief Immutable device context
32  */
33 struct numaker_ppc_config {
34 	const struct device *tcpc_dev;
35 };
36 
37 /**
38  * @brief Initializes the usb-c ppc driver
39  *
40  * @retval 0 on success
41  * @retval -ENODEV if dependent TCPC device is not ready
42  */
numaker_ppc_init(const struct device * dev)43 static int numaker_ppc_init(const struct device *dev)
44 {
45 	const struct numaker_ppc_config *const config = dev->config;
46 	const struct device *tcpc_dev = config->tcpc_dev;
47 
48 	/* Rely on TCPC */
49 	if (!device_is_ready(tcpc_dev)) {
50 		LOG_ERR("TCPC device not ready");
51 		return -ENODEV;
52 	}
53 
54 	return 0;
55 }
56 
57 /**
58  * @brief Check if PPC is in the dead battery mode
59  *
60  * @retval 1 if PPC is in the dead battery mode
61  * @retval 0 if PPC is not in the dead battery mode
62  * @retval -EIO if on failure
63  */
numaker_ppc_is_dead_battery_mode(const struct device * dev)64 static int numaker_ppc_is_dead_battery_mode(const struct device *dev)
65 {
66 	const struct numaker_ppc_config *const config = dev->config;
67 	const struct device *tcpc_dev = config->tcpc_dev;
68 
69 	return numaker_tcpc_ppc_is_dead_battery_mode(tcpc_dev);
70 }
71 
72 /**
73  * @brief Request the PPC to exit from the dead battery mode
74  *
75  * @retval 0 if request was successfully sent
76  * @retval -EIO if on failure
77  */
numaker_ppc_exit_dead_battery_mode(const struct device * dev)78 static int numaker_ppc_exit_dead_battery_mode(const struct device *dev)
79 {
80 	const struct numaker_ppc_config *const config = dev->config;
81 	const struct device *tcpc_dev = config->tcpc_dev;
82 
83 	return numaker_tcpc_ppc_exit_dead_battery_mode(tcpc_dev);
84 }
85 
86 /**
87  * @brief Check if the PPC is sourcing the VBUS
88  *
89  * @retval 1 if the PPC is sourcing the VBUS
90  * @retval 0 if the PPC is not sourcing the VBUS
91  * @retval -EIO on failure
92  */
numaker_ppc_is_vbus_source(const struct device * dev)93 static int numaker_ppc_is_vbus_source(const struct device *dev)
94 {
95 	const struct numaker_ppc_config *const config = dev->config;
96 	const struct device *tcpc_dev = config->tcpc_dev;
97 
98 	return numaker_tcpc_ppc_is_vbus_source(tcpc_dev);
99 }
100 
101 /**
102  * @brief Check if the PPC is sinking the VBUS
103  *
104  * @retval 1 if the PPC is sinking the VBUS
105  * @retval 0 if the PPC is not sinking the VBUS
106  * @retval -EIO on failure
107  */
numaker_ppc_is_vbus_sink(const struct device * dev)108 static int numaker_ppc_is_vbus_sink(const struct device *dev)
109 {
110 	const struct numaker_ppc_config *const config = dev->config;
111 	const struct device *tcpc_dev = config->tcpc_dev;
112 
113 	return numaker_tcpc_ppc_is_vbus_sink(tcpc_dev);
114 }
115 
116 /**
117  * @brief Set the state of VBUS sinking
118  *
119  * @retval 0 if success
120  * @retval -EIO on failure
121  */
numaker_ppc_set_snk_ctrl(const struct device * dev,bool enable)122 static int numaker_ppc_set_snk_ctrl(const struct device *dev, bool enable)
123 {
124 	const struct numaker_ppc_config *const config = dev->config;
125 	const struct device *tcpc_dev = config->tcpc_dev;
126 
127 	return numaker_tcpc_ppc_set_snk_ctrl(tcpc_dev, enable);
128 }
129 
130 /**
131  * @brief Set the state of VBUS sourcing
132  *
133  * @retval 0 if success
134  * @retval -EIO on failure
135  */
numaker_ppc_set_src_ctrl(const struct device * dev,bool enable)136 static int numaker_ppc_set_src_ctrl(const struct device *dev, bool enable)
137 {
138 	const struct numaker_ppc_config *const config = dev->config;
139 	const struct device *tcpc_dev = config->tcpc_dev;
140 
141 	return numaker_tcpc_ppc_set_src_ctrl(tcpc_dev, enable);
142 }
143 
144 /**
145  * @brief Set the state of VBUS discharging
146  *
147  * @retval 0 if success
148  * @retval -EIO on failure
149  */
numaker_ppc_set_vbus_discharge(const struct device * dev,bool enable)150 static int numaker_ppc_set_vbus_discharge(const struct device *dev, bool enable)
151 {
152 	const struct numaker_ppc_config *const config = dev->config;
153 	const struct device *tcpc_dev = config->tcpc_dev;
154 
155 	return numaker_tcpc_ppc_set_vbus_discharge(tcpc_dev, enable);
156 }
157 
158 /**
159  * @brief Check if VBUS is present
160  *
161  * @retval 1 if VBUS voltage is present
162  * @retval 0 if no VBUS voltage is detected
163  * @retval -EIO on failure
164  */
numaker_ppc_is_vbus_present(const struct device * dev)165 static int numaker_ppc_is_vbus_present(const struct device *dev)
166 {
167 	const struct numaker_ppc_config *const config = dev->config;
168 	const struct device *tcpc_dev = config->tcpc_dev;
169 
170 	return numaker_tcpc_ppc_is_vbus_present(tcpc_dev);
171 }
172 
173 /**
174  * @brief Set the callback used to notify about PPC events
175  *
176  * @retval 0 if success
177  */
numaker_ppc_set_event_handler(const struct device * dev,usbc_ppc_event_cb_t handler,void * data)178 static int numaker_ppc_set_event_handler(const struct device *dev, usbc_ppc_event_cb_t handler,
179 					 void *data)
180 {
181 	const struct numaker_ppc_config *const config = dev->config;
182 	const struct device *tcpc_dev = config->tcpc_dev;
183 
184 	return numaker_tcpc_ppc_set_event_handler(tcpc_dev, handler, data);
185 }
186 
187 /**
188  * @brief Print the values or PPC registers
189  *
190  * @retval 0 if success
191  * @retval -EIO on failure
192  */
numaker_ppc_dump_regs(const struct device * dev)193 static int numaker_ppc_dump_regs(const struct device *dev)
194 {
195 	const struct numaker_ppc_config *const config = dev->config;
196 	const struct device *tcpc_dev = config->tcpc_dev;
197 
198 	return numaker_tcpc_ppc_dump_regs(tcpc_dev);
199 }
200 
201 static DEVICE_API(usbc_ppc, numaker_ppc_driver_api) = {
202 	.is_dead_battery_mode = numaker_ppc_is_dead_battery_mode,
203 	.exit_dead_battery_mode = numaker_ppc_exit_dead_battery_mode,
204 	.is_vbus_source = numaker_ppc_is_vbus_source,
205 	.is_vbus_sink = numaker_ppc_is_vbus_sink,
206 	.set_snk_ctrl = numaker_ppc_set_snk_ctrl,
207 	.set_src_ctrl = numaker_ppc_set_src_ctrl,
208 	.set_vbus_discharge = numaker_ppc_set_vbus_discharge,
209 	.is_vbus_present = numaker_ppc_is_vbus_present,
210 	.set_event_handler = numaker_ppc_set_event_handler,
211 	.dump_regs = numaker_ppc_dump_regs,
212 };
213 
214 #define NUMAKER_TCPC(inst) DT_INST_PARENT(inst)
215 
216 #define PPC_NUMAKER_INIT(inst)                                                                     \
217 	static const struct numaker_ppc_config numaker_ppc_config_##inst = {                       \
218 		.tcpc_dev = DEVICE_DT_GET(NUMAKER_TCPC(inst)),                                     \
219 	};                                                                                         \
220                                                                                                    \
221 	DEVICE_DT_INST_DEFINE(inst, numaker_ppc_init, NULL, NULL, &numaker_ppc_config_##inst,      \
222 			      POST_KERNEL, CONFIG_USBC_PPC_INIT_PRIORITY,                          \
223 			      &numaker_ppc_driver_api);
224 
225 DT_INST_FOREACH_STATUS_OKAY(PPC_NUMAKER_INIT);
226