1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Power Supply for UCSI
4  *
5  * Copyright (C) 2020, Intel Corporation
6  * Author: K V, Abhilash <[email protected]>
7  * Author: Heikki Krogerus <[email protected]>
8  */
9 
10 #include <linux/property.h>
11 #include <linux/usb/pd.h>
12 
13 #include "ucsi.h"
14 
15 /* Power Supply access to expose source power information */
16 enum ucsi_psy_online_states {
17 	UCSI_PSY_OFFLINE = 0,
18 	UCSI_PSY_FIXED_ONLINE,
19 	UCSI_PSY_PROG_ONLINE,
20 };
21 
22 static enum power_supply_property ucsi_psy_props[] = {
23 	POWER_SUPPLY_PROP_CHARGE_TYPE,
24 	POWER_SUPPLY_PROP_USB_TYPE,
25 	POWER_SUPPLY_PROP_ONLINE,
26 	POWER_SUPPLY_PROP_VOLTAGE_MIN,
27 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
28 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
29 	POWER_SUPPLY_PROP_CURRENT_MAX,
30 	POWER_SUPPLY_PROP_CURRENT_NOW,
31 	POWER_SUPPLY_PROP_SCOPE,
32 };
33 
ucsi_psy_get_scope(struct ucsi_connector * con,union power_supply_propval * val)34 static int ucsi_psy_get_scope(struct ucsi_connector *con,
35 			      union power_supply_propval *val)
36 {
37 	u8 scope = POWER_SUPPLY_SCOPE_UNKNOWN;
38 	struct device *dev = con->ucsi->dev;
39 
40 	device_property_read_u8(dev, "scope", &scope);
41 	if (scope == POWER_SUPPLY_SCOPE_UNKNOWN) {
42 		u32 mask = UCSI_CAP_ATTR_POWER_AC_SUPPLY |
43 			   UCSI_CAP_ATTR_BATTERY_CHARGING;
44 
45 		if (con->ucsi->cap.attributes & mask)
46 			scope = POWER_SUPPLY_SCOPE_SYSTEM;
47 		else
48 			scope = POWER_SUPPLY_SCOPE_DEVICE;
49 	}
50 	val->intval = scope;
51 	return 0;
52 }
53 
ucsi_psy_get_online(struct ucsi_connector * con,union power_supply_propval * val)54 static int ucsi_psy_get_online(struct ucsi_connector *con,
55 			       union power_supply_propval *val)
56 {
57 	val->intval = UCSI_PSY_OFFLINE;
58 	if (UCSI_CONSTAT(con, CONNECTED) &&
59 	    (UCSI_CONSTAT(con, PWR_DIR) == TYPEC_SINK))
60 		val->intval = UCSI_PSY_FIXED_ONLINE;
61 	return 0;
62 }
63 
ucsi_psy_get_voltage_min(struct ucsi_connector * con,union power_supply_propval * val)64 static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
65 				    union power_supply_propval *val)
66 {
67 	u32 pdo;
68 
69 	switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
70 	case UCSI_CONSTAT_PWR_OPMODE_PD:
71 		pdo = con->src_pdos[0];
72 		val->intval = pdo_fixed_voltage(pdo) * 1000;
73 		break;
74 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
75 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
76 	case UCSI_CONSTAT_PWR_OPMODE_BC:
77 	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
78 		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
79 		break;
80 	default:
81 		val->intval = 0;
82 		break;
83 	}
84 	return 0;
85 }
86 
ucsi_psy_get_voltage_max(struct ucsi_connector * con,union power_supply_propval * val)87 static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
88 				    union power_supply_propval *val)
89 {
90 	u32 pdo;
91 
92 	switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
93 	case UCSI_CONSTAT_PWR_OPMODE_PD:
94 		if (con->num_pdos > 0) {
95 			pdo = con->src_pdos[con->num_pdos - 1];
96 			val->intval = pdo_fixed_voltage(pdo) * 1000;
97 		} else {
98 			val->intval = 0;
99 		}
100 		break;
101 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
102 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
103 	case UCSI_CONSTAT_PWR_OPMODE_BC:
104 	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
105 		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
106 		break;
107 	default:
108 		val->intval = 0;
109 		break;
110 	}
111 	return 0;
112 }
113 
ucsi_psy_get_voltage_now(struct ucsi_connector * con,union power_supply_propval * val)114 static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
115 				    union power_supply_propval *val)
116 {
117 	int index;
118 	u32 pdo;
119 
120 	switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
121 	case UCSI_CONSTAT_PWR_OPMODE_PD:
122 		index = rdo_index(con->rdo);
123 		if (index > 0) {
124 			pdo = con->src_pdos[index - 1];
125 			val->intval = pdo_fixed_voltage(pdo) * 1000;
126 		} else {
127 			val->intval = 0;
128 		}
129 		break;
130 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
131 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
132 	case UCSI_CONSTAT_PWR_OPMODE_BC:
133 	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
134 		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
135 		break;
136 	default:
137 		val->intval = 0;
138 		break;
139 	}
140 	return 0;
141 }
142 
ucsi_psy_get_current_max(struct ucsi_connector * con,union power_supply_propval * val)143 static int ucsi_psy_get_current_max(struct ucsi_connector *con,
144 				    union power_supply_propval *val)
145 {
146 	u32 pdo;
147 
148 	switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
149 	case UCSI_CONSTAT_PWR_OPMODE_PD:
150 		if (con->num_pdos > 0) {
151 			pdo = con->src_pdos[con->num_pdos - 1];
152 			val->intval = pdo_max_current(pdo) * 1000;
153 		} else {
154 			val->intval = 0;
155 		}
156 		break;
157 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
158 		val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
159 		break;
160 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
161 		val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
162 		break;
163 	case UCSI_CONSTAT_PWR_OPMODE_BC:
164 	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
165 	/* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
166 	default:
167 		val->intval = 0;
168 		break;
169 	}
170 	return 0;
171 }
172 
ucsi_psy_get_current_now(struct ucsi_connector * con,union power_supply_propval * val)173 static int ucsi_psy_get_current_now(struct ucsi_connector *con,
174 				    union power_supply_propval *val)
175 {
176 	if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD)
177 		val->intval = rdo_op_current(con->rdo) * 1000;
178 	else
179 		val->intval = 0;
180 	return 0;
181 }
182 
ucsi_psy_get_usb_type(struct ucsi_connector * con,union power_supply_propval * val)183 static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
184 				 union power_supply_propval *val)
185 {
186 	val->intval = POWER_SUPPLY_USB_TYPE_C;
187 	if (UCSI_CONSTAT(con, CONNECTED) &&
188 	    UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD)
189 		val->intval = POWER_SUPPLY_USB_TYPE_PD;
190 
191 	return 0;
192 }
193 
ucsi_psy_get_charge_type(struct ucsi_connector * con,union power_supply_propval * val)194 static int ucsi_psy_get_charge_type(struct ucsi_connector *con, union power_supply_propval *val)
195 {
196 	if (!(UCSI_CONSTAT(con, CONNECTED))) {
197 		val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
198 		return 0;
199 	}
200 
201 	/* The Battery Charging Cabability Status field is only valid in sink role. */
202 	if (UCSI_CONSTAT(con, PWR_DIR) != TYPEC_SINK) {
203 		val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
204 		return 0;
205 	}
206 
207 	switch (UCSI_CONSTAT(con, BC_STATUS)) {
208 	case UCSI_CONSTAT_BC_NOMINAL_CHARGING:
209 		val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
210 		break;
211 	case UCSI_CONSTAT_BC_SLOW_CHARGING:
212 	case UCSI_CONSTAT_BC_TRICKLE_CHARGING:
213 		val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
214 		break;
215 	default:
216 		val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
217 		break;
218 	}
219 
220 	return 0;
221 }
222 
ucsi_psy_get_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)223 static int ucsi_psy_get_prop(struct power_supply *psy,
224 			     enum power_supply_property psp,
225 			     union power_supply_propval *val)
226 {
227 	struct ucsi_connector *con = power_supply_get_drvdata(psy);
228 
229 	switch (psp) {
230 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
231 		return ucsi_psy_get_charge_type(con, val);
232 	case POWER_SUPPLY_PROP_USB_TYPE:
233 		return ucsi_psy_get_usb_type(con, val);
234 	case POWER_SUPPLY_PROP_ONLINE:
235 		return ucsi_psy_get_online(con, val);
236 	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
237 		return ucsi_psy_get_voltage_min(con, val);
238 	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
239 		return ucsi_psy_get_voltage_max(con, val);
240 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
241 		return ucsi_psy_get_voltage_now(con, val);
242 	case POWER_SUPPLY_PROP_CURRENT_MAX:
243 		return ucsi_psy_get_current_max(con, val);
244 	case POWER_SUPPLY_PROP_CURRENT_NOW:
245 		return ucsi_psy_get_current_now(con, val);
246 	case POWER_SUPPLY_PROP_SCOPE:
247 		return ucsi_psy_get_scope(con, val);
248 	default:
249 		return -EINVAL;
250 	}
251 }
252 
ucsi_register_port_psy(struct ucsi_connector * con)253 int ucsi_register_port_psy(struct ucsi_connector *con)
254 {
255 	struct power_supply_config psy_cfg = {};
256 	struct device *dev = con->ucsi->dev;
257 	char *psy_name;
258 
259 	psy_cfg.drv_data = con;
260 	psy_cfg.fwnode = dev_fwnode(dev);
261 
262 	psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
263 				  dev_name(dev), con->num);
264 	if (!psy_name)
265 		return -ENOMEM;
266 
267 	con->psy_desc.name = psy_name;
268 	con->psy_desc.type = POWER_SUPPLY_TYPE_USB;
269 	con->psy_desc.usb_types = BIT(POWER_SUPPLY_USB_TYPE_C)  |
270 				  BIT(POWER_SUPPLY_USB_TYPE_PD) |
271 				  BIT(POWER_SUPPLY_USB_TYPE_PD_PPS);
272 	con->psy_desc.properties = ucsi_psy_props;
273 	con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props);
274 	con->psy_desc.get_property = ucsi_psy_get_prop;
275 
276 	con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
277 
278 	return PTR_ERR_OR_ZERO(con->psy);
279 }
280 
ucsi_unregister_port_psy(struct ucsi_connector * con)281 void ucsi_unregister_port_psy(struct ucsi_connector *con)
282 {
283 	if (IS_ERR_OR_NULL(con->psy))
284 		return;
285 
286 	power_supply_unregister(con->psy);
287 	con->psy = NULL;
288 }
289 
ucsi_port_psy_changed(struct ucsi_connector * con)290 void ucsi_port_psy_changed(struct ucsi_connector *con)
291 {
292 	if (IS_ERR_OR_NULL(con->psy))
293 		return;
294 
295 	power_supply_changed(con->psy);
296 }
297