]> Git Repo - linux.git/blob - drivers/usb/typec/ucsi/psy.c
zram: support page writeback
[linux.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 <[email protected]>
7  * Author: Heikki Krogerus <[email protected]>
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 };
31
32 static int ucsi_psy_get_online(struct ucsi_connector *con,
33                                union power_supply_propval *val)
34 {
35         val->intval = UCSI_PSY_OFFLINE;
36         if (con->status.flags & UCSI_CONSTAT_CONNECTED &&
37             (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK)
38                 val->intval = UCSI_PSY_FIXED_ONLINE;
39         return 0;
40 }
41
42 static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
43                                     union power_supply_propval *val)
44 {
45         u32 pdo;
46
47         switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
48         case UCSI_CONSTAT_PWR_OPMODE_PD:
49                 pdo = con->src_pdos[0];
50                 val->intval = pdo_fixed_voltage(pdo) * 1000;
51                 break;
52         case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
53         case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
54         case UCSI_CONSTAT_PWR_OPMODE_BC:
55         case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
56                 val->intval = UCSI_TYPEC_VSAFE5V * 1000;
57                 break;
58         default:
59                 val->intval = 0;
60                 break;
61         }
62         return 0;
63 }
64
65 static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
66                                     union power_supply_propval *val)
67 {
68         u32 pdo;
69
70         switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
71         case UCSI_CONSTAT_PWR_OPMODE_PD:
72                 if (con->num_pdos > 0) {
73                         pdo = con->src_pdos[con->num_pdos - 1];
74                         val->intval = pdo_fixed_voltage(pdo) * 1000;
75                 } else {
76                         val->intval = 0;
77                 }
78                 break;
79         case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
80         case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
81         case UCSI_CONSTAT_PWR_OPMODE_BC:
82         case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
83                 val->intval = UCSI_TYPEC_VSAFE5V * 1000;
84                 break;
85         default:
86                 val->intval = 0;
87                 break;
88         }
89         return 0;
90 }
91
92 static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
93                                     union power_supply_propval *val)
94 {
95         int index;
96         u32 pdo;
97
98         switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
99         case UCSI_CONSTAT_PWR_OPMODE_PD:
100                 index = rdo_index(con->rdo);
101                 if (index > 0) {
102                         pdo = con->src_pdos[index - 1];
103                         val->intval = pdo_fixed_voltage(pdo) * 1000;
104                 } else {
105                         val->intval = 0;
106                 }
107                 break;
108         case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
109         case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
110         case UCSI_CONSTAT_PWR_OPMODE_BC:
111         case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
112                 val->intval = UCSI_TYPEC_VSAFE5V * 1000;
113                 break;
114         default:
115                 val->intval = 0;
116                 break;
117         }
118         return 0;
119 }
120
121 static int ucsi_psy_get_current_max(struct ucsi_connector *con,
122                                     union power_supply_propval *val)
123 {
124         u32 pdo;
125
126         switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
127         case UCSI_CONSTAT_PWR_OPMODE_PD:
128                 if (con->num_pdos > 0) {
129                         pdo = con->src_pdos[con->num_pdos - 1];
130                         val->intval = pdo_max_current(pdo) * 1000;
131                 } else {
132                         val->intval = 0;
133                 }
134                 break;
135         case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
136                 val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
137                 break;
138         case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
139                 val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
140                 break;
141         case UCSI_CONSTAT_PWR_OPMODE_BC:
142         case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
143         /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
144         default:
145                 val->intval = 0;
146                 break;
147         }
148         return 0;
149 }
150
151 static int ucsi_psy_get_current_now(struct ucsi_connector *con,
152                                     union power_supply_propval *val)
153 {
154         u16 flags = con->status.flags;
155
156         if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
157                 val->intval = rdo_op_current(con->rdo) * 1000;
158         else
159                 val->intval = 0;
160         return 0;
161 }
162
163 static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
164                                  union power_supply_propval *val)
165 {
166         u16 flags = con->status.flags;
167
168         val->intval = POWER_SUPPLY_USB_TYPE_C;
169         if (flags & UCSI_CONSTAT_CONNECTED &&
170             UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
171                 val->intval = POWER_SUPPLY_USB_TYPE_PD;
172
173         return 0;
174 }
175
176 static int ucsi_psy_get_prop(struct power_supply *psy,
177                              enum power_supply_property psp,
178                              union power_supply_propval *val)
179 {
180         struct ucsi_connector *con = power_supply_get_drvdata(psy);
181
182         switch (psp) {
183         case POWER_SUPPLY_PROP_USB_TYPE:
184                 return ucsi_psy_get_usb_type(con, val);
185         case POWER_SUPPLY_PROP_ONLINE:
186                 return ucsi_psy_get_online(con, val);
187         case POWER_SUPPLY_PROP_VOLTAGE_MIN:
188                 return ucsi_psy_get_voltage_min(con, val);
189         case POWER_SUPPLY_PROP_VOLTAGE_MAX:
190                 return ucsi_psy_get_voltage_max(con, val);
191         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
192                 return ucsi_psy_get_voltage_now(con, val);
193         case POWER_SUPPLY_PROP_CURRENT_MAX:
194                 return ucsi_psy_get_current_max(con, val);
195         case POWER_SUPPLY_PROP_CURRENT_NOW:
196                 return ucsi_psy_get_current_now(con, val);
197         default:
198                 return -EINVAL;
199         }
200 }
201
202 static enum power_supply_usb_type ucsi_psy_usb_types[] = {
203         POWER_SUPPLY_USB_TYPE_C,
204         POWER_SUPPLY_USB_TYPE_PD,
205         POWER_SUPPLY_USB_TYPE_PD_PPS,
206 };
207
208 int ucsi_register_port_psy(struct ucsi_connector *con)
209 {
210         struct power_supply_config psy_cfg = {};
211         struct device *dev = con->ucsi->dev;
212         char *psy_name;
213
214         psy_cfg.drv_data = con;
215         psy_cfg.fwnode = dev_fwnode(dev);
216
217         psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
218                                   dev_name(dev), con->num);
219         if (!psy_name)
220                 return -ENOMEM;
221
222         con->psy_desc.name = psy_name;
223         con->psy_desc.type = POWER_SUPPLY_TYPE_USB,
224         con->psy_desc.usb_types = ucsi_psy_usb_types;
225         con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
226         con->psy_desc.properties = ucsi_psy_props,
227         con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props),
228         con->psy_desc.get_property = ucsi_psy_get_prop;
229
230         con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
231
232         return PTR_ERR_OR_ZERO(con->psy);
233 }
234
235 void ucsi_unregister_port_psy(struct ucsi_connector *con)
236 {
237         if (IS_ERR_OR_NULL(con->psy))
238                 return;
239
240         power_supply_unregister(con->psy);
241         con->psy = NULL;
242 }
243
244 void ucsi_port_psy_changed(struct ucsi_connector *con)
245 {
246         if (IS_ERR_OR_NULL(con->psy))
247                 return;
248
249         power_supply_changed(con->psy);
250 }
This page took 0.049851 seconds and 4 git commands to generate.