1 // SPDX-License-Identifier: GPL-2.0
3 * Power Supply for UCSI
5 * Copyright (C) 2020, Intel Corporation
10 #include <linux/property.h>
11 #include <linux/usb/pd.h>
15 /* Power Supply access to expose source power information */
16 enum ucsi_psy_online_states {
18 UCSI_PSY_FIXED_ONLINE,
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,
34 static int ucsi_psy_get_scope(struct ucsi_connector *con,
35 union power_supply_propval *val)
37 u8 scope = POWER_SUPPLY_SCOPE_UNKNOWN;
38 struct device *dev = con->ucsi->dev;
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;
45 if (con->ucsi->cap.attributes & mask)
46 scope = POWER_SUPPLY_SCOPE_SYSTEM;
48 scope = POWER_SUPPLY_SCOPE_DEVICE;
54 static int ucsi_psy_get_online(struct ucsi_connector *con,
55 union power_supply_propval *val)
57 val->intval = UCSI_PSY_OFFLINE;
58 if (con->status.flags & UCSI_CONSTAT_CONNECTED &&
59 (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK)
60 val->intval = UCSI_PSY_FIXED_ONLINE;
64 static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
65 union power_supply_propval *val)
69 switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
70 case UCSI_CONSTAT_PWR_OPMODE_PD:
71 pdo = con->src_pdos[0];
72 val->intval = pdo_fixed_voltage(pdo) * 1000;
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;
87 static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
88 union power_supply_propval *val)
92 switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
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;
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;
114 static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
115 union power_supply_propval *val)
120 switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
121 case UCSI_CONSTAT_PWR_OPMODE_PD:
122 index = rdo_index(con->rdo);
124 pdo = con->src_pdos[index - 1];
125 val->intval = pdo_fixed_voltage(pdo) * 1000;
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;
143 static int ucsi_psy_get_current_max(struct ucsi_connector *con,
144 union power_supply_propval *val)
148 switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
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;
157 case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
158 val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
160 case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
161 val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
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 */
173 static int ucsi_psy_get_current_now(struct ucsi_connector *con,
174 union power_supply_propval *val)
176 u16 flags = con->status.flags;
178 if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
179 val->intval = rdo_op_current(con->rdo) * 1000;
185 static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
186 union power_supply_propval *val)
188 u16 flags = con->status.flags;
190 val->intval = POWER_SUPPLY_USB_TYPE_C;
191 if (flags & UCSI_CONSTAT_CONNECTED &&
192 UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
193 val->intval = POWER_SUPPLY_USB_TYPE_PD;
198 static int ucsi_psy_get_charge_type(struct ucsi_connector *con, union power_supply_propval *val)
200 if (!(con->status.flags & UCSI_CONSTAT_CONNECTED)) {
201 val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
205 /* The Battery Charging Cabability Status field is only valid in sink role. */
206 if ((con->status.flags & UCSI_CONSTAT_PWR_DIR) != TYPEC_SINK) {
207 val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
211 switch (UCSI_CONSTAT_BC_STATUS(con->status.pwr_status)) {
212 case UCSI_CONSTAT_BC_NOMINAL_CHARGING:
213 val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
215 case UCSI_CONSTAT_BC_SLOW_CHARGING:
216 case UCSI_CONSTAT_BC_TRICKLE_CHARGING:
217 val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
220 val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
227 static int ucsi_psy_get_prop(struct power_supply *psy,
228 enum power_supply_property psp,
229 union power_supply_propval *val)
231 struct ucsi_connector *con = power_supply_get_drvdata(psy);
234 case POWER_SUPPLY_PROP_CHARGE_TYPE:
235 return ucsi_psy_get_charge_type(con, val);
236 case POWER_SUPPLY_PROP_USB_TYPE:
237 return ucsi_psy_get_usb_type(con, val);
238 case POWER_SUPPLY_PROP_ONLINE:
239 return ucsi_psy_get_online(con, val);
240 case POWER_SUPPLY_PROP_VOLTAGE_MIN:
241 return ucsi_psy_get_voltage_min(con, val);
242 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
243 return ucsi_psy_get_voltage_max(con, val);
244 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
245 return ucsi_psy_get_voltage_now(con, val);
246 case POWER_SUPPLY_PROP_CURRENT_MAX:
247 return ucsi_psy_get_current_max(con, val);
248 case POWER_SUPPLY_PROP_CURRENT_NOW:
249 return ucsi_psy_get_current_now(con, val);
250 case POWER_SUPPLY_PROP_SCOPE:
251 return ucsi_psy_get_scope(con, val);
257 static enum power_supply_usb_type ucsi_psy_usb_types[] = {
258 POWER_SUPPLY_USB_TYPE_C,
259 POWER_SUPPLY_USB_TYPE_PD,
260 POWER_SUPPLY_USB_TYPE_PD_PPS,
263 int ucsi_register_port_psy(struct ucsi_connector *con)
265 struct power_supply_config psy_cfg = {};
266 struct device *dev = con->ucsi->dev;
269 psy_cfg.drv_data = con;
270 psy_cfg.fwnode = dev_fwnode(dev);
272 psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
273 dev_name(dev), con->num);
277 con->psy_desc.name = psy_name;
278 con->psy_desc.type = POWER_SUPPLY_TYPE_USB;
279 con->psy_desc.usb_types = ucsi_psy_usb_types;
280 con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
281 con->psy_desc.properties = ucsi_psy_props;
282 con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props);
283 con->psy_desc.get_property = ucsi_psy_get_prop;
285 con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
287 return PTR_ERR_OR_ZERO(con->psy);
290 void ucsi_unregister_port_psy(struct ucsi_connector *con)
292 if (IS_ERR_OR_NULL(con->psy))
295 power_supply_unregister(con->psy);
299 void ucsi_port_psy_changed(struct ucsi_connector *con)
301 if (IS_ERR_OR_NULL(con->psy))
304 power_supply_changed(con->psy);