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 <abhilash.k.v@intel.com>
7  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
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_USB_TYPE,
24 	POWER_SUPPLY_PROP_ONLINE,
25 	POWER_SUPPLY_PROP_VOLTAGE_MIN,
26 	POWER_SUPPLY_PROP_VOLTAGE_MAX,
27 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
28 	POWER_SUPPLY_PROP_CURRENT_MAX,
29 	POWER_SUPPLY_PROP_CURRENT_NOW,
30 };
31 
ucsi_psy_get_online(struct ucsi_connector * con,union power_supply_propval * val)32 static int ucsi_psy_get_online(struct ucsi_connector *con,
33 			       union power_supply_propval *val)
34 {
35 	val->intval = UCSI_PSY_OFFLINE;
36 	if (con->status.flags & UCSI_CONSTAT_CONNECTED &&
37 	    (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK)
38 		val->intval = UCSI_PSY_FIXED_ONLINE;
39 	return 0;
40 }
41 
ucsi_psy_get_voltage_min(struct ucsi_connector * con,union power_supply_propval * val)42 static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
43 				    union power_supply_propval *val)
44 {
45 	u32 pdo;
46 
47 	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
48 	case UCSI_CONSTAT_PWR_OPMODE_PD:
49 		pdo = con->src_pdos[0];
50 		val->intval = pdo_fixed_voltage(pdo) * 1000;
51 		break;
52 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
53 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
54 	case UCSI_CONSTAT_PWR_OPMODE_BC:
55 	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
56 		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
57 		break;
58 	default:
59 		val->intval = 0;
60 		break;
61 	}
62 	return 0;
63 }
64 
ucsi_psy_get_voltage_max(struct ucsi_connector * con,union power_supply_propval * val)65 static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
66 				    union power_supply_propval *val)
67 {
68 	u32 pdo;
69 
70 	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
71 	case UCSI_CONSTAT_PWR_OPMODE_PD:
72 		if (con->num_pdos > 0) {
73 			pdo = con->src_pdos[con->num_pdos - 1];
74 			val->intval = pdo_fixed_voltage(pdo) * 1000;
75 		} else {
76 			val->intval = 0;
77 		}
78 		break;
79 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
80 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
81 	case UCSI_CONSTAT_PWR_OPMODE_BC:
82 	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
83 		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
84 		break;
85 	default:
86 		val->intval = 0;
87 		break;
88 	}
89 	return 0;
90 }
91 
ucsi_psy_get_voltage_now(struct ucsi_connector * con,union power_supply_propval * val)92 static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
93 				    union power_supply_propval *val)
94 {
95 	int index;
96 	u32 pdo;
97 
98 	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
99 	case UCSI_CONSTAT_PWR_OPMODE_PD:
100 		index = rdo_index(con->rdo);
101 		if (index > 0) {
102 			pdo = con->src_pdos[index - 1];
103 			val->intval = pdo_fixed_voltage(pdo) * 1000;
104 		} else {
105 			val->intval = 0;
106 		}
107 		break;
108 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
109 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
110 	case UCSI_CONSTAT_PWR_OPMODE_BC:
111 	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
112 		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
113 		break;
114 	default:
115 		val->intval = 0;
116 		break;
117 	}
118 	return 0;
119 }
120 
ucsi_psy_get_current_max(struct ucsi_connector * con,union power_supply_propval * val)121 static int ucsi_psy_get_current_max(struct ucsi_connector *con,
122 				    union power_supply_propval *val)
123 {
124 	u32 pdo;
125 
126 	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
127 	case UCSI_CONSTAT_PWR_OPMODE_PD:
128 		if (con->num_pdos > 0) {
129 			pdo = con->src_pdos[con->num_pdos - 1];
130 			val->intval = pdo_max_current(pdo) * 1000;
131 		} else {
132 			val->intval = 0;
133 		}
134 		break;
135 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
136 		val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
137 		break;
138 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
139 		val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
140 		break;
141 	case UCSI_CONSTAT_PWR_OPMODE_BC:
142 	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
143 	/* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
144 	default:
145 		val->intval = 0;
146 		break;
147 	}
148 	return 0;
149 }
150 
ucsi_psy_get_current_now(struct ucsi_connector * con,union power_supply_propval * val)151 static int ucsi_psy_get_current_now(struct ucsi_connector *con,
152 				    union power_supply_propval *val)
153 {
154 	u16 flags = con->status.flags;
155 
156 	if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
157 		val->intval = rdo_op_current(con->rdo) * 1000;
158 	else
159 		val->intval = 0;
160 	return 0;
161 }
162 
ucsi_psy_get_usb_type(struct ucsi_connector * con,union power_supply_propval * val)163 static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
164 				 union power_supply_propval *val)
165 {
166 	u16 flags = con->status.flags;
167 
168 	val->intval = POWER_SUPPLY_USB_TYPE_C;
169 	if (flags & UCSI_CONSTAT_CONNECTED &&
170 	    UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
171 		val->intval = POWER_SUPPLY_USB_TYPE_PD;
172 
173 	return 0;
174 }
175 
ucsi_psy_get_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)176 static int ucsi_psy_get_prop(struct power_supply *psy,
177 			     enum power_supply_property psp,
178 			     union power_supply_propval *val)
179 {
180 	struct ucsi_connector *con = power_supply_get_drvdata(psy);
181 
182 	switch (psp) {
183 	case POWER_SUPPLY_PROP_USB_TYPE:
184 		return ucsi_psy_get_usb_type(con, val);
185 	case POWER_SUPPLY_PROP_ONLINE:
186 		return ucsi_psy_get_online(con, val);
187 	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
188 		return ucsi_psy_get_voltage_min(con, val);
189 	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
190 		return ucsi_psy_get_voltage_max(con, val);
191 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
192 		return ucsi_psy_get_voltage_now(con, val);
193 	case POWER_SUPPLY_PROP_CURRENT_MAX:
194 		return ucsi_psy_get_current_max(con, val);
195 	case POWER_SUPPLY_PROP_CURRENT_NOW:
196 		return ucsi_psy_get_current_now(con, val);
197 	default:
198 		return -EINVAL;
199 	}
200 }
201 
202 static enum power_supply_usb_type ucsi_psy_usb_types[] = {
203 	POWER_SUPPLY_USB_TYPE_C,
204 	POWER_SUPPLY_USB_TYPE_PD,
205 	POWER_SUPPLY_USB_TYPE_PD_PPS,
206 };
207 
ucsi_register_port_psy(struct ucsi_connector * con)208 int ucsi_register_port_psy(struct ucsi_connector *con)
209 {
210 	struct power_supply_config psy_cfg = {};
211 	struct device *dev = con->ucsi->dev;
212 	char *psy_name;
213 
214 	psy_cfg.drv_data = con;
215 	psy_cfg.fwnode = dev_fwnode(dev);
216 
217 	psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
218 				  dev_name(dev), con->num);
219 	if (!psy_name)
220 		return -ENOMEM;
221 
222 	con->psy_desc.name = psy_name;
223 	con->psy_desc.type = POWER_SUPPLY_TYPE_USB;
224 	con->psy_desc.usb_types = ucsi_psy_usb_types;
225 	con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
226 	con->psy_desc.properties = ucsi_psy_props;
227 	con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props);
228 	con->psy_desc.get_property = ucsi_psy_get_prop;
229 
230 	con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
231 
232 	return PTR_ERR_OR_ZERO(con->psy);
233 }
234 
ucsi_unregister_port_psy(struct ucsi_connector * con)235 void ucsi_unregister_port_psy(struct ucsi_connector *con)
236 {
237 	if (IS_ERR_OR_NULL(con->psy))
238 		return;
239 
240 	power_supply_unregister(con->psy);
241 	con->psy = NULL;
242 }
243 
ucsi_port_psy_changed(struct ucsi_connector * con)244 void ucsi_port_psy_changed(struct ucsi_connector *con)
245 {
246 	if (IS_ERR_OR_NULL(con->psy))
247 		return;
248 
249 	power_supply_changed(con->psy);
250 }
251