blob: e85c988b7034ca579c3006042fbb86a1a66617b4 [file] [log] [blame]
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -07001/*
Rui Miguel Silva2724be02015-11-12 15:36:00 +00002 * Power Supply driver for a Greybus module.
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -07003 *
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00004 * Copyright 2014-2015 Google Inc.
5 * Copyright 2014-2015 Linaro Ltd.
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -07006 *
7 * Released under the GPLv2 only.
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070012#include <linux/power_supply.h>
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000013#include <linux/slab.h>
14
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070015#include "greybus.h"
16
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000017#define PROP_MAX 32
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +080018
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000019struct gb_power_supply_prop {
20 enum power_supply_property prop;
Rui Miguel Silva47becc52016-07-13 14:11:20 +010021 u8 gb_prop;
Rui Miguel Silva6e720c22016-07-13 14:11:19 +010022 int val;
23 int previous_val;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000024 bool is_writeable;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070025};
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070026
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000027struct gb_power_supply {
28 u8 id;
Rui Miguel Silvaff85f722016-01-08 13:53:43 +000029 bool registered;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000030 struct power_supply *psy;
31 struct power_supply_desc desc;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000032 char name[64];
33 struct gb_power_supplies *supplies;
34 struct delayed_work work;
35 char *manufacturer;
36 char *model_name;
37 char *serial_number;
38 u8 type;
39 u8 properties_count;
40 u8 properties_count_str;
41 unsigned long last_update;
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +010042 u8 cache_invalid;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000043 unsigned int update_interval;
44 bool changed;
45 struct gb_power_supply_prop *props;
46 enum power_supply_property *props_raw;
Philip Yang4e013b62016-08-31 11:11:18 +080047 bool pm_acquired;
48 struct mutex supply_lock;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000049};
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080050
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000051struct gb_power_supplies {
52 struct gb_connection *connection;
53 u8 supplies_count;
54 struct gb_power_supply *supply;
55 struct mutex supplies_lock;
56};
57
Rui Miguel Silva722a1332016-09-08 17:17:50 +010058#define to_gb_power_supply(x) power_supply_get_drvdata(x)
59
60/*
61 * General power supply properties that could be absent from various reasons,
62 * like kernel versions or vendor specific versions
63 */
64#ifndef POWER_SUPPLY_PROP_VOLTAGE_BOOT
65 #define POWER_SUPPLY_PROP_VOLTAGE_BOOT -1
66#endif
67#ifndef POWER_SUPPLY_PROP_CURRENT_BOOT
68 #define POWER_SUPPLY_PROP_CURRENT_BOOT -1
69#endif
70#ifndef POWER_SUPPLY_PROP_CALIBRATE
71 #define POWER_SUPPLY_PROP_CALIBRATE -1
72#endif
73
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000074/* cache time in milliseconds, if cache_time is set to 0 cache is disable */
75static unsigned int cache_time = 1000;
76/*
77 * update interval initial and maximum value, between the two will
78 * back-off exponential
79 */
80static unsigned int update_interval_init = 1 * HZ;
81static unsigned int update_interval_max = 30 * HZ;
82
83struct gb_power_supply_changes {
84 enum power_supply_property prop;
85 u32 tolerance_change;
Rui Miguel Silvac4582f92016-08-16 22:31:55 +010086 void (*prop_changed)(struct gb_power_supply *gbpsy,
87 struct gb_power_supply_prop *prop);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000088};
89
Philip Yang4e013b62016-08-31 11:11:18 +080090static void gb_power_supply_state_change(struct gb_power_supply *gbpsy,
91 struct gb_power_supply_prop *prop);
92
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000093static const struct gb_power_supply_changes psy_props_changes[] = {
Rui Miguel Silvac4582f92016-08-16 22:31:55 +010094 { .prop = GB_POWER_SUPPLY_PROP_STATUS,
95 .tolerance_change = 0,
Philip Yang4e013b62016-08-31 11:11:18 +080096 .prop_changed = gb_power_supply_state_change,
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000097 },
Rui Miguel Silvac4582f92016-08-16 22:31:55 +010098 { .prop = GB_POWER_SUPPLY_PROP_TEMP,
99 .tolerance_change = 500,
100 .prop_changed = NULL,
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000101 },
Rui Miguel Silvac4582f92016-08-16 22:31:55 +0100102 { .prop = GB_POWER_SUPPLY_PROP_ONLINE,
103 .tolerance_change = 0,
104 .prop_changed = NULL,
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000105 },
106};
107
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100108static int get_psp_from_gb_prop(int gb_prop, enum power_supply_property *psp)
109{
110 int prop;
111
112 switch (gb_prop) {
113 case GB_POWER_SUPPLY_PROP_STATUS:
114 prop = POWER_SUPPLY_PROP_STATUS;
115 break;
116 case GB_POWER_SUPPLY_PROP_CHARGE_TYPE:
117 prop = POWER_SUPPLY_PROP_CHARGE_TYPE;
118 break;
119 case GB_POWER_SUPPLY_PROP_HEALTH:
120 prop = POWER_SUPPLY_PROP_HEALTH;
121 break;
122 case GB_POWER_SUPPLY_PROP_PRESENT:
123 prop = POWER_SUPPLY_PROP_PRESENT;
124 break;
125 case GB_POWER_SUPPLY_PROP_ONLINE:
126 prop = POWER_SUPPLY_PROP_ONLINE;
127 break;
128 case GB_POWER_SUPPLY_PROP_AUTHENTIC:
129 prop = POWER_SUPPLY_PROP_AUTHENTIC;
130 break;
131 case GB_POWER_SUPPLY_PROP_TECHNOLOGY:
132 prop = POWER_SUPPLY_PROP_TECHNOLOGY;
133 break;
134 case GB_POWER_SUPPLY_PROP_CYCLE_COUNT:
135 prop = POWER_SUPPLY_PROP_CYCLE_COUNT;
136 break;
137 case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX:
138 prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
139 break;
140 case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN:
141 prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
142 break;
143 case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
144 prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
145 break;
146 case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
147 prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
148 break;
149 case GB_POWER_SUPPLY_PROP_VOLTAGE_NOW:
150 prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
151 break;
152 case GB_POWER_SUPPLY_PROP_VOLTAGE_AVG:
153 prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
154 break;
155 case GB_POWER_SUPPLY_PROP_VOLTAGE_OCV:
156 prop = POWER_SUPPLY_PROP_VOLTAGE_OCV;
157 break;
158 case GB_POWER_SUPPLY_PROP_VOLTAGE_BOOT:
159 prop = POWER_SUPPLY_PROP_VOLTAGE_BOOT;
160 break;
161 case GB_POWER_SUPPLY_PROP_CURRENT_MAX:
162 prop = POWER_SUPPLY_PROP_CURRENT_MAX;
163 break;
164 case GB_POWER_SUPPLY_PROP_CURRENT_NOW:
165 prop = POWER_SUPPLY_PROP_CURRENT_NOW;
166 break;
167 case GB_POWER_SUPPLY_PROP_CURRENT_AVG:
168 prop = POWER_SUPPLY_PROP_CURRENT_AVG;
169 break;
170 case GB_POWER_SUPPLY_PROP_CURRENT_BOOT:
171 prop = POWER_SUPPLY_PROP_CURRENT_BOOT;
172 break;
173 case GB_POWER_SUPPLY_PROP_POWER_NOW:
174 prop = POWER_SUPPLY_PROP_POWER_NOW;
175 break;
176 case GB_POWER_SUPPLY_PROP_POWER_AVG:
177 prop = POWER_SUPPLY_PROP_POWER_AVG;
178 break;
179 case GB_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
180 prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
181 break;
182 case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
183 prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
184 break;
185 case GB_POWER_SUPPLY_PROP_CHARGE_FULL:
186 prop = POWER_SUPPLY_PROP_CHARGE_FULL;
187 break;
188 case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY:
189 prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
190 break;
191 case GB_POWER_SUPPLY_PROP_CHARGE_NOW:
192 prop = POWER_SUPPLY_PROP_CHARGE_NOW;
193 break;
194 case GB_POWER_SUPPLY_PROP_CHARGE_AVG:
195 prop = POWER_SUPPLY_PROP_CHARGE_AVG;
196 break;
197 case GB_POWER_SUPPLY_PROP_CHARGE_COUNTER:
198 prop = POWER_SUPPLY_PROP_CHARGE_COUNTER;
199 break;
200 case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
201 prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT;
202 break;
203 case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
204 prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
205 break;
206 case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
207 prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
208 break;
209 case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
210 prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX;
211 break;
212 case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
213 prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
214 break;
215 case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
216 prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX;
217 break;
218 case GB_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
219 prop = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
220 break;
221 case GB_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
222 prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
223 break;
224 case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN:
225 prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
226 break;
227 case GB_POWER_SUPPLY_PROP_ENERGY_FULL:
228 prop = POWER_SUPPLY_PROP_ENERGY_FULL;
229 break;
230 case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY:
231 prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
232 break;
233 case GB_POWER_SUPPLY_PROP_ENERGY_NOW:
234 prop = POWER_SUPPLY_PROP_ENERGY_NOW;
235 break;
236 case GB_POWER_SUPPLY_PROP_ENERGY_AVG:
237 prop = POWER_SUPPLY_PROP_ENERGY_AVG;
238 break;
239 case GB_POWER_SUPPLY_PROP_CAPACITY:
240 prop = POWER_SUPPLY_PROP_CAPACITY;
241 break;
242 case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
243 prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN;
244 break;
245 case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX:
246 prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX;
247 break;
248 case GB_POWER_SUPPLY_PROP_CAPACITY_LEVEL:
249 prop = POWER_SUPPLY_PROP_CAPACITY_LEVEL;
250 break;
251 case GB_POWER_SUPPLY_PROP_TEMP:
252 prop = POWER_SUPPLY_PROP_TEMP;
253 break;
254 case GB_POWER_SUPPLY_PROP_TEMP_MAX:
255 prop = POWER_SUPPLY_PROP_TEMP_MAX;
256 break;
257 case GB_POWER_SUPPLY_PROP_TEMP_MIN:
258 prop = POWER_SUPPLY_PROP_TEMP_MIN;
259 break;
260 case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
261 prop = POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
262 break;
263 case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
264 prop = POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
265 break;
266 case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT:
267 prop = POWER_SUPPLY_PROP_TEMP_AMBIENT;
268 break;
269 case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
270 prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
271 break;
272 case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
273 prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
274 break;
275 case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
276 prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW;
277 break;
278 case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
279 prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG;
280 break;
281 case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
282 prop = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW;
283 break;
284 case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
285 prop = POWER_SUPPLY_PROP_TIME_TO_FULL_AVG;
286 break;
287 case GB_POWER_SUPPLY_PROP_TYPE:
288 prop = POWER_SUPPLY_PROP_TYPE;
289 break;
290 case GB_POWER_SUPPLY_PROP_SCOPE:
291 prop = POWER_SUPPLY_PROP_SCOPE;
292 break;
293 case GB_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
294 prop = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT;
295 break;
296 case GB_POWER_SUPPLY_PROP_CALIBRATE:
297 prop = POWER_SUPPLY_PROP_CALIBRATE;
298 break;
299 default:
300 prop = -1;
301 break;
302 }
303
304 if (prop < 0)
305 return prop;
306
307 *psp = (enum power_supply_property)prop;
308
309 return 0;
310}
311
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000312static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy)
313{
314 return gbpsy->supplies->connection;
315}
316
317static struct gb_power_supply_prop *get_psy_prop(struct gb_power_supply *gbpsy,
318 enum power_supply_property psp)
319{
320 int i;
321
322 for (i = 0; i < gbpsy->properties_count; i++)
323 if (gbpsy->props[i].prop == psp)
324 return &gbpsy->props[i];
325 return NULL;
326}
327
328static int is_psy_prop_writeable(struct gb_power_supply *gbpsy,
329 enum power_supply_property psp)
330{
331 struct gb_power_supply_prop *prop;
332
333 prop = get_psy_prop(gbpsy, psp);
334 if (!prop)
335 return -ENOENT;
336 return prop->is_writeable ? 1 : 0;
337}
338
339static int is_prop_valint(enum power_supply_property psp)
340{
341 return ((psp < POWER_SUPPLY_PROP_MODEL_NAME) ? 1 : 0);
342}
343
344static void next_interval(struct gb_power_supply *gbpsy)
345{
346 if (gbpsy->update_interval == update_interval_max)
347 return;
348
349 /* do some exponential back-off in the update interval */
350 gbpsy->update_interval *= 2;
351 if (gbpsy->update_interval > update_interval_max)
352 gbpsy->update_interval = update_interval_max;
353}
354
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000355static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
356{
357 power_supply_changed(gbpsy->psy);
358}
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000359
Philip Yang4e013b62016-08-31 11:11:18 +0800360static void gb_power_supply_state_change(struct gb_power_supply *gbpsy,
361 struct gb_power_supply_prop *prop)
362{
363 struct gb_connection *connection = get_conn_from_psy(gbpsy);
364 int ret;
365
366 /*
367 * Check gbpsy->pm_acquired to make sure only one pair of 'get_sync'
368 * and 'put_autosuspend' runtime pm call for state property change.
369 */
370 mutex_lock(&gbpsy->supply_lock);
371
372 if ((prop->val == GB_POWER_SUPPLY_STATUS_CHARGING) &&
373 !gbpsy->pm_acquired) {
374 ret = gb_pm_runtime_get_sync(connection->bundle);
375 if (ret)
376 dev_err(&connection->bundle->dev,
377 "Fail to set wake lock for charging state\n");
378 else
379 gbpsy->pm_acquired = true;
380 } else {
381 if (gbpsy->pm_acquired) {
382 ret = gb_pm_runtime_put_autosuspend(connection->bundle);
383 if (ret)
384 dev_err(&connection->bundle->dev,
385 "Fail to set wake unlock for none charging\n");
386 else
387 gbpsy->pm_acquired = false;
388 }
389 }
390
391 mutex_unlock(&gbpsy->supply_lock);
392}
393
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000394static void check_changed(struct gb_power_supply *gbpsy,
395 struct gb_power_supply_prop *prop)
396{
397 const struct gb_power_supply_changes *psyc;
Rui Miguel Silva6e720c22016-07-13 14:11:19 +0100398 int val = prop->val;
399 int prev_val = prop->previous_val;
Rui Miguel Silvac4582f92016-08-16 22:31:55 +0100400 bool changed = false;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000401 int i;
402
403 for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) {
404 psyc = &psy_props_changes[i];
405 if (prop->prop == psyc->prop) {
406 if (!psyc->tolerance_change)
Rui Miguel Silvac4582f92016-08-16 22:31:55 +0100407 changed = true;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000408 else if (val < prev_val &&
409 prev_val - val > psyc->tolerance_change)
Rui Miguel Silvac4582f92016-08-16 22:31:55 +0100410 changed = true;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000411 else if (val > prev_val &&
412 val - prev_val > psyc->tolerance_change)
Rui Miguel Silvac4582f92016-08-16 22:31:55 +0100413 changed = true;
414
415 if (changed && psyc->prop_changed)
416 psyc->prop_changed(gbpsy, prop);
417
418 if (changed)
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000419 gbpsy->changed = true;
420 break;
421 }
422 }
423}
424
425static int total_props(struct gb_power_supply *gbpsy)
426{
427 /* this return the intval plus the strval properties */
428 return (gbpsy->properties_count + gbpsy->properties_count_str);
429}
430
431static void prop_append(struct gb_power_supply *gbpsy,
432 enum power_supply_property prop)
433{
434 enum power_supply_property *new_props_raw;
435
436 gbpsy->properties_count_str++;
437 new_props_raw = krealloc(gbpsy->props_raw, total_props(gbpsy) *
438 sizeof(enum power_supply_property),
439 GFP_KERNEL);
440 if (!new_props_raw)
441 return;
442 gbpsy->props_raw = new_props_raw;
443 gbpsy->props_raw[total_props(gbpsy) - 1] = prop;
444}
445
446static int __gb_power_supply_set_name(char *init_name, char *name, size_t len)
447{
448 unsigned int i = 0;
449 int ret = 0;
450 struct power_supply *psy;
451
452 if (!strlen(init_name))
453 init_name = "gb_power_supply";
454 strlcpy(name, init_name, len);
455
456 while ((ret < len) && (psy = power_supply_get_by_name(name))) {
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000457 power_supply_put(psy);
Rui Miguel Silva722a1332016-09-08 17:17:50 +0100458
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000459 ret = snprintf(name, len, "%s_%u", init_name, ++i);
460 }
461 if (ret >= len)
462 return -ENOMEM;
463 return i;
464}
465
466static void _gb_power_supply_append_props(struct gb_power_supply *gbpsy)
467{
468 if (strlen(gbpsy->manufacturer))
469 prop_append(gbpsy, POWER_SUPPLY_PROP_MANUFACTURER);
470 if (strlen(gbpsy->model_name))
471 prop_append(gbpsy, POWER_SUPPLY_PROP_MODEL_NAME);
472 if (strlen(gbpsy->serial_number))
473 prop_append(gbpsy, POWER_SUPPLY_PROP_SERIAL_NUMBER);
474}
475
476static int gb_power_supply_description_get(struct gb_power_supply *gbpsy)
477{
478 struct gb_connection *connection = get_conn_from_psy(gbpsy);
479 struct gb_power_supply_get_description_request req;
480 struct gb_power_supply_get_description_response resp;
481 int ret;
482
483 req.psy_id = gbpsy->id;
484
485 ret = gb_operation_sync(connection,
486 GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION,
487 &req, sizeof(req), &resp, sizeof(resp));
488 if (ret < 0)
489 return ret;
490
491 gbpsy->manufacturer = kstrndup(resp.manufacturer, PROP_MAX, GFP_KERNEL);
492 if (!gbpsy->manufacturer)
493 return -ENOMEM;
494 gbpsy->model_name = kstrndup(resp.model, PROP_MAX, GFP_KERNEL);
495 if (!gbpsy->model_name)
496 return -ENOMEM;
497 gbpsy->serial_number = kstrndup(resp.serial_number, PROP_MAX,
498 GFP_KERNEL);
499 if (!gbpsy->serial_number)
500 return -ENOMEM;
501
502 gbpsy->type = le16_to_cpu(resp.type);
503 gbpsy->properties_count = resp.properties_count;
504
505 return 0;
506}
507
508static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy)
509{
510 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000511 struct gb_power_supply_get_property_descriptors_request *req;
512 struct gb_power_supply_get_property_descriptors_response *resp;
513 struct gb_operation *op;
514 u8 props_count = gbpsy->properties_count;
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100515 enum power_supply_property psp;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000516 int ret;
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100517 int i, r = 0;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000518
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000519 if (props_count == 0)
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000520 return 0;
521
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000522 op = gb_operation_create(connection,
523 GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS,
524 sizeof(req), sizeof(*resp) + props_count *
525 sizeof(struct gb_power_supply_props_desc),
526 GFP_KERNEL);
527 if (!op)
528 return -ENOMEM;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000529
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000530 req = op->request->payload;
531 req->psy_id = gbpsy->id;
532
533 ret = gb_operation_request_send_sync(op);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000534 if (ret < 0)
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000535 goto out_put_operation;
536
537 resp = op->response->payload;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000538
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100539 /* validate received properties */
540 for (i = 0; i < props_count; i++) {
541 ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
542 if (ret < 0) {
543 dev_warn(&connection->bundle->dev,
544 "greybus property %u it is not supported by this kernel, dropped\n",
545 resp->props[i].property);
546 gbpsy->properties_count--;
547 }
548 }
549
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000550 gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props),
551 GFP_KERNEL);
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000552 if (!gbpsy->props) {
553 ret = -ENOMEM;
554 goto out_put_operation;
555 }
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000556
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000557 gbpsy->props_raw = kcalloc(gbpsy->properties_count,
558 sizeof(*gbpsy->props_raw), GFP_KERNEL);
559 if (!gbpsy->props_raw) {
560 ret = -ENOMEM;
561 goto out_put_operation;
562 }
563
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100564 /* Store available properties, skip the ones we do not support */
565 for (i = 0; i < props_count; i++) {
566 ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
567 if (ret < 0) {
568 r++;
569 continue;
570 }
571 gbpsy->props[i - r].prop = psp;
572 gbpsy->props[i - r].gb_prop = resp->props[i].property;
573 gbpsy->props_raw[i - r] = psp;
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000574 if (resp->props[i].is_writeable)
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100575 gbpsy->props[i - r].is_writeable = true;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000576 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800577
578 /*
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000579 * now append the properties that we already got information in the
580 * get_description operation. (char * ones)
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800581 */
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000582 _gb_power_supply_append_props(gbpsy);
583
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100584 ret = 0;
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000585out_put_operation:
586 gb_operation_put(op);
587
588 return ret;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800589}
590
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000591static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy,
592 enum power_supply_property psp)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700593{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000594 struct gb_connection *connection = get_conn_from_psy(gbpsy);
595 struct gb_power_supply_prop *prop;
596 struct gb_power_supply_get_property_request req;
597 struct gb_power_supply_get_property_response resp;
Rui Miguel Silva6e720c22016-07-13 14:11:19 +0100598 int val;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000599 int ret;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800600
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000601 prop = get_psy_prop(gbpsy, psp);
602 if (!prop)
603 return -EINVAL;
604 req.psy_id = gbpsy->id;
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100605 req.property = prop->gb_prop;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000606
607 ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY,
608 &req, sizeof(req), &resp, sizeof(resp));
609 if (ret < 0)
610 return ret;
611
612 val = le32_to_cpu(resp.prop_val);
613 if (val == prop->val)
614 return 0;
615
616 prop->previous_val = prop->val;
617 prop->val = val;
618
619 check_changed(gbpsy, prop);
620
621 return 0;
622}
623
624static int __gb_power_supply_property_get(struct gb_power_supply *gbpsy,
625 enum power_supply_property psp,
626 union power_supply_propval *val)
627{
628 struct gb_power_supply_prop *prop;
629
630 prop = get_psy_prop(gbpsy, psp);
631 if (!prop)
632 return -EINVAL;
633
634 val->intval = prop->val;
635 return 0;
636}
637
638static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy,
639 enum power_supply_property psp,
640 union power_supply_propval *val)
641{
642 switch (psp) {
643 case POWER_SUPPLY_PROP_MODEL_NAME:
Rui Miguel Silvaf921fb12016-01-08 13:53:46 +0000644 val->strval = gbpsy->model_name;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000645 break;
646 case POWER_SUPPLY_PROP_MANUFACTURER:
Rui Miguel Silvaf921fb12016-01-08 13:53:46 +0000647 val->strval = gbpsy->manufacturer;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000648 break;
649 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
Rui Miguel Silvaf921fb12016-01-08 13:53:46 +0000650 val->strval = gbpsy->serial_number;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000651 break;
652 default:
653 break;
654 }
655
656 return 0;
657}
658
659static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy,
660 enum power_supply_property psp,
661 union power_supply_propval *val)
662{
663 struct gb_connection *connection = get_conn_from_psy(gbpsy);
664 int ret;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800665
666 /*
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000667 * Properties of type const char *, were already fetched on
668 * get_description operation and should be cached in gb
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800669 */
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000670 if (is_prop_valint(psp))
671 ret = __gb_power_supply_property_get(gbpsy, psp, val);
672 else
673 ret = __gb_power_supply_property_strval_get(gbpsy, psp, val);
674
675 if (ret < 0)
676 dev_err(&connection->bundle->dev, "get property %u\n", psp);
677
678 return 0;
679}
680
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +0100681static int is_cache_valid(struct gb_power_supply *gbpsy)
682{
683 /* check if cache is good enough or it has expired */
684 if (gbpsy->cache_invalid) {
685 gbpsy->cache_invalid = 0;
686 return 0;
687 }
688
689 if (gbpsy->last_update &&
690 time_is_after_jiffies(gbpsy->last_update +
691 msecs_to_jiffies(cache_time)))
692 return 1;
693
694 return 0;
695}
696
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000697static int gb_power_supply_status_get(struct gb_power_supply *gbpsy)
698{
Philip Yang4e013b62016-08-31 11:11:18 +0800699 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000700 int ret = 0;
701 int i;
702
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +0100703 if (is_cache_valid(gbpsy))
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000704 return 0;
705
Philip Yang4e013b62016-08-31 11:11:18 +0800706 ret = gb_pm_runtime_get_sync(connection->bundle);
707 if (ret)
708 return ret;
709
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000710 for (i = 0; i < gbpsy->properties_count; i++) {
711 ret = __gb_power_supply_property_update(gbpsy,
712 gbpsy->props[i].prop);
713 if (ret < 0)
714 break;
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800715 }
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000716
717 if (ret == 0)
718 gbpsy->last_update = jiffies;
719
Philip Yang4e013b62016-08-31 11:11:18 +0800720 gb_pm_runtime_put_autosuspend(connection->bundle);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000721 return ret;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700722}
723
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000724static void gb_power_supply_status_update(struct gb_power_supply *gbpsy)
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800725{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000726 /* check if there a change that need to be reported */
727 gb_power_supply_status_get(gbpsy);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800728
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000729 if (!gbpsy->changed)
730 return;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800731
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000732 gbpsy->update_interval = update_interval_init;
733 __gb_power_supply_changed(gbpsy);
734 gbpsy->changed = false;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800735}
736
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000737static void gb_power_supply_work(struct work_struct *work)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700738{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000739 struct gb_power_supply *gbpsy = container_of(work,
740 struct gb_power_supply,
741 work.work);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800742
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000743 /*
744 * if the poll interval is not set, disable polling, this is helpful
745 * specially at unregister time.
746 */
747 if (!gbpsy->update_interval)
748 return;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800749
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000750 gb_power_supply_status_update(gbpsy);
751 next_interval(gbpsy);
752 schedule_delayed_work(&gbpsy->work, gbpsy->update_interval);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700753}
754
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700755static int get_property(struct power_supply *b,
756 enum power_supply_property psp,
757 union power_supply_propval *val)
758{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000759 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700760
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000761 gb_power_supply_status_get(gbpsy);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700762
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000763 return _gb_power_supply_property_get(gbpsy, psp, val);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700764}
765
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000766static int gb_power_supply_property_set(struct gb_power_supply *gbpsy,
767 enum power_supply_property psp,
768 int val)
769{
770 struct gb_connection *connection = get_conn_from_psy(gbpsy);
771 struct gb_power_supply_prop *prop;
772 struct gb_power_supply_set_property_request req;
773 int ret;
774
Philip Yang4e013b62016-08-31 11:11:18 +0800775 ret = gb_pm_runtime_get_sync(connection->bundle);
776 if (ret)
777 return ret;
778
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000779 prop = get_psy_prop(gbpsy, psp);
Philip Yang4e013b62016-08-31 11:11:18 +0800780 if (!prop) {
781 ret = -EINVAL;
782 goto out;
783 }
784
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000785 req.psy_id = gbpsy->id;
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100786 req.property = prop->gb_prop;
Rui Miguel Silva6e720c22016-07-13 14:11:19 +0100787 req.prop_val = cpu_to_le32((s32)val);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000788
789 ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY,
790 &req, sizeof(req), NULL, 0);
791 if (ret < 0)
792 goto out;
793
794 /* cache immediately the new value */
795 prop->val = val;
796
797out:
Philip Yang4e013b62016-08-31 11:11:18 +0800798 gb_pm_runtime_put_autosuspend(connection->bundle);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000799 return ret;
800}
801
802static int set_property(struct power_supply *b,
803 enum power_supply_property psp,
804 const union power_supply_propval *val)
805{
806 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
807
808 return gb_power_supply_property_set(gbpsy, psp, val->intval);
809}
810
811static int property_is_writeable(struct power_supply *b,
812 enum power_supply_property psp)
813{
814 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
815
816 return is_psy_prop_writeable(gbpsy, psp);
817}
818
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000819static int gb_power_supply_register(struct gb_power_supply *gbpsy)
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200820{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000821 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200822 struct power_supply_config cfg = {};
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200823
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000824 cfg.drv_data = gbpsy;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200825
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000826 gbpsy->desc.name = gbpsy->name;
827 gbpsy->desc.type = gbpsy->type;
828 gbpsy->desc.properties = gbpsy->props_raw;
829 gbpsy->desc.num_properties = total_props(gbpsy);
830 gbpsy->desc.get_property = get_property;
831 gbpsy->desc.set_property = set_property;
832 gbpsy->desc.property_is_writeable = property_is_writeable;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200833
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000834 gbpsy->psy = power_supply_register(&connection->bundle->dev,
835 &gbpsy->desc, &cfg);
Alex Elder95073cc2016-05-24 13:34:51 -0500836 return PTR_ERR_OR_ZERO(gbpsy->psy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200837}
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200838
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000839static void _gb_power_supply_free(struct gb_power_supply *gbpsy)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700840{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000841 kfree(gbpsy->serial_number);
842 kfree(gbpsy->model_name);
843 kfree(gbpsy->manufacturer);
844 kfree(gbpsy->props_raw);
845 kfree(gbpsy->props);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000846}
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700847
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000848static void _gb_power_supply_release(struct gb_power_supply *gbpsy)
849{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000850 gbpsy->update_interval = 0;
851
852 cancel_delayed_work_sync(&gbpsy->work);
Rui Miguel Silva722a1332016-09-08 17:17:50 +0100853
Rui Miguel Silvaff85f722016-01-08 13:53:43 +0000854 if (gbpsy->registered)
855 power_supply_unregister(gbpsy->psy);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000856
857 _gb_power_supply_free(gbpsy);
858}
859
860static void _gb_power_supplies_release(struct gb_power_supplies *supplies)
861{
862 int i;
863
Rui Miguel Silva7ccac20d2016-01-08 13:53:42 +0000864 if (!supplies->supply)
865 return;
866
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000867 mutex_lock(&supplies->supplies_lock);
868 for (i = 0; i < supplies->supplies_count; i++)
869 _gb_power_supply_release(&supplies->supply[i]);
Rui Miguel Silvaaccad1b2016-01-08 13:53:47 +0000870 kfree(supplies->supply);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000871 mutex_unlock(&supplies->supplies_lock);
Rui Miguel Silva23f25ba2016-01-08 13:53:44 +0000872 kfree(supplies);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000873}
874
875static int gb_power_supplies_get_count(struct gb_power_supplies *supplies)
876{
877 struct gb_power_supply_get_supplies_response resp;
878 int ret;
879
880 ret = gb_operation_sync(supplies->connection,
881 GB_POWER_SUPPLY_TYPE_GET_SUPPLIES,
882 NULL, 0, &resp, sizeof(resp));
883 if (ret < 0)
884 return ret;
885
886 if (!resp.supplies_count)
887 return -EINVAL;
888
889 supplies->supplies_count = resp.supplies_count;
890
891 return ret;
892}
893
894static int gb_power_supply_config(struct gb_power_supplies *supplies, int id)
895{
896 struct gb_power_supply *gbpsy = &supplies->supply[id];
897 int ret;
898
899 gbpsy->supplies = supplies;
900 gbpsy->id = id;
901
902 ret = gb_power_supply_description_get(gbpsy);
903 if (ret < 0)
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530904 return ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000905
Rui Miguel Silva6ac91662016-08-26 11:52:05 +0100906 return gb_power_supply_prop_descriptors_get(gbpsy);
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530907}
908
909static int gb_power_supply_enable(struct gb_power_supply *gbpsy)
910{
911 int ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000912
Rui Miguel Silva6ac91662016-08-26 11:52:05 +0100913 /* guarantee that we have an unique name, before register */
914 ret = __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name,
915 sizeof(gbpsy->name));
916 if (ret < 0)
917 return ret;
918
Philip Yang4e013b62016-08-31 11:11:18 +0800919 mutex_init(&gbpsy->supply_lock);
920
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000921 ret = gb_power_supply_register(gbpsy);
922 if (ret < 0)
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530923 return ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000924
925 gbpsy->update_interval = update_interval_init;
926 INIT_DELAYED_WORK(&gbpsy->work, gb_power_supply_work);
927 schedule_delayed_work(&gbpsy->work, 0);
928
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530929 /* everything went fine, mark it for release code to know */
930 gbpsy->registered = true;
931
932 return 0;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000933}
934
935static int gb_power_supplies_setup(struct gb_power_supplies *supplies)
936{
937 struct gb_connection *connection = supplies->connection;
938 int ret;
939 int i;
940
941 mutex_lock(&supplies->supplies_lock);
942
943 ret = gb_power_supplies_get_count(supplies);
944 if (ret < 0)
945 goto out;
946
947 supplies->supply = kzalloc(supplies->supplies_count *
948 sizeof(struct gb_power_supply),
949 GFP_KERNEL);
950
Johan Hovolde0d91ff2016-01-07 12:28:29 +0100951 if (!supplies->supply) {
952 ret = -ENOMEM;
953 goto out;
954 }
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700955
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000956 for (i = 0; i < supplies->supplies_count; i++) {
957 ret = gb_power_supply_config(supplies, i);
958 if (ret < 0) {
959 dev_err(&connection->bundle->dev,
960 "Fail to configure supplies devices\n");
961 goto out;
962 }
963 }
964out:
965 mutex_unlock(&supplies->supplies_lock);
966 return ret;
967}
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +0800968
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530969static int gb_power_supplies_register(struct gb_power_supplies *supplies)
970{
971 struct gb_connection *connection = supplies->connection;
972 int ret = 0;
973 int i;
974
975 mutex_lock(&supplies->supplies_lock);
976
977 for (i = 0; i < supplies->supplies_count; i++) {
978 ret = gb_power_supply_enable(&supplies->supply[i]);
979 if (ret < 0) {
980 dev_err(&connection->bundle->dev,
981 "Fail to enable supplies devices\n");
982 break;
983 }
984 }
985
986 mutex_unlock(&supplies->supplies_lock);
987 return ret;
988}
989
Viresh Kumar68b13092016-02-12 16:08:33 +0530990static int gb_supplies_request_handler(struct gb_operation *op)
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000991{
992 struct gb_connection *connection = op->connection;
Greg Kroah-Hartman0ec30632016-03-22 14:30:35 -0400993 struct gb_power_supplies *supplies = gb_connection_get_data(connection);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000994 struct gb_power_supply *gbpsy;
995 struct gb_message *request;
996 struct gb_power_supply_event_request *payload;
997 u8 psy_id;
998 u8 event;
999 int ret = 0;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +08001000
Viresh Kumar68b13092016-02-12 16:08:33 +05301001 if (op->type != GB_POWER_SUPPLY_TYPE_EVENT) {
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001002 dev_err(&connection->bundle->dev,
Viresh Kumar68b13092016-02-12 16:08:33 +05301003 "Unsupported unsolicited event: %u\n", op->type);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001004 return -EINVAL;
1005 }
1006
1007 request = op->request;
1008
1009 if (request->payload_size < sizeof(*payload)) {
1010 dev_err(&connection->bundle->dev,
1011 "Wrong event size received (%zu < %zu)\n",
1012 request->payload_size, sizeof(*payload));
1013 return -EINVAL;
1014 }
1015
1016 payload = request->payload;
1017 psy_id = payload->psy_id;
1018 mutex_lock(&supplies->supplies_lock);
Rui Miguel Silvaadb57cf2016-01-12 14:38:21 +00001019 if (psy_id >= supplies->supplies_count ||
1020 !supplies->supply[psy_id].registered) {
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001021 dev_err(&connection->bundle->dev,
1022 "Event received for unconfigured power_supply id: %d\n",
1023 psy_id);
1024 ret = -EINVAL;
1025 goto out_unlock;
1026 }
1027
1028 event = payload->event;
1029 /*
1030 * we will only handle events after setup is done and before release is
1031 * running. For that just check update_interval.
1032 */
1033 gbpsy = &supplies->supply[psy_id];
Rui Miguel Silva5f66d622016-08-16 22:31:56 +01001034 if (!gbpsy->update_interval) {
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001035 ret = -ESHUTDOWN;
1036 goto out_unlock;
1037 }
1038
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +01001039 if (event & GB_POWER_SUPPLY_UPDATE) {
1040 /*
1041 * we need to make sure we invalidate cache, if not no new
1042 * values for the properties will be fetch and the all propose
1043 * of this event is missed
1044 */
1045 gbpsy->cache_invalid = 1;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001046 gb_power_supply_status_update(gbpsy);
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +01001047 }
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001048
1049out_unlock:
1050 mutex_unlock(&supplies->supplies_lock);
1051 return ret;
1052}
1053
Viresh Kumar68b13092016-02-12 16:08:33 +05301054static int gb_power_supply_probe(struct gb_bundle *bundle,
1055 const struct greybus_bundle_id *id)
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001056{
Viresh Kumar68b13092016-02-12 16:08:33 +05301057 struct greybus_descriptor_cport *cport_desc;
1058 struct gb_connection *connection;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001059 struct gb_power_supplies *supplies;
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +00001060 int ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001061
Viresh Kumar68b13092016-02-12 16:08:33 +05301062 if (bundle->num_cports != 1)
1063 return -ENODEV;
1064
1065 cport_desc = &bundle->cport_desc[0];
1066 if (cport_desc->protocol_id != GREYBUS_PROTOCOL_POWER_SUPPLY)
1067 return -ENODEV;
1068
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001069 supplies = kzalloc(sizeof(*supplies), GFP_KERNEL);
1070 if (!supplies)
1071 return -ENOMEM;
1072
Viresh Kumar68b13092016-02-12 16:08:33 +05301073 connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
1074 gb_supplies_request_handler);
1075 if (IS_ERR(connection)) {
1076 ret = PTR_ERR(connection);
1077 goto out;
1078 }
1079
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001080 supplies->connection = connection;
Greg Kroah-Hartman0ec30632016-03-22 14:30:35 -04001081 gb_connection_set_data(connection, supplies);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001082
1083 mutex_init(&supplies->supplies_lock);
1084
Viresh Kumar68b13092016-02-12 16:08:33 +05301085 greybus_set_drvdata(bundle, supplies);
1086
1087 /* We aren't ready to receive an incoming request yet */
1088 ret = gb_connection_enable_tx(connection);
1089 if (ret)
1090 goto error_connection_destroy;
1091
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +00001092 ret = gb_power_supplies_setup(supplies);
1093 if (ret < 0)
Viresh Kumar68b13092016-02-12 16:08:33 +05301094 goto error_connection_disable;
1095
1096 /* We are ready to receive an incoming request now, enable RX as well */
1097 ret = gb_connection_enable(connection);
1098 if (ret)
1099 goto error_connection_disable;
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +00001100
Viresh Kumar7e9fba82016-02-12 16:08:32 +05301101 ret = gb_power_supplies_register(supplies);
1102 if (ret < 0)
Viresh Kumar68b13092016-02-12 16:08:33 +05301103 goto error_connection_disable;
Viresh Kumar7e9fba82016-02-12 16:08:32 +05301104
Philip Yang4e013b62016-08-31 11:11:18 +08001105 gb_pm_runtime_put_autosuspend(bundle);
Viresh Kumar7e9fba82016-02-12 16:08:32 +05301106 return 0;
1107
Viresh Kumar68b13092016-02-12 16:08:33 +05301108error_connection_disable:
1109 gb_connection_disable(connection);
1110error_connection_destroy:
1111 gb_connection_destroy(connection);
Viresh Kumar7e9fba82016-02-12 16:08:32 +05301112out:
1113 _gb_power_supplies_release(supplies);
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +00001114 return ret;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -07001115}
1116
Viresh Kumar68b13092016-02-12 16:08:33 +05301117static void gb_power_supply_disconnect(struct gb_bundle *bundle)
Alex Elder697e55d2014-10-20 23:01:04 -05001118{
Viresh Kumar68b13092016-02-12 16:08:33 +05301119 struct gb_power_supplies *supplies = greybus_get_drvdata(bundle);
1120
1121 gb_connection_disable(supplies->connection);
1122 gb_connection_destroy(supplies->connection);
Alex Elder697e55d2014-10-20 23:01:04 -05001123
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001124 _gb_power_supplies_release(supplies);
Alex Elder697e55d2014-10-20 23:01:04 -05001125}
1126
Viresh Kumar68b13092016-02-12 16:08:33 +05301127static const struct greybus_bundle_id gb_power_supply_id_table[] = {
1128 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) },
1129 { }
Alex Elder19d03de2014-11-05 16:12:53 -06001130};
Viresh Kumar68b13092016-02-12 16:08:33 +05301131MODULE_DEVICE_TABLE(greybus, gb_power_supply_id_table);
Alex Elder19d03de2014-11-05 16:12:53 -06001132
Viresh Kumar68b13092016-02-12 16:08:33 +05301133static struct greybus_driver gb_power_supply_driver = {
1134 .name = "power_supply",
1135 .probe = gb_power_supply_probe,
1136 .disconnect = gb_power_supply_disconnect,
1137 .id_table = gb_power_supply_id_table,
1138};
1139module_greybus_driver(gb_power_supply_driver);
Alex Elder19d03de2014-11-05 16:12:53 -06001140
Greg Kroah-Hartman7dd26262014-12-24 13:01:42 -08001141MODULE_LICENSE("GPL v2");