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
);
40 if (scope
== POWER_SUPPLY_SCOPE_UNKNOWN
) {
41 u32 mask
= UCSI_CAP_ATTR_POWER_AC_SUPPLY
|
42 UCSI_CAP_ATTR_BATTERY_CHARGING
;
44 if (con
->ucsi
->cap
.attributes
& mask
)
45 scope
= POWER_SUPPLY_SCOPE_SYSTEM
;
47 scope
= POWER_SUPPLY_SCOPE_DEVICE
;
53 static int ucsi_psy_get_online(struct ucsi_connector
*con
,
54 union power_supply_propval
*val
)
56 val
->intval
= UCSI_PSY_OFFLINE
;
57 if (con
->status
.flags
& UCSI_CONSTAT_CONNECTED
&&
58 (con
->status
.flags
& UCSI_CONSTAT_PWR_DIR
) == TYPEC_SINK
)
59 val
->intval
= UCSI_PSY_FIXED_ONLINE
;
63 static int ucsi_psy_get_voltage_min(struct ucsi_connector
*con
,
64 union power_supply_propval
*val
)
68 switch (UCSI_CONSTAT_PWR_OPMODE(con
->status
.flags
)) {
69 case UCSI_CONSTAT_PWR_OPMODE_PD
:
70 pdo
= con
->src_pdos
[0];
71 val
->intval
= pdo_fixed_voltage(pdo
) * 1000;
73 case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0
:
74 case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5
:
75 case UCSI_CONSTAT_PWR_OPMODE_BC
:
76 case UCSI_CONSTAT_PWR_OPMODE_DEFAULT
:
77 val
->intval
= UCSI_TYPEC_VSAFE5V
* 1000;
86 static int ucsi_psy_get_voltage_max(struct ucsi_connector
*con
,
87 union power_supply_propval
*val
)
91 switch (UCSI_CONSTAT_PWR_OPMODE(con
->status
.flags
)) {
92 case UCSI_CONSTAT_PWR_OPMODE_PD
:
93 if (con
->num_pdos
> 0) {
94 pdo
= con
->src_pdos
[con
->num_pdos
- 1];
95 val
->intval
= pdo_fixed_voltage(pdo
) * 1000;
100 case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0
:
101 case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5
:
102 case UCSI_CONSTAT_PWR_OPMODE_BC
:
103 case UCSI_CONSTAT_PWR_OPMODE_DEFAULT
:
104 val
->intval
= UCSI_TYPEC_VSAFE5V
* 1000;
113 static int ucsi_psy_get_voltage_now(struct ucsi_connector
*con
,
114 union power_supply_propval
*val
)
119 switch (UCSI_CONSTAT_PWR_OPMODE(con
->status
.flags
)) {
120 case UCSI_CONSTAT_PWR_OPMODE_PD
:
121 index
= rdo_index(con
->rdo
);
123 pdo
= con
->src_pdos
[index
- 1];
124 val
->intval
= pdo_fixed_voltage(pdo
) * 1000;
129 case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0
:
130 case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5
:
131 case UCSI_CONSTAT_PWR_OPMODE_BC
:
132 case UCSI_CONSTAT_PWR_OPMODE_DEFAULT
:
133 val
->intval
= UCSI_TYPEC_VSAFE5V
* 1000;
142 static int ucsi_psy_get_current_max(struct ucsi_connector
*con
,
143 union power_supply_propval
*val
)
147 switch (UCSI_CONSTAT_PWR_OPMODE(con
->status
.flags
)) {
148 case UCSI_CONSTAT_PWR_OPMODE_PD
:
149 if (con
->num_pdos
> 0) {
150 pdo
= con
->src_pdos
[con
->num_pdos
- 1];
151 val
->intval
= pdo_max_current(pdo
) * 1000;
156 case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5
:
157 val
->intval
= UCSI_TYPEC_1_5_CURRENT
* 1000;
159 case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0
:
160 val
->intval
= UCSI_TYPEC_3_0_CURRENT
* 1000;
162 case UCSI_CONSTAT_PWR_OPMODE_BC
:
163 case UCSI_CONSTAT_PWR_OPMODE_DEFAULT
:
164 /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
172 static int ucsi_psy_get_current_now(struct ucsi_connector
*con
,
173 union power_supply_propval
*val
)
175 u16 flags
= con
->status
.flags
;
177 if (UCSI_CONSTAT_PWR_OPMODE(flags
) == UCSI_CONSTAT_PWR_OPMODE_PD
)
178 val
->intval
= rdo_op_current(con
->rdo
) * 1000;
184 static int ucsi_psy_get_usb_type(struct ucsi_connector
*con
,
185 union power_supply_propval
*val
)
187 u16 flags
= con
->status
.flags
;
189 val
->intval
= POWER_SUPPLY_USB_TYPE_C
;
190 if (flags
& UCSI_CONSTAT_CONNECTED
&&
191 UCSI_CONSTAT_PWR_OPMODE(flags
) == UCSI_CONSTAT_PWR_OPMODE_PD
)
192 val
->intval
= POWER_SUPPLY_USB_TYPE_PD
;
197 static int ucsi_psy_get_prop(struct power_supply
*psy
,
198 enum power_supply_property psp
,
199 union power_supply_propval
*val
)
201 struct ucsi_connector
*con
= power_supply_get_drvdata(psy
);
204 case POWER_SUPPLY_PROP_USB_TYPE
:
205 return ucsi_psy_get_usb_type(con
, val
);
206 case POWER_SUPPLY_PROP_ONLINE
:
207 return ucsi_psy_get_online(con
, val
);
208 case POWER_SUPPLY_PROP_VOLTAGE_MIN
:
209 return ucsi_psy_get_voltage_min(con
, val
);
210 case POWER_SUPPLY_PROP_VOLTAGE_MAX
:
211 return ucsi_psy_get_voltage_max(con
, val
);
212 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
213 return ucsi_psy_get_voltage_now(con
, val
);
214 case POWER_SUPPLY_PROP_CURRENT_MAX
:
215 return ucsi_psy_get_current_max(con
, val
);
216 case POWER_SUPPLY_PROP_CURRENT_NOW
:
217 return ucsi_psy_get_current_now(con
, val
);
218 case POWER_SUPPLY_PROP_SCOPE
:
219 return ucsi_psy_get_scope(con
, val
);
225 static enum power_supply_usb_type ucsi_psy_usb_types
[] = {
226 POWER_SUPPLY_USB_TYPE_C
,
227 POWER_SUPPLY_USB_TYPE_PD
,
228 POWER_SUPPLY_USB_TYPE_PD_PPS
,
231 int ucsi_register_port_psy(struct ucsi_connector
*con
)
233 struct power_supply_config psy_cfg
= {};
234 struct device
*dev
= con
->ucsi
->dev
;
237 psy_cfg
.drv_data
= con
;
238 psy_cfg
.fwnode
= dev_fwnode(dev
);
240 psy_name
= devm_kasprintf(dev
, GFP_KERNEL
, "ucsi-source-psy-%s%d",
241 dev_name(dev
), con
->num
);
245 con
->psy_desc
.name
= psy_name
;
246 con
->psy_desc
.type
= POWER_SUPPLY_TYPE_USB
;
247 con
->psy_desc
.usb_types
= ucsi_psy_usb_types
;
248 con
->psy_desc
.num_usb_types
= ARRAY_SIZE(ucsi_psy_usb_types
);
249 con
->psy_desc
.properties
= ucsi_psy_props
;
250 con
->psy_desc
.num_properties
= ARRAY_SIZE(ucsi_psy_props
);
251 con
->psy_desc
.get_property
= ucsi_psy_get_prop
;
253 con
->psy
= power_supply_register(dev
, &con
->psy_desc
, &psy_cfg
);
255 return PTR_ERR_OR_ZERO(con
->psy
);
258 void ucsi_unregister_port_psy(struct ucsi_connector
*con
)
260 if (IS_ERR_OR_NULL(con
->psy
))
263 power_supply_unregister(con
->psy
);
267 void ucsi_port_psy_changed(struct ucsi_connector
*con
)
269 if (IS_ERR_OR_NULL(con
->psy
))
272 power_supply_changed(con
->psy
);