powerpc/mm: Avoid calling arch_enter/leave_lazy_mmu() in set_ptes
[platform/kernel/linux-starfive.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         val->intval = scope;
41         return 0;
42 }
43
44 static int ucsi_psy_get_online(struct ucsi_connector *con,
45                                union power_supply_propval *val)
46 {
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;
51         return 0;
52 }
53
54 static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
55                                     union power_supply_propval *val)
56 {
57         u32 pdo;
58
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;
63                 break;
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;
69                 break;
70         default:
71                 val->intval = 0;
72                 break;
73         }
74         return 0;
75 }
76
77 static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
78                                     union power_supply_propval *val)
79 {
80         u32 pdo;
81
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;
87                 } else {
88                         val->intval = 0;
89                 }
90                 break;
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;
96                 break;
97         default:
98                 val->intval = 0;
99                 break;
100         }
101         return 0;
102 }
103
104 static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
105                                     union power_supply_propval *val)
106 {
107         int index;
108         u32 pdo;
109
110         switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
111         case UCSI_CONSTAT_PWR_OPMODE_PD:
112                 index = rdo_index(con->rdo);
113                 if (index > 0) {
114                         pdo = con->src_pdos[index - 1];
115                         val->intval = pdo_fixed_voltage(pdo) * 1000;
116                 } else {
117                         val->intval = 0;
118                 }
119                 break;
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;
125                 break;
126         default:
127                 val->intval = 0;
128                 break;
129         }
130         return 0;
131 }
132
133 static int ucsi_psy_get_current_max(struct ucsi_connector *con,
134                                     union power_supply_propval *val)
135 {
136         u32 pdo;
137
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;
143                 } else {
144                         val->intval = 0;
145                 }
146                 break;
147         case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
148                 val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
149                 break;
150         case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
151                 val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
152                 break;
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 */
156         default:
157                 val->intval = 0;
158                 break;
159         }
160         return 0;
161 }
162
163 static int ucsi_psy_get_current_now(struct ucsi_connector *con,
164                                     union power_supply_propval *val)
165 {
166         u16 flags = con->status.flags;
167
168         if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
169                 val->intval = rdo_op_current(con->rdo) * 1000;
170         else
171                 val->intval = 0;
172         return 0;
173 }
174
175 static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
176                                  union power_supply_propval *val)
177 {
178         u16 flags = con->status.flags;
179
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;
184
185         return 0;
186 }
187
188 static int ucsi_psy_get_prop(struct power_supply *psy,
189                              enum power_supply_property psp,
190                              union power_supply_propval *val)
191 {
192         struct ucsi_connector *con = power_supply_get_drvdata(psy);
193
194         switch (psp) {
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);
211         default:
212                 return -EINVAL;
213         }
214 }
215
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,
220 };
221
222 int ucsi_register_port_psy(struct ucsi_connector *con)
223 {
224         struct power_supply_config psy_cfg = {};
225         struct device *dev = con->ucsi->dev;
226         char *psy_name;
227
228         psy_cfg.drv_data = con;
229         psy_cfg.fwnode = dev_fwnode(dev);
230
231         psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
232                                   dev_name(dev), con->num);
233         if (!psy_name)
234                 return -ENOMEM;
235
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;
243
244         con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
245
246         return PTR_ERR_OR_ZERO(con->psy);
247 }
248
249 void ucsi_unregister_port_psy(struct ucsi_connector *con)
250 {
251         if (IS_ERR_OR_NULL(con->psy))
252                 return;
253
254         power_supply_unregister(con->psy);
255         con->psy = NULL;
256 }
257
258 void ucsi_port_psy_changed(struct ucsi_connector *con)
259 {
260         if (IS_ERR_OR_NULL(con->psy))
261                 return;
262
263         power_supply_changed(con->psy);
264 }