1 /*
2  * Copyright (c) 2023 The Chromium OS Authors.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/devicetree.h>
10 #include <zephyr/drivers/adc.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/init.h>
13 #include <zephyr/usb_c/usbc.h>
14 #include <zephyr/drivers/pwm.h>
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(power_ctrl, LOG_LEVEL_DBG);
18 
19 #include "power_ctrl.h"
20 
21 #define PORT1_DCDC_DETECT_NODE	DT_PATH(dcdc_detect)
22 
23 #define PORT1_SOURCE_EN_NODE	DT_NODELABEL(source_en)
24 #define PORT1_DCDC_EN_NODE	DT_NODELABEL(dcdc_en)
25 #define PORT1_PWM_CTL_NODE	DT_NODELABEL(pwm_ctl)
26 #define PORT1_VCONN1_EN_NODE	DT_NODELABEL(vconn1_en)
27 #define PORT1_VCONN2_EN_NODE	DT_NODELABEL(vconn2_en)
28 
29 /* DCDC Voltage is 19V and setting min threshold to 18V */
30 #define MIN_DCDC_DETECT_MV 18000
31 
32 #define PWM_FOR_15V	45000
33 #define PWM_FOR_9V	30000
34 #define PWM_FOR_5V	21500
35 #define PWM_FOR_0V	0
36 
37 static const struct gpio_dt_spec source_en = GPIO_DT_SPEC_GET(PORT1_SOURCE_EN_NODE, gpios);
38 static const struct gpio_dt_spec dcdc_en = GPIO_DT_SPEC_GET(PORT1_DCDC_EN_NODE, gpios);
39 
40 static const struct gpio_dt_spec vconn1_en = GPIO_DT_SPEC_GET(PORT1_VCONN1_EN_NODE, gpios);
41 static const struct gpio_dt_spec vconn2_en = GPIO_DT_SPEC_GET(PORT1_VCONN2_EN_NODE, gpios);
42 
43 static const struct pwm_dt_spec pwm_ctl = PWM_DT_SPEC_GET(PORT1_PWM_CTL_NODE);
44 
vconn_ctrl_set(enum vconn_t v)45 int vconn_ctrl_set(enum vconn_t v)
46 {
47 	switch (v) {
48 	case VCONN_OFF:
49 		gpio_pin_set_dt(&vconn1_en, 0);
50 		gpio_pin_set_dt(&vconn2_en, 0);
51 		break;
52 	case VCONN1_ON:
53 		gpio_pin_set_dt(&vconn1_en, 1);
54 		gpio_pin_set_dt(&vconn2_en, 0);
55 		break;
56 	case VCONN2_ON:
57 		gpio_pin_set_dt(&vconn1_en, 0);
58 		gpio_pin_set_dt(&vconn2_en, 1);
59 		break;
60 	};
61 
62 	return 0;
63 }
64 
source_ctrl_set(enum source_t v)65 int source_ctrl_set(enum source_t v)
66 {
67 	uint32_t pwmv;
68 
69 	switch (v) {
70 	case SOURCE_0V:
71 		pwmv = PWM_FOR_0V;
72 		break;
73 	case SOURCE_5V:
74 		pwmv = PWM_FOR_5V;
75 		break;
76 	case SOURCE_9V:
77 		pwmv = PWM_FOR_9V;
78 		break;
79 	case SOURCE_15V:
80 		pwmv = PWM_FOR_15V;
81 		break;
82 	default:
83 		pwmv = PWM_FOR_0V;
84 	}
85 
86 	return pwm_set_pulse_dt(&pwm_ctl, pwmv);
87 }
88 
power_ctrl_init(void)89 static int power_ctrl_init(void)
90 {
91 	int ret;
92 
93 	if (!gpio_is_ready_dt(&source_en)) {
94 		LOG_ERR("Error: Source Enable device %s is not ready",
95 			source_en.port->name);
96 		return -ENODEV;
97 	}
98 
99 	if (!gpio_is_ready_dt(&dcdc_en)) {
100 		LOG_ERR("Error: DCDC Enable device %s is not ready",
101 			dcdc_en.port->name);
102 		return -ENODEV;
103 	}
104 
105 	if (!gpio_is_ready_dt(&vconn1_en)) {
106 		LOG_ERR("Error: VCONN1 Enable device %s is not ready",
107 			vconn1_en.port->name);
108 		return -ENODEV;
109 	}
110 
111 	if (!gpio_is_ready_dt(&vconn2_en)) {
112 		LOG_ERR("Error: VCONN2 Enable device %s is not ready",
113 			vconn2_en.port->name);
114 		return -ENODEV;
115 	}
116 
117 	if (!pwm_is_ready_dt(&pwm_ctl)) {
118 		LOG_ERR("Error: PWM CTL device is not ready");
119 		return -ENODEV;
120 	}
121 
122 	ret = gpio_pin_configure_dt(&source_en, GPIO_OUTPUT);
123 	if (ret != 0) {
124 		LOG_ERR("Error %d: failed to configure Source Enable device %s pin %d",
125 			ret, source_en.port->name, source_en.pin);
126 		return ret;
127 	}
128 
129 	ret = gpio_pin_configure_dt(&dcdc_en, GPIO_OUTPUT);
130 	if (ret != 0) {
131 		LOG_ERR("Error %d: failed to configure DCDC Enable device %s pin %d",
132 			ret, dcdc_en.port->name, dcdc_en.pin);
133 		return ret;
134 	}
135 
136 	ret = gpio_pin_configure_dt(&vconn1_en, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
137 	if (ret != 0) {
138 		LOG_ERR("Error %d: failed to configure VCONN1 Enable device %s pin %d",
139 			ret, vconn1_en.port->name, vconn1_en.pin);
140 		return ret;
141 	}
142 
143 	ret = gpio_pin_configure_dt(&vconn2_en, GPIO_OUTPUT | GPIO_OPEN_DRAIN);
144 	if (ret != 0) {
145 		LOG_ERR("Error %d: failed to configure VCONN2 Enable device %s pin %d",
146 			ret, vconn2_en.port->name, vconn1_en.pin);
147 		return ret;
148 	}
149 
150 	ret = gpio_pin_set_dt(&source_en, 1);
151 	if (ret != 0) {
152 		LOG_ERR("Error %d: failed to enable source", ret);
153 		return ret;
154 	}
155 
156 	ret = gpio_pin_set_dt(&dcdc_en, 1);
157 	if (ret != 0) {
158 		LOG_ERR("Error %d: failed to enable dcdc converter", ret);
159 		return ret;
160 	}
161 
162 	vconn_ctrl_set(VCONN_OFF);
163 
164 	ret = source_ctrl_set(SOURCE_0V);
165 	if (ret != 0) {
166 		LOG_ERR("Error %d: failed to set VBUS to 0V", ret);
167 		return ret;
168 	}
169 
170 	return 0;
171 }
172 
173 SYS_INIT(power_ctrl_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
174