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