blob: 1ce57f6333dad44f51a57e2d20bcb37dea3135ba [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;
21 u32 val;
22 u32 previous_val;
23 bool is_writeable;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070024};
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070025
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000026struct gb_power_supply {
27 u8 id;
28#ifdef DRIVER_OWNS_PSY_STRUCT
29 struct power_supply psy;
30#define to_gb_power_supply(x) container_of(x, struct gb_power_supply, psy)
31#else
32 struct power_supply *psy;
33 struct power_supply_desc desc;
34#define to_gb_power_supply(x) power_supply_get_drvdata(x)
35#endif
36 char name[64];
37 struct gb_power_supplies *supplies;
38 struct delayed_work work;
39 char *manufacturer;
40 char *model_name;
41 char *serial_number;
42 u8 type;
43 u8 properties_count;
44 u8 properties_count_str;
45 unsigned long last_update;
46 unsigned int update_interval;
47 bool changed;
48 struct gb_power_supply_prop *props;
49 enum power_supply_property *props_raw;
50};
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080051
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000052struct gb_power_supplies {
53 struct gb_connection *connection;
54 u8 supplies_count;
55 struct gb_power_supply *supply;
56 struct mutex supplies_lock;
57};
58
59/* cache time in milliseconds, if cache_time is set to 0 cache is disable */
60static unsigned int cache_time = 1000;
61/*
62 * update interval initial and maximum value, between the two will
63 * back-off exponential
64 */
65static unsigned int update_interval_init = 1 * HZ;
66static unsigned int update_interval_max = 30 * HZ;
67
68struct gb_power_supply_changes {
69 enum power_supply_property prop;
70 u32 tolerance_change;
71};
72
73static const struct gb_power_supply_changes psy_props_changes[] = {
74 { .prop = GB_POWER_SUPPLY_PROP_STATUS,
75 .tolerance_change = 0,
76 },
77 { .prop = GB_POWER_SUPPLY_PROP_TEMP,
78 .tolerance_change = 500,
79 },
80 { .prop = GB_POWER_SUPPLY_PROP_ONLINE,
81 .tolerance_change = 0,
82 },
83};
84
85static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy)
86{
87 return gbpsy->supplies->connection;
88}
89
90static struct gb_power_supply_prop *get_psy_prop(struct gb_power_supply *gbpsy,
91 enum power_supply_property psp)
92{
93 int i;
94
95 for (i = 0; i < gbpsy->properties_count; i++)
96 if (gbpsy->props[i].prop == psp)
97 return &gbpsy->props[i];
98 return NULL;
99}
100
101static int is_psy_prop_writeable(struct gb_power_supply *gbpsy,
102 enum power_supply_property psp)
103{
104 struct gb_power_supply_prop *prop;
105
106 prop = get_psy_prop(gbpsy, psp);
107 if (!prop)
108 return -ENOENT;
109 return prop->is_writeable ? 1 : 0;
110}
111
112static int is_prop_valint(enum power_supply_property psp)
113{
114 return ((psp < POWER_SUPPLY_PROP_MODEL_NAME) ? 1 : 0);
115}
116
117static void next_interval(struct gb_power_supply *gbpsy)
118{
119 if (gbpsy->update_interval == update_interval_max)
120 return;
121
122 /* do some exponential back-off in the update interval */
123 gbpsy->update_interval *= 2;
124 if (gbpsy->update_interval > update_interval_max)
125 gbpsy->update_interval = update_interval_max;
126}
127
128#ifdef DRIVER_OWNS_PSY_STRUCT
129static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
130{
131 power_supply_changed(&gbpsy->psy);
132}
133#else
134static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
135{
136 power_supply_changed(gbpsy->psy);
137}
138#endif
139
140static void check_changed(struct gb_power_supply *gbpsy,
141 struct gb_power_supply_prop *prop)
142{
143 const struct gb_power_supply_changes *psyc;
144 u32 val = prop->val;
145 u32 prev_val = prop->previous_val;
146 int i;
147
148 for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) {
149 psyc = &psy_props_changes[i];
150 if (prop->prop == psyc->prop) {
151 if (!psyc->tolerance_change)
152 gbpsy->changed = true;
153 else if (val < prev_val &&
154 prev_val - val > psyc->tolerance_change)
155 gbpsy->changed = true;
156 else if (val > prev_val &&
157 val - prev_val > psyc->tolerance_change)
158 gbpsy->changed = true;
159 break;
160 }
161 }
162}
163
164static int total_props(struct gb_power_supply *gbpsy)
165{
166 /* this return the intval plus the strval properties */
167 return (gbpsy->properties_count + gbpsy->properties_count_str);
168}
169
170static void prop_append(struct gb_power_supply *gbpsy,
171 enum power_supply_property prop)
172{
173 enum power_supply_property *new_props_raw;
174
175 gbpsy->properties_count_str++;
176 new_props_raw = krealloc(gbpsy->props_raw, total_props(gbpsy) *
177 sizeof(enum power_supply_property),
178 GFP_KERNEL);
179 if (!new_props_raw)
180 return;
181 gbpsy->props_raw = new_props_raw;
182 gbpsy->props_raw[total_props(gbpsy) - 1] = prop;
183}
184
185static int __gb_power_supply_set_name(char *init_name, char *name, size_t len)
186{
187 unsigned int i = 0;
188 int ret = 0;
189 struct power_supply *psy;
190
191 if (!strlen(init_name))
192 init_name = "gb_power_supply";
193 strlcpy(name, init_name, len);
194
195 while ((ret < len) && (psy = power_supply_get_by_name(name))) {
196#ifdef PSY_HAVE_PUT
197 power_supply_put(psy);
198#endif
199 ret = snprintf(name, len, "%s_%u", init_name, ++i);
200 }
201 if (ret >= len)
202 return -ENOMEM;
203 return i;
204}
205
206static void _gb_power_supply_append_props(struct gb_power_supply *gbpsy)
207{
208 if (strlen(gbpsy->manufacturer))
209 prop_append(gbpsy, POWER_SUPPLY_PROP_MANUFACTURER);
210 if (strlen(gbpsy->model_name))
211 prop_append(gbpsy, POWER_SUPPLY_PROP_MODEL_NAME);
212 if (strlen(gbpsy->serial_number))
213 prop_append(gbpsy, POWER_SUPPLY_PROP_SERIAL_NUMBER);
214}
215
216static int gb_power_supply_description_get(struct gb_power_supply *gbpsy)
217{
218 struct gb_connection *connection = get_conn_from_psy(gbpsy);
219 struct gb_power_supply_get_description_request req;
220 struct gb_power_supply_get_description_response resp;
221 int ret;
222
223 req.psy_id = gbpsy->id;
224
225 ret = gb_operation_sync(connection,
226 GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION,
227 &req, sizeof(req), &resp, sizeof(resp));
228 if (ret < 0)
229 return ret;
230
231 gbpsy->manufacturer = kstrndup(resp.manufacturer, PROP_MAX, GFP_KERNEL);
232 if (!gbpsy->manufacturer)
233 return -ENOMEM;
234 gbpsy->model_name = kstrndup(resp.model, PROP_MAX, GFP_KERNEL);
235 if (!gbpsy->model_name)
236 return -ENOMEM;
237 gbpsy->serial_number = kstrndup(resp.serial_number, PROP_MAX,
238 GFP_KERNEL);
239 if (!gbpsy->serial_number)
240 return -ENOMEM;
241
242 gbpsy->type = le16_to_cpu(resp.type);
243 gbpsy->properties_count = resp.properties_count;
244
245 return 0;
246}
247
248static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy)
249{
250 struct gb_connection *connection = get_conn_from_psy(gbpsy);
251 struct gb_power_supply_get_property_descriptors_request req;
252 struct gb_power_supply_get_property_descriptors_response resp;
253 int ret;
254 int i;
255
256 if (gbpsy->properties_count == 0)
257 return 0;
258
259 req.psy_id = gbpsy->id;
260
261 ret = gb_operation_sync(connection,
262 GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS,
263 &req, sizeof(req), &resp,
264 sizeof(resp) + gbpsy->properties_count *
265 sizeof(struct gb_power_supply_props_desc));
266 if (ret < 0)
267 return ret;
268
269 gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props),
270 GFP_KERNEL);
271 if (!gbpsy->props)
272 return -ENOMEM;
273
274 gbpsy->props_raw = kzalloc(gbpsy->properties_count *
275 sizeof(*gbpsy->props_raw), GFP_KERNEL);
276 if (!gbpsy->props_raw)
277 return -ENOMEM;
278
279 /* Store available properties */
280 for (i = 0; i < gbpsy->properties_count; i++) {
281 gbpsy->props[i].prop = resp.props[i].property;
282 gbpsy->props_raw[i] = resp.props[i].property;
283 if (resp.props[i].is_writeable)
284 gbpsy->props[i].is_writeable = true;
285 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800286
287 /*
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000288 * now append the properties that we already got information in the
289 * get_description operation. (char * ones)
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800290 */
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000291 _gb_power_supply_append_props(gbpsy);
292
293 return 0;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800294}
295
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000296static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy,
297 enum power_supply_property psp)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700298{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000299 struct gb_connection *connection = get_conn_from_psy(gbpsy);
300 struct gb_power_supply_prop *prop;
301 struct gb_power_supply_get_property_request req;
302 struct gb_power_supply_get_property_response resp;
303 u32 val;
304 int ret;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800305
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000306 prop = get_psy_prop(gbpsy, psp);
307 if (!prop)
308 return -EINVAL;
309 req.psy_id = gbpsy->id;
310 req.property = (u8)psp;
311
312 ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY,
313 &req, sizeof(req), &resp, sizeof(resp));
314 if (ret < 0)
315 return ret;
316
317 val = le32_to_cpu(resp.prop_val);
318 if (val == prop->val)
319 return 0;
320
321 prop->previous_val = prop->val;
322 prop->val = val;
323
324 check_changed(gbpsy, prop);
325
326 return 0;
327}
328
329static int __gb_power_supply_property_get(struct gb_power_supply *gbpsy,
330 enum power_supply_property psp,
331 union power_supply_propval *val)
332{
333 struct gb_power_supply_prop *prop;
334
335 prop = get_psy_prop(gbpsy, psp);
336 if (!prop)
337 return -EINVAL;
338
339 val->intval = prop->val;
340 return 0;
341}
342
343static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy,
344 enum power_supply_property psp,
345 union power_supply_propval *val)
346{
347 switch (psp) {
348 case POWER_SUPPLY_PROP_MODEL_NAME:
349 val->strval = kstrndup(gbpsy->model_name, PROP_MAX, GFP_KERNEL);
350 break;
351 case POWER_SUPPLY_PROP_MANUFACTURER:
352 val->strval = kstrndup(gbpsy->manufacturer, PROP_MAX,
353 GFP_KERNEL);
354 break;
355 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
356 val->strval = kstrndup(gbpsy->serial_number, PROP_MAX,
357 GFP_KERNEL);
358 break;
359 default:
360 break;
361 }
362
363 return 0;
364}
365
366static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy,
367 enum power_supply_property psp,
368 union power_supply_propval *val)
369{
370 struct gb_connection *connection = get_conn_from_psy(gbpsy);
371 int ret;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800372
373 /*
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000374 * Properties of type const char *, were already fetched on
375 * get_description operation and should be cached in gb
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800376 */
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000377 if (is_prop_valint(psp))
378 ret = __gb_power_supply_property_get(gbpsy, psp, val);
379 else
380 ret = __gb_power_supply_property_strval_get(gbpsy, psp, val);
381
382 if (ret < 0)
383 dev_err(&connection->bundle->dev, "get property %u\n", psp);
384
385 return 0;
386}
387
388static int gb_power_supply_status_get(struct gb_power_supply *gbpsy)
389{
390 int ret = 0;
391 int i;
392
393 /* check if cache is good enough */
394 if (gbpsy->last_update &&
395 time_is_after_jiffies(gbpsy->last_update +
396 msecs_to_jiffies(cache_time)))
397 return 0;
398
399 for (i = 0; i < gbpsy->properties_count; i++) {
400 ret = __gb_power_supply_property_update(gbpsy,
401 gbpsy->props[i].prop);
402 if (ret < 0)
403 break;
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800404 }
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000405
406 if (ret == 0)
407 gbpsy->last_update = jiffies;
408
409 return ret;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700410}
411
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000412static void gb_power_supply_status_update(struct gb_power_supply *gbpsy)
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800413{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000414 /* check if there a change that need to be reported */
415 gb_power_supply_status_get(gbpsy);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800416
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000417 if (!gbpsy->changed)
418 return;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800419
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000420 gbpsy->update_interval = update_interval_init;
421 __gb_power_supply_changed(gbpsy);
422 gbpsy->changed = false;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800423}
424
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000425static void gb_power_supply_work(struct work_struct *work)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700426{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000427 struct gb_power_supply *gbpsy = container_of(work,
428 struct gb_power_supply,
429 work.work);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800430
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000431 /*
432 * if the poll interval is not set, disable polling, this is helpful
433 * specially at unregister time.
434 */
435 if (!gbpsy->update_interval)
436 return;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800437
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000438 gb_power_supply_status_update(gbpsy);
439 next_interval(gbpsy);
440 schedule_delayed_work(&gbpsy->work, gbpsy->update_interval);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700441}
442
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700443static int get_property(struct power_supply *b,
444 enum power_supply_property psp,
445 union power_supply_propval *val)
446{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000447 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700448
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000449 gb_power_supply_status_get(gbpsy);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700450
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000451 return _gb_power_supply_property_get(gbpsy, psp, val);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700452}
453
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000454static int gb_power_supply_property_set(struct gb_power_supply *gbpsy,
455 enum power_supply_property psp,
456 int val)
457{
458 struct gb_connection *connection = get_conn_from_psy(gbpsy);
459 struct gb_power_supply_prop *prop;
460 struct gb_power_supply_set_property_request req;
461 int ret;
462
463 prop = get_psy_prop(gbpsy, psp);
464 if (!prop)
465 return -EINVAL;
466 req.psy_id = gbpsy->id;
467 req.property = (u8)psp;
468 req.prop_val = cpu_to_le32(val);
469
470 ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY,
471 &req, sizeof(req), NULL, 0);
472 if (ret < 0)
473 goto out;
474
475 /* cache immediately the new value */
476 prop->val = val;
477
478out:
479 return ret;
480}
481
482static int set_property(struct power_supply *b,
483 enum power_supply_property psp,
484 const union power_supply_propval *val)
485{
486 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
487
488 return gb_power_supply_property_set(gbpsy, psp, val->intval);
489}
490
491static int property_is_writeable(struct power_supply *b,
492 enum power_supply_property psp)
493{
494 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
495
496 return is_psy_prop_writeable(gbpsy, psp);
497}
498
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700499
Alex Elder5c586402015-05-07 13:00:21 -0500500#ifdef DRIVER_OWNS_PSY_STRUCT
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000501static int gb_power_supply_register(struct gb_power_supply *gbpsy)
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200502{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000503 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200504
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000505 gbpsy->psy.name = gbpsy->name;
506 gbpsy->psy.type = gbpsy->type;
507 gbpsy->psy.properties = gbpsy->props_raw;
508 gbpsy->psy.num_properties = total_props(gbpsy);
509 gbpsy->psy.get_property = get_property;
510 gbpsy->psy.set_property = set_property;
511 gbpsy->psy.property_is_writeable = property_is_writeable;
512
513 return power_supply_register(&connection->bundle->dev,
514 &gbpsy->psy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200515}
516#else
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000517static int gb_power_supply_register(struct gb_power_supply *gbpsy)
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200518{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000519 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200520 struct power_supply_config cfg = {};
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200521
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000522 cfg.drv_data = gbpsy;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200523
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000524 gbpsy->desc.name = gbpsy->name;
525 gbpsy->desc.type = gbpsy->type;
526 gbpsy->desc.properties = gbpsy->props_raw;
527 gbpsy->desc.num_properties = total_props(gbpsy);
528 gbpsy->desc.get_property = get_property;
529 gbpsy->desc.set_property = set_property;
530 gbpsy->desc.property_is_writeable = property_is_writeable;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200531
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000532 gbpsy->psy = power_supply_register(&connection->bundle->dev,
533 &gbpsy->desc, &cfg);
534 if (IS_ERR(gbpsy->psy))
535 return PTR_ERR(gbpsy->psy);
Alex Elder9ade6d32015-05-07 13:00:20 -0500536
537 return 0;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200538}
539#endif
540
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000541static void _gb_power_supply_free(struct gb_power_supply *gbpsy)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700542{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000543 kfree(gbpsy->serial_number);
544 kfree(gbpsy->model_name);
545 kfree(gbpsy->manufacturer);
546 kfree(gbpsy->props_raw);
547 kfree(gbpsy->props);
548 kfree(gbpsy);
549}
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700550
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000551static void _gb_power_supply_release(struct gb_power_supply *gbpsy)
552{
553 if (!gbpsy)
554 return;
555
556 gbpsy->update_interval = 0;
557
558 cancel_delayed_work_sync(&gbpsy->work);
559#ifdef DRIVER_OWNS_PSY_STRUCT
560 power_supply_unregister(&gbpsy->psy);
561#else
562 power_supply_unregister(gbpsy->psy);
563#endif
564
565 _gb_power_supply_free(gbpsy);
566}
567
568static void _gb_power_supplies_release(struct gb_power_supplies *supplies)
569{
570 int i;
571
Rui Miguel Silva7ccac20d2016-01-08 13:53:42 +0000572 if (!supplies->supply)
573 return;
574
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000575 mutex_lock(&supplies->supplies_lock);
576 for (i = 0; i < supplies->supplies_count; i++)
577 _gb_power_supply_release(&supplies->supply[i]);
578 mutex_unlock(&supplies->supplies_lock);
579}
580
581static int gb_power_supplies_get_count(struct gb_power_supplies *supplies)
582{
583 struct gb_power_supply_get_supplies_response resp;
584 int ret;
585
586 ret = gb_operation_sync(supplies->connection,
587 GB_POWER_SUPPLY_TYPE_GET_SUPPLIES,
588 NULL, 0, &resp, sizeof(resp));
589 if (ret < 0)
590 return ret;
591
592 if (!resp.supplies_count)
593 return -EINVAL;
594
595 supplies->supplies_count = resp.supplies_count;
596
597 return ret;
598}
599
600static int gb_power_supply_config(struct gb_power_supplies *supplies, int id)
601{
602 struct gb_power_supply *gbpsy = &supplies->supply[id];
603 int ret;
604
605 gbpsy->supplies = supplies;
606 gbpsy->id = id;
607
608 ret = gb_power_supply_description_get(gbpsy);
609 if (ret < 0)
610 goto out;
611
612 ret = gb_power_supply_prop_descriptors_get(gbpsy);
613 if (ret < 0)
614 goto out;
615
616 /* guarantee that we have an unique name, before register */
617 ret = __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name,
618 sizeof(gbpsy->name));
619 if (ret < 0)
620 goto out;
621
622 ret = gb_power_supply_register(gbpsy);
623 if (ret < 0)
624 goto out;
625
626 gbpsy->update_interval = update_interval_init;
627 INIT_DELAYED_WORK(&gbpsy->work, gb_power_supply_work);
628 schedule_delayed_work(&gbpsy->work, 0);
629
630out:
631 return ret;
632}
633
634static int gb_power_supplies_setup(struct gb_power_supplies *supplies)
635{
636 struct gb_connection *connection = supplies->connection;
637 int ret;
638 int i;
639
640 mutex_lock(&supplies->supplies_lock);
641
642 ret = gb_power_supplies_get_count(supplies);
643 if (ret < 0)
644 goto out;
645
646 supplies->supply = kzalloc(supplies->supplies_count *
647 sizeof(struct gb_power_supply),
648 GFP_KERNEL);
649
Johan Hovolde0d91ff2016-01-07 12:28:29 +0100650 if (!supplies->supply) {
651 ret = -ENOMEM;
652 goto out;
653 }
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700654
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000655 for (i = 0; i < supplies->supplies_count; i++) {
656 ret = gb_power_supply_config(supplies, i);
657 if (ret < 0) {
658 dev_err(&connection->bundle->dev,
659 "Fail to configure supplies devices\n");
660 goto out;
661 }
662 }
663out:
664 mutex_unlock(&supplies->supplies_lock);
665 return ret;
666}
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +0800667
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000668static int gb_power_supply_event_recv(u8 type, struct gb_operation *op)
669{
670 struct gb_connection *connection = op->connection;
671 struct gb_power_supplies *supplies = connection->private;
672 struct gb_power_supply *gbpsy;
673 struct gb_message *request;
674 struct gb_power_supply_event_request *payload;
675 u8 psy_id;
676 u8 event;
677 int ret = 0;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800678
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000679 if (type != GB_POWER_SUPPLY_TYPE_EVENT) {
680 dev_err(&connection->bundle->dev,
681 "Unsupported unsolicited event: %u\n", type);
682 return -EINVAL;
683 }
684
685 request = op->request;
686
687 if (request->payload_size < sizeof(*payload)) {
688 dev_err(&connection->bundle->dev,
689 "Wrong event size received (%zu < %zu)\n",
690 request->payload_size, sizeof(*payload));
691 return -EINVAL;
692 }
693
694 payload = request->payload;
695 psy_id = payload->psy_id;
696 mutex_lock(&supplies->supplies_lock);
697 if (psy_id >= supplies->supplies_count || !&supplies->supply[psy_id]) {
698 dev_err(&connection->bundle->dev,
699 "Event received for unconfigured power_supply id: %d\n",
700 psy_id);
701 ret = -EINVAL;
702 goto out_unlock;
703 }
704
705 event = payload->event;
706 /*
707 * we will only handle events after setup is done and before release is
708 * running. For that just check update_interval.
709 */
710 gbpsy = &supplies->supply[psy_id];
711 if (gbpsy->update_interval) {
712 ret = -ESHUTDOWN;
713 goto out_unlock;
714 }
715
716 if (event & GB_POWER_SUPPLY_UPDATE)
717 gb_power_supply_status_update(gbpsy);
718
719out_unlock:
720 mutex_unlock(&supplies->supplies_lock);
721 return ret;
722}
723
724static int gb_power_supply_connection_init(struct gb_connection *connection)
725{
726 struct gb_power_supplies *supplies;
727
728 supplies = kzalloc(sizeof(*supplies), GFP_KERNEL);
729 if (!supplies)
730 return -ENOMEM;
731
732 supplies->connection = connection;
733 connection->private = supplies;
734
735 mutex_init(&supplies->supplies_lock);
736
737 return gb_power_supplies_setup(supplies);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700738}
739
Rui Miguel Silva2724be02015-11-12 15:36:00 +0000740static void gb_power_supply_connection_exit(struct gb_connection *connection)
Alex Elder697e55d2014-10-20 23:01:04 -0500741{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000742 struct gb_power_supplies *supplies = connection->private;
Alex Elder697e55d2014-10-20 23:01:04 -0500743
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000744 _gb_power_supplies_release(supplies);
Alex Elder697e55d2014-10-20 23:01:04 -0500745}
746
Rui Miguel Silva2724be02015-11-12 15:36:00 +0000747static struct gb_protocol power_supply_protocol = {
748 .name = "power_supply",
749 .id = GREYBUS_PROTOCOL_POWER_SUPPLY,
750 .major = GB_POWER_SUPPLY_VERSION_MAJOR,
751 .minor = GB_POWER_SUPPLY_VERSION_MINOR,
752 .connection_init = gb_power_supply_connection_init,
753 .connection_exit = gb_power_supply_connection_exit,
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000754 .request_recv = gb_power_supply_event_recv,
Alex Elder19d03de2014-11-05 16:12:53 -0600755};
756
Rui Miguel Silva2724be02015-11-12 15:36:00 +0000757gb_protocol_driver(&power_supply_protocol);
Alex Elder19d03de2014-11-05 16:12:53 -0600758
Greg Kroah-Hartman7dd26262014-12-24 13:01:42 -0800759MODULE_LICENSE("GPL v2");