blob: 2dc193a081b0a821464dc466ff52b5cc3f57d36b [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;
Rui Miguel Silvaff85f722016-01-08 13:53:43 +000028 bool registered;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000029#ifdef DRIVER_OWNS_PSY_STRUCT
30 struct power_supply psy;
31#define to_gb_power_supply(x) container_of(x, struct gb_power_supply, psy)
32#else
33 struct power_supply *psy;
34 struct power_supply_desc desc;
35#define to_gb_power_supply(x) power_supply_get_drvdata(x)
36#endif
37 char name[64];
38 struct gb_power_supplies *supplies;
39 struct delayed_work work;
40 char *manufacturer;
41 char *model_name;
42 char *serial_number;
43 u8 type;
44 u8 properties_count;
45 u8 properties_count_str;
46 unsigned long last_update;
47 unsigned int update_interval;
48 bool changed;
49 struct gb_power_supply_prop *props;
50 enum power_supply_property *props_raw;
51};
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080052
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000053struct gb_power_supplies {
54 struct gb_connection *connection;
55 u8 supplies_count;
56 struct gb_power_supply *supply;
57 struct mutex supplies_lock;
58};
59
60/* cache time in milliseconds, if cache_time is set to 0 cache is disable */
61static unsigned int cache_time = 1000;
62/*
63 * update interval initial and maximum value, between the two will
64 * back-off exponential
65 */
66static unsigned int update_interval_init = 1 * HZ;
67static unsigned int update_interval_max = 30 * HZ;
68
69struct gb_power_supply_changes {
70 enum power_supply_property prop;
71 u32 tolerance_change;
72};
73
74static const struct gb_power_supply_changes psy_props_changes[] = {
75 { .prop = GB_POWER_SUPPLY_PROP_STATUS,
76 .tolerance_change = 0,
77 },
78 { .prop = GB_POWER_SUPPLY_PROP_TEMP,
79 .tolerance_change = 500,
80 },
81 { .prop = GB_POWER_SUPPLY_PROP_ONLINE,
82 .tolerance_change = 0,
83 },
84};
85
86static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy)
87{
88 return gbpsy->supplies->connection;
89}
90
91static struct gb_power_supply_prop *get_psy_prop(struct gb_power_supply *gbpsy,
92 enum power_supply_property psp)
93{
94 int i;
95
96 for (i = 0; i < gbpsy->properties_count; i++)
97 if (gbpsy->props[i].prop == psp)
98 return &gbpsy->props[i];
99 return NULL;
100}
101
102static int is_psy_prop_writeable(struct gb_power_supply *gbpsy,
103 enum power_supply_property psp)
104{
105 struct gb_power_supply_prop *prop;
106
107 prop = get_psy_prop(gbpsy, psp);
108 if (!prop)
109 return -ENOENT;
110 return prop->is_writeable ? 1 : 0;
111}
112
113static int is_prop_valint(enum power_supply_property psp)
114{
115 return ((psp < POWER_SUPPLY_PROP_MODEL_NAME) ? 1 : 0);
116}
117
118static void next_interval(struct gb_power_supply *gbpsy)
119{
120 if (gbpsy->update_interval == update_interval_max)
121 return;
122
123 /* do some exponential back-off in the update interval */
124 gbpsy->update_interval *= 2;
125 if (gbpsy->update_interval > update_interval_max)
126 gbpsy->update_interval = update_interval_max;
127}
128
129#ifdef DRIVER_OWNS_PSY_STRUCT
130static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
131{
132 power_supply_changed(&gbpsy->psy);
133}
134#else
135static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
136{
137 power_supply_changed(gbpsy->psy);
138}
139#endif
140
141static void check_changed(struct gb_power_supply *gbpsy,
142 struct gb_power_supply_prop *prop)
143{
144 const struct gb_power_supply_changes *psyc;
145 u32 val = prop->val;
146 u32 prev_val = prop->previous_val;
147 int i;
148
149 for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) {
150 psyc = &psy_props_changes[i];
151 if (prop->prop == psyc->prop) {
152 if (!psyc->tolerance_change)
153 gbpsy->changed = true;
154 else if (val < prev_val &&
155 prev_val - val > psyc->tolerance_change)
156 gbpsy->changed = true;
157 else if (val > prev_val &&
158 val - prev_val > psyc->tolerance_change)
159 gbpsy->changed = true;
160 break;
161 }
162 }
163}
164
165static int total_props(struct gb_power_supply *gbpsy)
166{
167 /* this return the intval plus the strval properties */
168 return (gbpsy->properties_count + gbpsy->properties_count_str);
169}
170
171static void prop_append(struct gb_power_supply *gbpsy,
172 enum power_supply_property prop)
173{
174 enum power_supply_property *new_props_raw;
175
176 gbpsy->properties_count_str++;
177 new_props_raw = krealloc(gbpsy->props_raw, total_props(gbpsy) *
178 sizeof(enum power_supply_property),
179 GFP_KERNEL);
180 if (!new_props_raw)
181 return;
182 gbpsy->props_raw = new_props_raw;
183 gbpsy->props_raw[total_props(gbpsy) - 1] = prop;
184}
185
186static int __gb_power_supply_set_name(char *init_name, char *name, size_t len)
187{
188 unsigned int i = 0;
189 int ret = 0;
190 struct power_supply *psy;
191
192 if (!strlen(init_name))
193 init_name = "gb_power_supply";
194 strlcpy(name, init_name, len);
195
196 while ((ret < len) && (psy = power_supply_get_by_name(name))) {
197#ifdef PSY_HAVE_PUT
198 power_supply_put(psy);
199#endif
200 ret = snprintf(name, len, "%s_%u", init_name, ++i);
201 }
202 if (ret >= len)
203 return -ENOMEM;
204 return i;
205}
206
207static void _gb_power_supply_append_props(struct gb_power_supply *gbpsy)
208{
209 if (strlen(gbpsy->manufacturer))
210 prop_append(gbpsy, POWER_SUPPLY_PROP_MANUFACTURER);
211 if (strlen(gbpsy->model_name))
212 prop_append(gbpsy, POWER_SUPPLY_PROP_MODEL_NAME);
213 if (strlen(gbpsy->serial_number))
214 prop_append(gbpsy, POWER_SUPPLY_PROP_SERIAL_NUMBER);
215}
216
217static int gb_power_supply_description_get(struct gb_power_supply *gbpsy)
218{
219 struct gb_connection *connection = get_conn_from_psy(gbpsy);
220 struct gb_power_supply_get_description_request req;
221 struct gb_power_supply_get_description_response resp;
222 int ret;
223
224 req.psy_id = gbpsy->id;
225
226 ret = gb_operation_sync(connection,
227 GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION,
228 &req, sizeof(req), &resp, sizeof(resp));
229 if (ret < 0)
230 return ret;
231
232 gbpsy->manufacturer = kstrndup(resp.manufacturer, PROP_MAX, GFP_KERNEL);
233 if (!gbpsy->manufacturer)
234 return -ENOMEM;
235 gbpsy->model_name = kstrndup(resp.model, PROP_MAX, GFP_KERNEL);
236 if (!gbpsy->model_name)
237 return -ENOMEM;
238 gbpsy->serial_number = kstrndup(resp.serial_number, PROP_MAX,
239 GFP_KERNEL);
240 if (!gbpsy->serial_number)
241 return -ENOMEM;
242
243 gbpsy->type = le16_to_cpu(resp.type);
244 gbpsy->properties_count = resp.properties_count;
245
246 return 0;
247}
248
249static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy)
250{
251 struct gb_connection *connection = get_conn_from_psy(gbpsy);
252 struct gb_power_supply_get_property_descriptors_request req;
253 struct gb_power_supply_get_property_descriptors_response resp;
254 int ret;
255 int i;
256
257 if (gbpsy->properties_count == 0)
258 return 0;
259
260 req.psy_id = gbpsy->id;
261
262 ret = gb_operation_sync(connection,
263 GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS,
264 &req, sizeof(req), &resp,
265 sizeof(resp) + gbpsy->properties_count *
266 sizeof(struct gb_power_supply_props_desc));
267 if (ret < 0)
268 return ret;
269
270 gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props),
271 GFP_KERNEL);
272 if (!gbpsy->props)
273 return -ENOMEM;
274
275 gbpsy->props_raw = kzalloc(gbpsy->properties_count *
276 sizeof(*gbpsy->props_raw), GFP_KERNEL);
277 if (!gbpsy->props_raw)
278 return -ENOMEM;
279
280 /* Store available properties */
281 for (i = 0; i < gbpsy->properties_count; i++) {
282 gbpsy->props[i].prop = resp.props[i].property;
283 gbpsy->props_raw[i] = resp.props[i].property;
284 if (resp.props[i].is_writeable)
285 gbpsy->props[i].is_writeable = true;
286 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800287
288 /*
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000289 * now append the properties that we already got information in the
290 * get_description operation. (char * ones)
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800291 */
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000292 _gb_power_supply_append_props(gbpsy);
293
294 return 0;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800295}
296
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000297static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy,
298 enum power_supply_property psp)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700299{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000300 struct gb_connection *connection = get_conn_from_psy(gbpsy);
301 struct gb_power_supply_prop *prop;
302 struct gb_power_supply_get_property_request req;
303 struct gb_power_supply_get_property_response resp;
304 u32 val;
305 int ret;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800306
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000307 prop = get_psy_prop(gbpsy, psp);
308 if (!prop)
309 return -EINVAL;
310 req.psy_id = gbpsy->id;
311 req.property = (u8)psp;
312
313 ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY,
314 &req, sizeof(req), &resp, sizeof(resp));
315 if (ret < 0)
316 return ret;
317
318 val = le32_to_cpu(resp.prop_val);
319 if (val == prop->val)
320 return 0;
321
322 prop->previous_val = prop->val;
323 prop->val = val;
324
325 check_changed(gbpsy, prop);
326
327 return 0;
328}
329
330static int __gb_power_supply_property_get(struct gb_power_supply *gbpsy,
331 enum power_supply_property psp,
332 union power_supply_propval *val)
333{
334 struct gb_power_supply_prop *prop;
335
336 prop = get_psy_prop(gbpsy, psp);
337 if (!prop)
338 return -EINVAL;
339
340 val->intval = prop->val;
341 return 0;
342}
343
344static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy,
345 enum power_supply_property psp,
346 union power_supply_propval *val)
347{
348 switch (psp) {
349 case POWER_SUPPLY_PROP_MODEL_NAME:
Rui Miguel Silvaf921fb12016-01-08 13:53:46 +0000350 val->strval = gbpsy->model_name;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000351 break;
352 case POWER_SUPPLY_PROP_MANUFACTURER:
Rui Miguel Silvaf921fb12016-01-08 13:53:46 +0000353 val->strval = gbpsy->manufacturer;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000354 break;
355 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
Rui Miguel Silvaf921fb12016-01-08 13:53:46 +0000356 val->strval = gbpsy->serial_number;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000357 break;
358 default:
359 break;
360 }
361
362 return 0;
363}
364
365static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy,
366 enum power_supply_property psp,
367 union power_supply_propval *val)
368{
369 struct gb_connection *connection = get_conn_from_psy(gbpsy);
370 int ret;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800371
372 /*
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000373 * Properties of type const char *, were already fetched on
374 * get_description operation and should be cached in gb
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800375 */
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000376 if (is_prop_valint(psp))
377 ret = __gb_power_supply_property_get(gbpsy, psp, val);
378 else
379 ret = __gb_power_supply_property_strval_get(gbpsy, psp, val);
380
381 if (ret < 0)
382 dev_err(&connection->bundle->dev, "get property %u\n", psp);
383
384 return 0;
385}
386
387static int gb_power_supply_status_get(struct gb_power_supply *gbpsy)
388{
389 int ret = 0;
390 int i;
391
392 /* check if cache is good enough */
393 if (gbpsy->last_update &&
394 time_is_after_jiffies(gbpsy->last_update +
395 msecs_to_jiffies(cache_time)))
396 return 0;
397
398 for (i = 0; i < gbpsy->properties_count; i++) {
399 ret = __gb_power_supply_property_update(gbpsy,
400 gbpsy->props[i].prop);
401 if (ret < 0)
402 break;
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800403 }
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000404
405 if (ret == 0)
406 gbpsy->last_update = jiffies;
407
408 return ret;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700409}
410
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000411static void gb_power_supply_status_update(struct gb_power_supply *gbpsy)
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800412{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000413 /* check if there a change that need to be reported */
414 gb_power_supply_status_get(gbpsy);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800415
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000416 if (!gbpsy->changed)
417 return;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800418
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000419 gbpsy->update_interval = update_interval_init;
420 __gb_power_supply_changed(gbpsy);
421 gbpsy->changed = false;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800422}
423
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000424static void gb_power_supply_work(struct work_struct *work)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700425{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000426 struct gb_power_supply *gbpsy = container_of(work,
427 struct gb_power_supply,
428 work.work);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800429
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000430 /*
431 * if the poll interval is not set, disable polling, this is helpful
432 * specially at unregister time.
433 */
434 if (!gbpsy->update_interval)
435 return;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800436
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000437 gb_power_supply_status_update(gbpsy);
438 next_interval(gbpsy);
439 schedule_delayed_work(&gbpsy->work, gbpsy->update_interval);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700440}
441
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700442static int get_property(struct power_supply *b,
443 enum power_supply_property psp,
444 union power_supply_propval *val)
445{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000446 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700447
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000448 gb_power_supply_status_get(gbpsy);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700449
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000450 return _gb_power_supply_property_get(gbpsy, psp, val);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700451}
452
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000453static int gb_power_supply_property_set(struct gb_power_supply *gbpsy,
454 enum power_supply_property psp,
455 int val)
456{
457 struct gb_connection *connection = get_conn_from_psy(gbpsy);
458 struct gb_power_supply_prop *prop;
459 struct gb_power_supply_set_property_request req;
460 int ret;
461
462 prop = get_psy_prop(gbpsy, psp);
463 if (!prop)
464 return -EINVAL;
465 req.psy_id = gbpsy->id;
466 req.property = (u8)psp;
467 req.prop_val = cpu_to_le32(val);
468
469 ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY,
470 &req, sizeof(req), NULL, 0);
471 if (ret < 0)
472 goto out;
473
474 /* cache immediately the new value */
475 prop->val = val;
476
477out:
478 return ret;
479}
480
481static int set_property(struct power_supply *b,
482 enum power_supply_property psp,
483 const union power_supply_propval *val)
484{
485 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
486
487 return gb_power_supply_property_set(gbpsy, psp, val->intval);
488}
489
490static int property_is_writeable(struct power_supply *b,
491 enum power_supply_property psp)
492{
493 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
494
495 return is_psy_prop_writeable(gbpsy, psp);
496}
497
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700498
Alex Elder5c586402015-05-07 13:00:21 -0500499#ifdef DRIVER_OWNS_PSY_STRUCT
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000500static int gb_power_supply_register(struct gb_power_supply *gbpsy)
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200501{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000502 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200503
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000504 gbpsy->psy.name = gbpsy->name;
505 gbpsy->psy.type = gbpsy->type;
506 gbpsy->psy.properties = gbpsy->props_raw;
507 gbpsy->psy.num_properties = total_props(gbpsy);
508 gbpsy->psy.get_property = get_property;
509 gbpsy->psy.set_property = set_property;
510 gbpsy->psy.property_is_writeable = property_is_writeable;
511
512 return power_supply_register(&connection->bundle->dev,
513 &gbpsy->psy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200514}
515#else
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000516static int gb_power_supply_register(struct gb_power_supply *gbpsy)
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200517{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000518 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200519 struct power_supply_config cfg = {};
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200520
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000521 cfg.drv_data = gbpsy;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200522
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000523 gbpsy->desc.name = gbpsy->name;
524 gbpsy->desc.type = gbpsy->type;
525 gbpsy->desc.properties = gbpsy->props_raw;
526 gbpsy->desc.num_properties = total_props(gbpsy);
527 gbpsy->desc.get_property = get_property;
528 gbpsy->desc.set_property = set_property;
529 gbpsy->desc.property_is_writeable = property_is_writeable;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200530
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000531 gbpsy->psy = power_supply_register(&connection->bundle->dev,
532 &gbpsy->desc, &cfg);
533 if (IS_ERR(gbpsy->psy))
534 return PTR_ERR(gbpsy->psy);
Alex Elder9ade6d32015-05-07 13:00:20 -0500535
536 return 0;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200537}
538#endif
539
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000540static void _gb_power_supply_free(struct gb_power_supply *gbpsy)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700541{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000542 kfree(gbpsy->serial_number);
543 kfree(gbpsy->model_name);
544 kfree(gbpsy->manufacturer);
545 kfree(gbpsy->props_raw);
546 kfree(gbpsy->props);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000547}
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700548
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000549static void _gb_power_supply_release(struct gb_power_supply *gbpsy)
550{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000551
552 gbpsy->update_interval = 0;
553
554 cancel_delayed_work_sync(&gbpsy->work);
555#ifdef DRIVER_OWNS_PSY_STRUCT
Rui Miguel Silvaff85f722016-01-08 13:53:43 +0000556 if (gbpsy->registered)
557 power_supply_unregister(&gbpsy->psy);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000558#else
Rui Miguel Silvaff85f722016-01-08 13:53:43 +0000559 if (gbpsy->registered)
560 power_supply_unregister(gbpsy->psy);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000561#endif
562
563 _gb_power_supply_free(gbpsy);
564}
565
566static void _gb_power_supplies_release(struct gb_power_supplies *supplies)
567{
568 int i;
569
Rui Miguel Silva7ccac20d2016-01-08 13:53:42 +0000570 if (!supplies->supply)
571 return;
572
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000573 mutex_lock(&supplies->supplies_lock);
574 for (i = 0; i < supplies->supplies_count; i++)
575 _gb_power_supply_release(&supplies->supply[i]);
Rui Miguel Silvaaccad1b2016-01-08 13:53:47 +0000576 kfree(supplies->supply);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000577 mutex_unlock(&supplies->supplies_lock);
Rui Miguel Silva23f25ba2016-01-08 13:53:44 +0000578 kfree(supplies);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000579}
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:
Rui Miguel Silvaff85f722016-01-08 13:53:43 +0000631 /* if everything went fine just mark it for release code to know */
632 if (ret == 0)
633 gbpsy->registered = true;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000634 return ret;
635}
636
637static int gb_power_supplies_setup(struct gb_power_supplies *supplies)
638{
639 struct gb_connection *connection = supplies->connection;
640 int ret;
641 int i;
642
643 mutex_lock(&supplies->supplies_lock);
644
645 ret = gb_power_supplies_get_count(supplies);
646 if (ret < 0)
647 goto out;
648
649 supplies->supply = kzalloc(supplies->supplies_count *
650 sizeof(struct gb_power_supply),
651 GFP_KERNEL);
652
Johan Hovolde0d91ff2016-01-07 12:28:29 +0100653 if (!supplies->supply) {
654 ret = -ENOMEM;
655 goto out;
656 }
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700657
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000658 for (i = 0; i < supplies->supplies_count; i++) {
659 ret = gb_power_supply_config(supplies, i);
660 if (ret < 0) {
661 dev_err(&connection->bundle->dev,
662 "Fail to configure supplies devices\n");
663 goto out;
664 }
665 }
666out:
667 mutex_unlock(&supplies->supplies_lock);
668 return ret;
669}
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +0800670
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000671static int gb_power_supply_event_recv(u8 type, struct gb_operation *op)
672{
673 struct gb_connection *connection = op->connection;
674 struct gb_power_supplies *supplies = connection->private;
675 struct gb_power_supply *gbpsy;
676 struct gb_message *request;
677 struct gb_power_supply_event_request *payload;
678 u8 psy_id;
679 u8 event;
680 int ret = 0;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800681
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000682 if (type != GB_POWER_SUPPLY_TYPE_EVENT) {
683 dev_err(&connection->bundle->dev,
684 "Unsupported unsolicited event: %u\n", type);
685 return -EINVAL;
686 }
687
688 request = op->request;
689
690 if (request->payload_size < sizeof(*payload)) {
691 dev_err(&connection->bundle->dev,
692 "Wrong event size received (%zu < %zu)\n",
693 request->payload_size, sizeof(*payload));
694 return -EINVAL;
695 }
696
697 payload = request->payload;
698 psy_id = payload->psy_id;
699 mutex_lock(&supplies->supplies_lock);
Rui Miguel Silvaadb57cf2016-01-12 14:38:21 +0000700 if (psy_id >= supplies->supplies_count ||
701 !supplies->supply[psy_id].registered) {
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000702 dev_err(&connection->bundle->dev,
703 "Event received for unconfigured power_supply id: %d\n",
704 psy_id);
705 ret = -EINVAL;
706 goto out_unlock;
707 }
708
709 event = payload->event;
710 /*
711 * we will only handle events after setup is done and before release is
712 * running. For that just check update_interval.
713 */
714 gbpsy = &supplies->supply[psy_id];
715 if (gbpsy->update_interval) {
716 ret = -ESHUTDOWN;
717 goto out_unlock;
718 }
719
720 if (event & GB_POWER_SUPPLY_UPDATE)
721 gb_power_supply_status_update(gbpsy);
722
723out_unlock:
724 mutex_unlock(&supplies->supplies_lock);
725 return ret;
726}
727
728static int gb_power_supply_connection_init(struct gb_connection *connection)
729{
730 struct gb_power_supplies *supplies;
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +0000731 int ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000732
733 supplies = kzalloc(sizeof(*supplies), GFP_KERNEL);
734 if (!supplies)
735 return -ENOMEM;
736
737 supplies->connection = connection;
738 connection->private = supplies;
739
740 mutex_init(&supplies->supplies_lock);
741
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +0000742 ret = gb_power_supplies_setup(supplies);
743 if (ret < 0)
744 _gb_power_supplies_release(supplies);
745
746 return ret;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700747}
748
Rui Miguel Silva2724be02015-11-12 15:36:00 +0000749static void gb_power_supply_connection_exit(struct gb_connection *connection)
Alex Elder697e55d2014-10-20 23:01:04 -0500750{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000751 struct gb_power_supplies *supplies = connection->private;
Alex Elder697e55d2014-10-20 23:01:04 -0500752
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000753 _gb_power_supplies_release(supplies);
Alex Elder697e55d2014-10-20 23:01:04 -0500754}
755
Rui Miguel Silva2724be02015-11-12 15:36:00 +0000756static struct gb_protocol power_supply_protocol = {
757 .name = "power_supply",
758 .id = GREYBUS_PROTOCOL_POWER_SUPPLY,
759 .major = GB_POWER_SUPPLY_VERSION_MAJOR,
760 .minor = GB_POWER_SUPPLY_VERSION_MINOR,
761 .connection_init = gb_power_supply_connection_init,
762 .connection_exit = gb_power_supply_connection_exit,
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000763 .request_recv = gb_power_supply_event_recv,
Alex Elder19d03de2014-11-05 16:12:53 -0600764};
765
Rui Miguel Silva2724be02015-11-12 15:36:00 +0000766gb_protocol_driver(&power_supply_protocol);
Alex Elder19d03de2014-11-05 16:12:53 -0600767
Greg Kroah-Hartman7dd26262014-12-24 13:01:42 -0800768MODULE_LICENSE("GPL v2");