GNU Linux-libre 6.8.7-gnu
[releases.git] / drivers / usb / typec / ucsi / psy.c
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         POWER_SUPPLY_PROP_SCOPE,
31 };
32
33 static int ucsi_psy_get_scope(struct ucsi_connector *con,
34                               union power_supply_propval *val)
35 {
36         u8 scope = POWER_SUPPLY_SCOPE_UNKNOWN;
37         struct device *dev = con->ucsi->dev;
38
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;
43
44                 if (con->ucsi->cap.attributes & mask)
45                         scope = POWER_SUPPLY_SCOPE_SYSTEM;
46                 else
47                         scope = POWER_SUPPLY_SCOPE_DEVICE;
48         }
49         val->intval = scope;
50         return 0;
51 }
52
53 static int ucsi_psy_get_online(struct ucsi_connector *con,
54                                union power_supply_propval *val)
55 {
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;
60         return 0;
61 }
62
63 static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
64                                     union power_supply_propval *val)
65 {
66         u32 pdo;
67
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;
72                 break;
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;
78                 break;
79         default:
80                 val->intval = 0;
81                 break;
82         }
83         return 0;
84 }
85
86 static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
87                                     union power_supply_propval *val)
88 {
89         u32 pdo;
90
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;
96                 } else {
97                         val->intval = 0;
98                 }
99                 break;
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;
105                 break;
106         default:
107                 val->intval = 0;
108                 break;
109         }
110         return 0;
111 }
112
113 static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
114                                     union power_supply_propval *val)
115 {
116         int index;
117         u32 pdo;
118
119         switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
120         case UCSI_CONSTAT_PWR_OPMODE_PD:
121                 index = rdo_index(con->rdo);
122                 if (index > 0) {
123                         pdo = con->src_pdos[index - 1];
124                         val->intval = pdo_fixed_voltage(pdo) * 1000;
125                 } else {
126                         val->intval = 0;
127                 }
128                 break;
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;
134                 break;
135         default:
136                 val->intval = 0;
137                 break;
138         }
139         return 0;
140 }
141
142 static int ucsi_psy_get_current_max(struct ucsi_connector *con,
143                                     union power_supply_propval *val)
144 {
145         u32 pdo;
146
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;
152                 } else {
153                         val->intval = 0;
154                 }
155                 break;
156         case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
157                 val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
158                 break;
159         case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
160                 val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
161                 break;
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 */
165         default:
166                 val->intval = 0;
167                 break;
168         }
169         return 0;
170 }
171
172 static int ucsi_psy_get_current_now(struct ucsi_connector *con,
173                                     union power_supply_propval *val)
174 {
175         u16 flags = con->status.flags;
176
177         if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
178                 val->intval = rdo_op_current(con->rdo) * 1000;
179         else
180                 val->intval = 0;
181         return 0;
182 }
183
184 static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
185                                  union power_supply_propval *val)
186 {
187         u16 flags = con->status.flags;
188
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;
193
194         return 0;
195 }
196
197 static int ucsi_psy_get_prop(struct power_supply *psy,
198                              enum power_supply_property psp,
199                              union power_supply_propval *val)
200 {
201         struct ucsi_connector *con = power_supply_get_drvdata(psy);
202
203         switch (psp) {
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);
220         default:
221                 return -EINVAL;
222         }
223 }
224
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,
229 };
230
231 int ucsi_register_port_psy(struct ucsi_connector *con)
232 {
233         struct power_supply_config psy_cfg = {};
234         struct device *dev = con->ucsi->dev;
235         char *psy_name;
236
237         psy_cfg.drv_data = con;
238         psy_cfg.fwnode = dev_fwnode(dev);
239
240         psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
241                                   dev_name(dev), con->num);
242         if (!psy_name)
243                 return -ENOMEM;
244
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;
252
253         con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
254
255         return PTR_ERR_OR_ZERO(con->psy);
256 }
257
258 void ucsi_unregister_port_psy(struct ucsi_connector *con)
259 {
260         if (IS_ERR_OR_NULL(con->psy))
261                 return;
262
263         power_supply_unregister(con->psy);
264         con->psy = NULL;
265 }
266
267 void ucsi_port_psy_changed(struct ucsi_connector *con)
268 {
269         if (IS_ERR_OR_NULL(con->psy))
270                 return;
271
272         power_supply_changed(con->psy);
273 }