1 // SPDX-License-Identifier: GPL-2.0
3 * Power Supply for UCSI
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>
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_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 POWER_SUPPLY_PROP_SCOPE,
33 static int ucsi_psy_get_scope(struct ucsi_connector *con,
34 union power_supply_propval *val)
36 u8 scope = POWER_SUPPLY_SCOPE_UNKNOWN;
37 struct device *dev = con->ucsi->dev;
39 device_property_read_u8(dev, "scope", &scope);
44 static int ucsi_psy_get_online(struct ucsi_connector *con,
45 union power_supply_propval *val)
47 val->intval = UCSI_PSY_OFFLINE;
48 if (con->status.flags & UCSI_CONSTAT_CONNECTED &&
49 (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK)
50 val->intval = UCSI_PSY_FIXED_ONLINE;
54 static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
55 union power_supply_propval *val)
59 switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
60 case UCSI_CONSTAT_PWR_OPMODE_PD:
61 pdo = con->src_pdos[0];
62 val->intval = pdo_fixed_voltage(pdo) * 1000;
64 case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
65 case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
66 case UCSI_CONSTAT_PWR_OPMODE_BC:
67 case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
68 val->intval = UCSI_TYPEC_VSAFE5V * 1000;
77 static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
78 union power_supply_propval *val)
82 switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
83 case UCSI_CONSTAT_PWR_OPMODE_PD:
84 if (con->num_pdos > 0) {
85 pdo = con->src_pdos[con->num_pdos - 1];
86 val->intval = pdo_fixed_voltage(pdo) * 1000;
91 case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
92 case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
93 case UCSI_CONSTAT_PWR_OPMODE_BC:
94 case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
95 val->intval = UCSI_TYPEC_VSAFE5V * 1000;
104 static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
105 union power_supply_propval *val)
110 switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
111 case UCSI_CONSTAT_PWR_OPMODE_PD:
112 index = rdo_index(con->rdo);
114 pdo = con->src_pdos[index - 1];
115 val->intval = pdo_fixed_voltage(pdo) * 1000;
120 case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
121 case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
122 case UCSI_CONSTAT_PWR_OPMODE_BC:
123 case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
124 val->intval = UCSI_TYPEC_VSAFE5V * 1000;
133 static int ucsi_psy_get_current_max(struct ucsi_connector *con,
134 union power_supply_propval *val)
138 switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
139 case UCSI_CONSTAT_PWR_OPMODE_PD:
140 if (con->num_pdos > 0) {
141 pdo = con->src_pdos[con->num_pdos - 1];
142 val->intval = pdo_max_current(pdo) * 1000;
147 case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
148 val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
150 case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
151 val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
153 case UCSI_CONSTAT_PWR_OPMODE_BC:
154 case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
155 /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
163 static int ucsi_psy_get_current_now(struct ucsi_connector *con,
164 union power_supply_propval *val)
166 u16 flags = con->status.flags;
168 if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
169 val->intval = rdo_op_current(con->rdo) * 1000;
175 static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
176 union power_supply_propval *val)
178 u16 flags = con->status.flags;
180 val->intval = POWER_SUPPLY_USB_TYPE_C;
181 if (flags & UCSI_CONSTAT_CONNECTED &&
182 UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
183 val->intval = POWER_SUPPLY_USB_TYPE_PD;
188 static int ucsi_psy_get_prop(struct power_supply *psy,
189 enum power_supply_property psp,
190 union power_supply_propval *val)
192 struct ucsi_connector *con = power_supply_get_drvdata(psy);
195 case POWER_SUPPLY_PROP_USB_TYPE:
196 return ucsi_psy_get_usb_type(con, val);
197 case POWER_SUPPLY_PROP_ONLINE:
198 return ucsi_psy_get_online(con, val);
199 case POWER_SUPPLY_PROP_VOLTAGE_MIN:
200 return ucsi_psy_get_voltage_min(con, val);
201 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
202 return ucsi_psy_get_voltage_max(con, val);
203 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
204 return ucsi_psy_get_voltage_now(con, val);
205 case POWER_SUPPLY_PROP_CURRENT_MAX:
206 return ucsi_psy_get_current_max(con, val);
207 case POWER_SUPPLY_PROP_CURRENT_NOW:
208 return ucsi_psy_get_current_now(con, val);
209 case POWER_SUPPLY_PROP_SCOPE:
210 return ucsi_psy_get_scope(con, val);
216 static enum power_supply_usb_type ucsi_psy_usb_types[] = {
217 POWER_SUPPLY_USB_TYPE_C,
218 POWER_SUPPLY_USB_TYPE_PD,
219 POWER_SUPPLY_USB_TYPE_PD_PPS,
222 int ucsi_register_port_psy(struct ucsi_connector *con)
224 struct power_supply_config psy_cfg = {};
225 struct device *dev = con->ucsi->dev;
228 psy_cfg.drv_data = con;
229 psy_cfg.fwnode = dev_fwnode(dev);
231 psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
232 dev_name(dev), con->num);
236 con->psy_desc.name = psy_name;
237 con->psy_desc.type = POWER_SUPPLY_TYPE_USB;
238 con->psy_desc.usb_types = ucsi_psy_usb_types;
239 con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
240 con->psy_desc.properties = ucsi_psy_props;
241 con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props);
242 con->psy_desc.get_property = ucsi_psy_get_prop;
244 con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
246 return PTR_ERR_OR_ZERO(con->psy);
249 void ucsi_unregister_port_psy(struct ucsi_connector *con)
251 if (IS_ERR_OR_NULL(con->psy))
254 power_supply_unregister(con->psy);
258 void ucsi_port_psy_changed(struct ucsi_connector *con)
260 if (IS_ERR_OR_NULL(con->psy))
263 power_supply_changed(con->psy);