1
2
3
4
5
6
7
8
9
10#include <linux/property.h>
11#include <linux/usb/pd.h>
12
13#include "ucsi.h"
14
15
16enum ucsi_psy_online_states {
17 UCSI_PSY_OFFLINE = 0,
18 UCSI_PSY_FIXED_ONLINE,
19 UCSI_PSY_PROG_ONLINE,
20};
21
22static 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
32static 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
42static 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
65static 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
92static 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
121static 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
144 default:
145 val->intval = 0;
146 break;
147 }
148 return 0;
149}
150
151static 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
163static 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
176static 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
202static 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
208int 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
235void 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
244void 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}
251