blob: 199a19a634b6c13ec7ea75a67cb9d2261b0b1150 [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;
Sandeep Patilf8811c72016-03-15 12:28:38 -070030#ifndef CORE_OWNS_PSY_STRUCT
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000031 struct power_supply psy;
32#define to_gb_power_supply(x) container_of(x, struct gb_power_supply, psy)
33#else
34 struct power_supply *psy;
35 struct power_supply_desc desc;
36#define to_gb_power_supply(x) power_supply_get_drvdata(x)
37#endif
38 char name[64];
39 struct gb_power_supplies *supplies;
40 struct delayed_work work;
41 char *manufacturer;
42 char *model_name;
43 char *serial_number;
44 u8 type;
45 u8 properties_count;
46 u8 properties_count_str;
47 unsigned long last_update;
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +010048 u8 cache_invalid;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000049 unsigned int update_interval;
50 bool changed;
51 struct gb_power_supply_prop *props;
52 enum power_supply_property *props_raw;
53};
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080054
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000055struct gb_power_supplies {
56 struct gb_connection *connection;
57 u8 supplies_count;
58 struct gb_power_supply *supply;
59 struct mutex supplies_lock;
60};
61
62/* cache time in milliseconds, if cache_time is set to 0 cache is disable */
63static unsigned int cache_time = 1000;
64/*
65 * update interval initial and maximum value, between the two will
66 * back-off exponential
67 */
68static unsigned int update_interval_init = 1 * HZ;
69static unsigned int update_interval_max = 30 * HZ;
70
71struct gb_power_supply_changes {
72 enum power_supply_property prop;
73 u32 tolerance_change;
Rui Miguel Silvac4582f92016-08-16 22:31:55 +010074 void (*prop_changed)(struct gb_power_supply *gbpsy,
75 struct gb_power_supply_prop *prop);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000076};
77
78static const struct gb_power_supply_changes psy_props_changes[] = {
Rui Miguel Silvac4582f92016-08-16 22:31:55 +010079 { .prop = GB_POWER_SUPPLY_PROP_STATUS,
80 .tolerance_change = 0,
81 .prop_changed = NULL,
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000082 },
Rui Miguel Silvac4582f92016-08-16 22:31:55 +010083 { .prop = GB_POWER_SUPPLY_PROP_TEMP,
84 .tolerance_change = 500,
85 .prop_changed = NULL,
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000086 },
Rui Miguel Silvac4582f92016-08-16 22:31:55 +010087 { .prop = GB_POWER_SUPPLY_PROP_ONLINE,
88 .tolerance_change = 0,
89 .prop_changed = NULL,
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +000090 },
91};
92
Rui Miguel Silva47becc52016-07-13 14:11:20 +010093static int get_psp_from_gb_prop(int gb_prop, enum power_supply_property *psp)
94{
95 int prop;
96
97 switch (gb_prop) {
98 case GB_POWER_SUPPLY_PROP_STATUS:
99 prop = POWER_SUPPLY_PROP_STATUS;
100 break;
101 case GB_POWER_SUPPLY_PROP_CHARGE_TYPE:
102 prop = POWER_SUPPLY_PROP_CHARGE_TYPE;
103 break;
104 case GB_POWER_SUPPLY_PROP_HEALTH:
105 prop = POWER_SUPPLY_PROP_HEALTH;
106 break;
107 case GB_POWER_SUPPLY_PROP_PRESENT:
108 prop = POWER_SUPPLY_PROP_PRESENT;
109 break;
110 case GB_POWER_SUPPLY_PROP_ONLINE:
111 prop = POWER_SUPPLY_PROP_ONLINE;
112 break;
113 case GB_POWER_SUPPLY_PROP_AUTHENTIC:
114 prop = POWER_SUPPLY_PROP_AUTHENTIC;
115 break;
116 case GB_POWER_SUPPLY_PROP_TECHNOLOGY:
117 prop = POWER_SUPPLY_PROP_TECHNOLOGY;
118 break;
119 case GB_POWER_SUPPLY_PROP_CYCLE_COUNT:
120 prop = POWER_SUPPLY_PROP_CYCLE_COUNT;
121 break;
122 case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX:
123 prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
124 break;
125 case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN:
126 prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
127 break;
128 case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
129 prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
130 break;
131 case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
132 prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
133 break;
134 case GB_POWER_SUPPLY_PROP_VOLTAGE_NOW:
135 prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
136 break;
137 case GB_POWER_SUPPLY_PROP_VOLTAGE_AVG:
138 prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
139 break;
140 case GB_POWER_SUPPLY_PROP_VOLTAGE_OCV:
141 prop = POWER_SUPPLY_PROP_VOLTAGE_OCV;
142 break;
143 case GB_POWER_SUPPLY_PROP_VOLTAGE_BOOT:
144 prop = POWER_SUPPLY_PROP_VOLTAGE_BOOT;
145 break;
146 case GB_POWER_SUPPLY_PROP_CURRENT_MAX:
147 prop = POWER_SUPPLY_PROP_CURRENT_MAX;
148 break;
149 case GB_POWER_SUPPLY_PROP_CURRENT_NOW:
150 prop = POWER_SUPPLY_PROP_CURRENT_NOW;
151 break;
152 case GB_POWER_SUPPLY_PROP_CURRENT_AVG:
153 prop = POWER_SUPPLY_PROP_CURRENT_AVG;
154 break;
155 case GB_POWER_SUPPLY_PROP_CURRENT_BOOT:
156 prop = POWER_SUPPLY_PROP_CURRENT_BOOT;
157 break;
158 case GB_POWER_SUPPLY_PROP_POWER_NOW:
159 prop = POWER_SUPPLY_PROP_POWER_NOW;
160 break;
161 case GB_POWER_SUPPLY_PROP_POWER_AVG:
162 prop = POWER_SUPPLY_PROP_POWER_AVG;
163 break;
164 case GB_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
165 prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
166 break;
167 case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
168 prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
169 break;
170 case GB_POWER_SUPPLY_PROP_CHARGE_FULL:
171 prop = POWER_SUPPLY_PROP_CHARGE_FULL;
172 break;
173 case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY:
174 prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
175 break;
176 case GB_POWER_SUPPLY_PROP_CHARGE_NOW:
177 prop = POWER_SUPPLY_PROP_CHARGE_NOW;
178 break;
179 case GB_POWER_SUPPLY_PROP_CHARGE_AVG:
180 prop = POWER_SUPPLY_PROP_CHARGE_AVG;
181 break;
182 case GB_POWER_SUPPLY_PROP_CHARGE_COUNTER:
183 prop = POWER_SUPPLY_PROP_CHARGE_COUNTER;
184 break;
185 case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
186 prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT;
187 break;
188 case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
189 prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
190 break;
191 case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
192 prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
193 break;
194 case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
195 prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX;
196 break;
197 case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
198 prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
199 break;
200 case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
201 prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX;
202 break;
203 case GB_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
204 prop = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
205 break;
206 case GB_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
207 prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
208 break;
209 case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN:
210 prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
211 break;
212 case GB_POWER_SUPPLY_PROP_ENERGY_FULL:
213 prop = POWER_SUPPLY_PROP_ENERGY_FULL;
214 break;
215 case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY:
216 prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
217 break;
218 case GB_POWER_SUPPLY_PROP_ENERGY_NOW:
219 prop = POWER_SUPPLY_PROP_ENERGY_NOW;
220 break;
221 case GB_POWER_SUPPLY_PROP_ENERGY_AVG:
222 prop = POWER_SUPPLY_PROP_ENERGY_AVG;
223 break;
224 case GB_POWER_SUPPLY_PROP_CAPACITY:
225 prop = POWER_SUPPLY_PROP_CAPACITY;
226 break;
227 case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
228 prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN;
229 break;
230 case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX:
231 prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX;
232 break;
233 case GB_POWER_SUPPLY_PROP_CAPACITY_LEVEL:
234 prop = POWER_SUPPLY_PROP_CAPACITY_LEVEL;
235 break;
236 case GB_POWER_SUPPLY_PROP_TEMP:
237 prop = POWER_SUPPLY_PROP_TEMP;
238 break;
239 case GB_POWER_SUPPLY_PROP_TEMP_MAX:
240 prop = POWER_SUPPLY_PROP_TEMP_MAX;
241 break;
242 case GB_POWER_SUPPLY_PROP_TEMP_MIN:
243 prop = POWER_SUPPLY_PROP_TEMP_MIN;
244 break;
245 case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
246 prop = POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
247 break;
248 case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
249 prop = POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
250 break;
251 case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT:
252 prop = POWER_SUPPLY_PROP_TEMP_AMBIENT;
253 break;
254 case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
255 prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
256 break;
257 case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
258 prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
259 break;
260 case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
261 prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW;
262 break;
263 case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
264 prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG;
265 break;
266 case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
267 prop = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW;
268 break;
269 case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
270 prop = POWER_SUPPLY_PROP_TIME_TO_FULL_AVG;
271 break;
272 case GB_POWER_SUPPLY_PROP_TYPE:
273 prop = POWER_SUPPLY_PROP_TYPE;
274 break;
275 case GB_POWER_SUPPLY_PROP_SCOPE:
276 prop = POWER_SUPPLY_PROP_SCOPE;
277 break;
278 case GB_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
279 prop = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT;
280 break;
281 case GB_POWER_SUPPLY_PROP_CALIBRATE:
282 prop = POWER_SUPPLY_PROP_CALIBRATE;
283 break;
284 default:
285 prop = -1;
286 break;
287 }
288
289 if (prop < 0)
290 return prop;
291
292 *psp = (enum power_supply_property)prop;
293
294 return 0;
295}
296
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000297static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy)
298{
299 return gbpsy->supplies->connection;
300}
301
302static struct gb_power_supply_prop *get_psy_prop(struct gb_power_supply *gbpsy,
303 enum power_supply_property psp)
304{
305 int i;
306
307 for (i = 0; i < gbpsy->properties_count; i++)
308 if (gbpsy->props[i].prop == psp)
309 return &gbpsy->props[i];
310 return NULL;
311}
312
313static int is_psy_prop_writeable(struct gb_power_supply *gbpsy,
314 enum power_supply_property psp)
315{
316 struct gb_power_supply_prop *prop;
317
318 prop = get_psy_prop(gbpsy, psp);
319 if (!prop)
320 return -ENOENT;
321 return prop->is_writeable ? 1 : 0;
322}
323
324static int is_prop_valint(enum power_supply_property psp)
325{
326 return ((psp < POWER_SUPPLY_PROP_MODEL_NAME) ? 1 : 0);
327}
328
329static void next_interval(struct gb_power_supply *gbpsy)
330{
331 if (gbpsy->update_interval == update_interval_max)
332 return;
333
334 /* do some exponential back-off in the update interval */
335 gbpsy->update_interval *= 2;
336 if (gbpsy->update_interval > update_interval_max)
337 gbpsy->update_interval = update_interval_max;
338}
339
Sandeep Patilf8811c72016-03-15 12:28:38 -0700340#ifndef CORE_OWNS_PSY_STRUCT
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000341static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
342{
343 power_supply_changed(&gbpsy->psy);
344}
345#else
346static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
347{
348 power_supply_changed(gbpsy->psy);
349}
350#endif
351
352static void check_changed(struct gb_power_supply *gbpsy,
353 struct gb_power_supply_prop *prop)
354{
355 const struct gb_power_supply_changes *psyc;
Rui Miguel Silva6e720c22016-07-13 14:11:19 +0100356 int val = prop->val;
357 int prev_val = prop->previous_val;
Rui Miguel Silvac4582f92016-08-16 22:31:55 +0100358 bool changed = false;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000359 int i;
360
361 for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) {
362 psyc = &psy_props_changes[i];
363 if (prop->prop == psyc->prop) {
364 if (!psyc->tolerance_change)
Rui Miguel Silvac4582f92016-08-16 22:31:55 +0100365 changed = true;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000366 else if (val < prev_val &&
367 prev_val - val > psyc->tolerance_change)
Rui Miguel Silvac4582f92016-08-16 22:31:55 +0100368 changed = true;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000369 else if (val > prev_val &&
370 val - prev_val > psyc->tolerance_change)
Rui Miguel Silvac4582f92016-08-16 22:31:55 +0100371 changed = true;
372
373 if (changed && psyc->prop_changed)
374 psyc->prop_changed(gbpsy, prop);
375
376 if (changed)
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000377 gbpsy->changed = true;
378 break;
379 }
380 }
381}
382
383static int total_props(struct gb_power_supply *gbpsy)
384{
385 /* this return the intval plus the strval properties */
386 return (gbpsy->properties_count + gbpsy->properties_count_str);
387}
388
389static void prop_append(struct gb_power_supply *gbpsy,
390 enum power_supply_property prop)
391{
392 enum power_supply_property *new_props_raw;
393
394 gbpsy->properties_count_str++;
395 new_props_raw = krealloc(gbpsy->props_raw, total_props(gbpsy) *
396 sizeof(enum power_supply_property),
397 GFP_KERNEL);
398 if (!new_props_raw)
399 return;
400 gbpsy->props_raw = new_props_raw;
401 gbpsy->props_raw[total_props(gbpsy) - 1] = prop;
402}
403
404static int __gb_power_supply_set_name(char *init_name, char *name, size_t len)
405{
406 unsigned int i = 0;
407 int ret = 0;
408 struct power_supply *psy;
409
410 if (!strlen(init_name))
411 init_name = "gb_power_supply";
412 strlcpy(name, init_name, len);
413
414 while ((ret < len) && (psy = power_supply_get_by_name(name))) {
415#ifdef PSY_HAVE_PUT
416 power_supply_put(psy);
417#endif
418 ret = snprintf(name, len, "%s_%u", init_name, ++i);
419 }
420 if (ret >= len)
421 return -ENOMEM;
422 return i;
423}
424
425static void _gb_power_supply_append_props(struct gb_power_supply *gbpsy)
426{
427 if (strlen(gbpsy->manufacturer))
428 prop_append(gbpsy, POWER_SUPPLY_PROP_MANUFACTURER);
429 if (strlen(gbpsy->model_name))
430 prop_append(gbpsy, POWER_SUPPLY_PROP_MODEL_NAME);
431 if (strlen(gbpsy->serial_number))
432 prop_append(gbpsy, POWER_SUPPLY_PROP_SERIAL_NUMBER);
433}
434
435static int gb_power_supply_description_get(struct gb_power_supply *gbpsy)
436{
437 struct gb_connection *connection = get_conn_from_psy(gbpsy);
438 struct gb_power_supply_get_description_request req;
439 struct gb_power_supply_get_description_response resp;
440 int ret;
441
442 req.psy_id = gbpsy->id;
443
444 ret = gb_operation_sync(connection,
445 GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION,
446 &req, sizeof(req), &resp, sizeof(resp));
447 if (ret < 0)
448 return ret;
449
450 gbpsy->manufacturer = kstrndup(resp.manufacturer, PROP_MAX, GFP_KERNEL);
451 if (!gbpsy->manufacturer)
452 return -ENOMEM;
453 gbpsy->model_name = kstrndup(resp.model, PROP_MAX, GFP_KERNEL);
454 if (!gbpsy->model_name)
455 return -ENOMEM;
456 gbpsy->serial_number = kstrndup(resp.serial_number, PROP_MAX,
457 GFP_KERNEL);
458 if (!gbpsy->serial_number)
459 return -ENOMEM;
460
461 gbpsy->type = le16_to_cpu(resp.type);
462 gbpsy->properties_count = resp.properties_count;
463
464 return 0;
465}
466
467static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy)
468{
469 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000470 struct gb_power_supply_get_property_descriptors_request *req;
471 struct gb_power_supply_get_property_descriptors_response *resp;
472 struct gb_operation *op;
473 u8 props_count = gbpsy->properties_count;
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100474 enum power_supply_property psp;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000475 int ret;
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100476 int i, r = 0;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000477
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000478 if (props_count == 0)
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000479 return 0;
480
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000481 op = gb_operation_create(connection,
482 GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS,
483 sizeof(req), sizeof(*resp) + props_count *
484 sizeof(struct gb_power_supply_props_desc),
485 GFP_KERNEL);
486 if (!op)
487 return -ENOMEM;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000488
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000489 req = op->request->payload;
490 req->psy_id = gbpsy->id;
491
492 ret = gb_operation_request_send_sync(op);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000493 if (ret < 0)
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000494 goto out_put_operation;
495
496 resp = op->response->payload;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000497
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100498 /* validate received properties */
499 for (i = 0; i < props_count; i++) {
500 ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
501 if (ret < 0) {
502 dev_warn(&connection->bundle->dev,
503 "greybus property %u it is not supported by this kernel, dropped\n",
504 resp->props[i].property);
505 gbpsy->properties_count--;
506 }
507 }
508
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000509 gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props),
510 GFP_KERNEL);
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000511 if (!gbpsy->props) {
512 ret = -ENOMEM;
513 goto out_put_operation;
514 }
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000515
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000516 gbpsy->props_raw = kcalloc(gbpsy->properties_count,
517 sizeof(*gbpsy->props_raw), GFP_KERNEL);
518 if (!gbpsy->props_raw) {
519 ret = -ENOMEM;
520 goto out_put_operation;
521 }
522
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100523 /* Store available properties, skip the ones we do not support */
524 for (i = 0; i < props_count; i++) {
525 ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
526 if (ret < 0) {
527 r++;
528 continue;
529 }
530 gbpsy->props[i - r].prop = psp;
531 gbpsy->props[i - r].gb_prop = resp->props[i].property;
532 gbpsy->props_raw[i - r] = psp;
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000533 if (resp->props[i].is_writeable)
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100534 gbpsy->props[i - r].is_writeable = true;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000535 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800536
537 /*
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000538 * now append the properties that we already got information in the
539 * get_description operation. (char * ones)
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800540 */
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000541 _gb_power_supply_append_props(gbpsy);
542
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100543 ret = 0;
Rui Miguel Silva9d151342016-02-04 14:00:36 +0000544out_put_operation:
545 gb_operation_put(op);
546
547 return ret;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800548}
549
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000550static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy,
551 enum power_supply_property psp)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700552{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000553 struct gb_connection *connection = get_conn_from_psy(gbpsy);
554 struct gb_power_supply_prop *prop;
555 struct gb_power_supply_get_property_request req;
556 struct gb_power_supply_get_property_response resp;
Rui Miguel Silva6e720c22016-07-13 14:11:19 +0100557 int val;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000558 int ret;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800559
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000560 prop = get_psy_prop(gbpsy, psp);
561 if (!prop)
562 return -EINVAL;
563 req.psy_id = gbpsy->id;
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100564 req.property = prop->gb_prop;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000565
566 ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY,
567 &req, sizeof(req), &resp, sizeof(resp));
568 if (ret < 0)
569 return ret;
570
571 val = le32_to_cpu(resp.prop_val);
572 if (val == prop->val)
573 return 0;
574
575 prop->previous_val = prop->val;
576 prop->val = val;
577
578 check_changed(gbpsy, prop);
579
580 return 0;
581}
582
583static int __gb_power_supply_property_get(struct gb_power_supply *gbpsy,
584 enum power_supply_property psp,
585 union power_supply_propval *val)
586{
587 struct gb_power_supply_prop *prop;
588
589 prop = get_psy_prop(gbpsy, psp);
590 if (!prop)
591 return -EINVAL;
592
593 val->intval = prop->val;
594 return 0;
595}
596
597static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy,
598 enum power_supply_property psp,
599 union power_supply_propval *val)
600{
601 switch (psp) {
602 case POWER_SUPPLY_PROP_MODEL_NAME:
Rui Miguel Silvaf921fb12016-01-08 13:53:46 +0000603 val->strval = gbpsy->model_name;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000604 break;
605 case POWER_SUPPLY_PROP_MANUFACTURER:
Rui Miguel Silvaf921fb12016-01-08 13:53:46 +0000606 val->strval = gbpsy->manufacturer;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000607 break;
608 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
Rui Miguel Silvaf921fb12016-01-08 13:53:46 +0000609 val->strval = gbpsy->serial_number;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000610 break;
611 default:
612 break;
613 }
614
615 return 0;
616}
617
618static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy,
619 enum power_supply_property psp,
620 union power_supply_propval *val)
621{
622 struct gb_connection *connection = get_conn_from_psy(gbpsy);
623 int ret;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800624
625 /*
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000626 * Properties of type const char *, were already fetched on
627 * get_description operation and should be cached in gb
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800628 */
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000629 if (is_prop_valint(psp))
630 ret = __gb_power_supply_property_get(gbpsy, psp, val);
631 else
632 ret = __gb_power_supply_property_strval_get(gbpsy, psp, val);
633
634 if (ret < 0)
635 dev_err(&connection->bundle->dev, "get property %u\n", psp);
636
637 return 0;
638}
639
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +0100640static int is_cache_valid(struct gb_power_supply *gbpsy)
641{
642 /* check if cache is good enough or it has expired */
643 if (gbpsy->cache_invalid) {
644 gbpsy->cache_invalid = 0;
645 return 0;
646 }
647
648 if (gbpsy->last_update &&
649 time_is_after_jiffies(gbpsy->last_update +
650 msecs_to_jiffies(cache_time)))
651 return 1;
652
653 return 0;
654}
655
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000656static int gb_power_supply_status_get(struct gb_power_supply *gbpsy)
657{
658 int ret = 0;
659 int i;
660
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +0100661 if (is_cache_valid(gbpsy))
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000662 return 0;
663
664 for (i = 0; i < gbpsy->properties_count; i++) {
665 ret = __gb_power_supply_property_update(gbpsy,
666 gbpsy->props[i].prop);
667 if (ret < 0)
668 break;
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800669 }
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000670
671 if (ret == 0)
672 gbpsy->last_update = jiffies;
673
674 return ret;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700675}
676
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000677static void gb_power_supply_status_update(struct gb_power_supply *gbpsy)
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800678{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000679 /* check if there a change that need to be reported */
680 gb_power_supply_status_get(gbpsy);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800681
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000682 if (!gbpsy->changed)
683 return;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800684
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000685 gbpsy->update_interval = update_interval_init;
686 __gb_power_supply_changed(gbpsy);
687 gbpsy->changed = false;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800688}
689
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000690static void gb_power_supply_work(struct work_struct *work)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700691{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000692 struct gb_power_supply *gbpsy = container_of(work,
693 struct gb_power_supply,
694 work.work);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800695
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000696 /*
697 * if the poll interval is not set, disable polling, this is helpful
698 * specially at unregister time.
699 */
700 if (!gbpsy->update_interval)
701 return;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800702
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000703 gb_power_supply_status_update(gbpsy);
704 next_interval(gbpsy);
705 schedule_delayed_work(&gbpsy->work, gbpsy->update_interval);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700706}
707
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700708static int get_property(struct power_supply *b,
709 enum power_supply_property psp,
710 union power_supply_propval *val)
711{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000712 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700713
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000714 gb_power_supply_status_get(gbpsy);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700715
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000716 return _gb_power_supply_property_get(gbpsy, psp, val);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700717}
718
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000719static int gb_power_supply_property_set(struct gb_power_supply *gbpsy,
720 enum power_supply_property psp,
721 int val)
722{
723 struct gb_connection *connection = get_conn_from_psy(gbpsy);
724 struct gb_power_supply_prop *prop;
725 struct gb_power_supply_set_property_request req;
726 int ret;
727
728 prop = get_psy_prop(gbpsy, psp);
729 if (!prop)
730 return -EINVAL;
731 req.psy_id = gbpsy->id;
Rui Miguel Silva47becc52016-07-13 14:11:20 +0100732 req.property = prop->gb_prop;
Rui Miguel Silva6e720c22016-07-13 14:11:19 +0100733 req.prop_val = cpu_to_le32((s32)val);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000734
735 ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY,
736 &req, sizeof(req), NULL, 0);
737 if (ret < 0)
738 goto out;
739
740 /* cache immediately the new value */
741 prop->val = val;
742
743out:
744 return ret;
745}
746
747static int set_property(struct power_supply *b,
748 enum power_supply_property psp,
749 const union power_supply_propval *val)
750{
751 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
752
753 return gb_power_supply_property_set(gbpsy, psp, val->intval);
754}
755
756static int property_is_writeable(struct power_supply *b,
757 enum power_supply_property psp)
758{
759 struct gb_power_supply *gbpsy = to_gb_power_supply(b);
760
761 return is_psy_prop_writeable(gbpsy, psp);
762}
763
Sandeep Patilf8811c72016-03-15 12:28:38 -0700764#ifndef CORE_OWNS_PSY_STRUCT
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000765static int gb_power_supply_register(struct gb_power_supply *gbpsy)
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200766{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000767 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200768
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000769 gbpsy->psy.name = gbpsy->name;
770 gbpsy->psy.type = gbpsy->type;
771 gbpsy->psy.properties = gbpsy->props_raw;
772 gbpsy->psy.num_properties = total_props(gbpsy);
773 gbpsy->psy.get_property = get_property;
774 gbpsy->psy.set_property = set_property;
775 gbpsy->psy.property_is_writeable = property_is_writeable;
776
777 return power_supply_register(&connection->bundle->dev,
778 &gbpsy->psy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200779}
780#else
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000781static int gb_power_supply_register(struct gb_power_supply *gbpsy)
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200782{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000783 struct gb_connection *connection = get_conn_from_psy(gbpsy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200784 struct power_supply_config cfg = {};
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200785
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000786 cfg.drv_data = gbpsy;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200787
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000788 gbpsy->desc.name = gbpsy->name;
789 gbpsy->desc.type = gbpsy->type;
790 gbpsy->desc.properties = gbpsy->props_raw;
791 gbpsy->desc.num_properties = total_props(gbpsy);
792 gbpsy->desc.get_property = get_property;
793 gbpsy->desc.set_property = set_property;
794 gbpsy->desc.property_is_writeable = property_is_writeable;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200795
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000796 gbpsy->psy = power_supply_register(&connection->bundle->dev,
797 &gbpsy->desc, &cfg);
Alex Elder95073cc2016-05-24 13:34:51 -0500798 return PTR_ERR_OR_ZERO(gbpsy->psy);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200799}
800#endif
801
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000802static void _gb_power_supply_free(struct gb_power_supply *gbpsy)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700803{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000804 kfree(gbpsy->serial_number);
805 kfree(gbpsy->model_name);
806 kfree(gbpsy->manufacturer);
807 kfree(gbpsy->props_raw);
808 kfree(gbpsy->props);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000809}
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700810
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000811static void _gb_power_supply_release(struct gb_power_supply *gbpsy)
812{
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000813 gbpsy->update_interval = 0;
814
815 cancel_delayed_work_sync(&gbpsy->work);
Sandeep Patilf8811c72016-03-15 12:28:38 -0700816#ifndef CORE_OWNS_PSY_STRUCT
Rui Miguel Silvaff85f722016-01-08 13:53:43 +0000817 if (gbpsy->registered)
818 power_supply_unregister(&gbpsy->psy);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000819#else
Rui Miguel Silvaff85f722016-01-08 13:53:43 +0000820 if (gbpsy->registered)
821 power_supply_unregister(gbpsy->psy);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000822#endif
823
824 _gb_power_supply_free(gbpsy);
825}
826
827static void _gb_power_supplies_release(struct gb_power_supplies *supplies)
828{
829 int i;
830
Rui Miguel Silva7ccac20d2016-01-08 13:53:42 +0000831 if (!supplies->supply)
832 return;
833
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000834 mutex_lock(&supplies->supplies_lock);
835 for (i = 0; i < supplies->supplies_count; i++)
836 _gb_power_supply_release(&supplies->supply[i]);
Rui Miguel Silvaaccad1b2016-01-08 13:53:47 +0000837 kfree(supplies->supply);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000838 mutex_unlock(&supplies->supplies_lock);
Rui Miguel Silva23f25ba2016-01-08 13:53:44 +0000839 kfree(supplies);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000840}
841
842static int gb_power_supplies_get_count(struct gb_power_supplies *supplies)
843{
844 struct gb_power_supply_get_supplies_response resp;
845 int ret;
846
847 ret = gb_operation_sync(supplies->connection,
848 GB_POWER_SUPPLY_TYPE_GET_SUPPLIES,
849 NULL, 0, &resp, sizeof(resp));
850 if (ret < 0)
851 return ret;
852
853 if (!resp.supplies_count)
854 return -EINVAL;
855
856 supplies->supplies_count = resp.supplies_count;
857
858 return ret;
859}
860
861static int gb_power_supply_config(struct gb_power_supplies *supplies, int id)
862{
863 struct gb_power_supply *gbpsy = &supplies->supply[id];
864 int ret;
865
866 gbpsy->supplies = supplies;
867 gbpsy->id = id;
868
869 ret = gb_power_supply_description_get(gbpsy);
870 if (ret < 0)
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530871 return ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000872
873 ret = gb_power_supply_prop_descriptors_get(gbpsy);
874 if (ret < 0)
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530875 return ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000876
877 /* guarantee that we have an unique name, before register */
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530878 return __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name,
879 sizeof(gbpsy->name));
880}
881
882static int gb_power_supply_enable(struct gb_power_supply *gbpsy)
883{
884 int ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000885
886 ret = gb_power_supply_register(gbpsy);
887 if (ret < 0)
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530888 return ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000889
890 gbpsy->update_interval = update_interval_init;
891 INIT_DELAYED_WORK(&gbpsy->work, gb_power_supply_work);
892 schedule_delayed_work(&gbpsy->work, 0);
893
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530894 /* everything went fine, mark it for release code to know */
895 gbpsy->registered = true;
896
897 return 0;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000898}
899
900static int gb_power_supplies_setup(struct gb_power_supplies *supplies)
901{
902 struct gb_connection *connection = supplies->connection;
903 int ret;
904 int i;
905
906 mutex_lock(&supplies->supplies_lock);
907
908 ret = gb_power_supplies_get_count(supplies);
909 if (ret < 0)
910 goto out;
911
912 supplies->supply = kzalloc(supplies->supplies_count *
913 sizeof(struct gb_power_supply),
914 GFP_KERNEL);
915
Johan Hovolde0d91ff2016-01-07 12:28:29 +0100916 if (!supplies->supply) {
917 ret = -ENOMEM;
918 goto out;
919 }
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700920
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000921 for (i = 0; i < supplies->supplies_count; i++) {
922 ret = gb_power_supply_config(supplies, i);
923 if (ret < 0) {
924 dev_err(&connection->bundle->dev,
925 "Fail to configure supplies devices\n");
926 goto out;
927 }
928 }
929out:
930 mutex_unlock(&supplies->supplies_lock);
931 return ret;
932}
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +0800933
Viresh Kumar7e9fba82016-02-12 16:08:32 +0530934static int gb_power_supplies_register(struct gb_power_supplies *supplies)
935{
936 struct gb_connection *connection = supplies->connection;
937 int ret = 0;
938 int i;
939
940 mutex_lock(&supplies->supplies_lock);
941
942 for (i = 0; i < supplies->supplies_count; i++) {
943 ret = gb_power_supply_enable(&supplies->supply[i]);
944 if (ret < 0) {
945 dev_err(&connection->bundle->dev,
946 "Fail to enable supplies devices\n");
947 break;
948 }
949 }
950
951 mutex_unlock(&supplies->supplies_lock);
952 return ret;
953}
954
Viresh Kumar68b13092016-02-12 16:08:33 +0530955static int gb_supplies_request_handler(struct gb_operation *op)
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000956{
957 struct gb_connection *connection = op->connection;
Greg Kroah-Hartman0ec30632016-03-22 14:30:35 -0400958 struct gb_power_supplies *supplies = gb_connection_get_data(connection);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000959 struct gb_power_supply *gbpsy;
960 struct gb_message *request;
961 struct gb_power_supply_event_request *payload;
962 u8 psy_id;
963 u8 event;
964 int ret = 0;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800965
Viresh Kumar68b13092016-02-12 16:08:33 +0530966 if (op->type != GB_POWER_SUPPLY_TYPE_EVENT) {
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000967 dev_err(&connection->bundle->dev,
Viresh Kumar68b13092016-02-12 16:08:33 +0530968 "Unsupported unsolicited event: %u\n", op->type);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000969 return -EINVAL;
970 }
971
972 request = op->request;
973
974 if (request->payload_size < sizeof(*payload)) {
975 dev_err(&connection->bundle->dev,
976 "Wrong event size received (%zu < %zu)\n",
977 request->payload_size, sizeof(*payload));
978 return -EINVAL;
979 }
980
981 payload = request->payload;
982 psy_id = payload->psy_id;
983 mutex_lock(&supplies->supplies_lock);
Rui Miguel Silvaadb57cf2016-01-12 14:38:21 +0000984 if (psy_id >= supplies->supplies_count ||
985 !supplies->supply[psy_id].registered) {
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +0000986 dev_err(&connection->bundle->dev,
987 "Event received for unconfigured power_supply id: %d\n",
988 psy_id);
989 ret = -EINVAL;
990 goto out_unlock;
991 }
992
993 event = payload->event;
994 /*
995 * we will only handle events after setup is done and before release is
996 * running. For that just check update_interval.
997 */
998 gbpsy = &supplies->supply[psy_id];
Rui Miguel Silva5f66d622016-08-16 22:31:56 +0100999 if (!gbpsy->update_interval) {
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001000 ret = -ESHUTDOWN;
1001 goto out_unlock;
1002 }
1003
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +01001004 if (event & GB_POWER_SUPPLY_UPDATE) {
1005 /*
1006 * we need to make sure we invalidate cache, if not no new
1007 * values for the properties will be fetch and the all propose
1008 * of this event is missed
1009 */
1010 gbpsy->cache_invalid = 1;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001011 gb_power_supply_status_update(gbpsy);
Rui Miguel Silvab5fbe812016-08-16 22:31:57 +01001012 }
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001013
1014out_unlock:
1015 mutex_unlock(&supplies->supplies_lock);
1016 return ret;
1017}
1018
Viresh Kumar68b13092016-02-12 16:08:33 +05301019static int gb_power_supply_probe(struct gb_bundle *bundle,
1020 const struct greybus_bundle_id *id)
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001021{
Viresh Kumar68b13092016-02-12 16:08:33 +05301022 struct greybus_descriptor_cport *cport_desc;
1023 struct gb_connection *connection;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001024 struct gb_power_supplies *supplies;
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +00001025 int ret;
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001026
Viresh Kumar68b13092016-02-12 16:08:33 +05301027 if (bundle->num_cports != 1)
1028 return -ENODEV;
1029
1030 cport_desc = &bundle->cport_desc[0];
1031 if (cport_desc->protocol_id != GREYBUS_PROTOCOL_POWER_SUPPLY)
1032 return -ENODEV;
1033
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001034 supplies = kzalloc(sizeof(*supplies), GFP_KERNEL);
1035 if (!supplies)
1036 return -ENOMEM;
1037
Viresh Kumar68b13092016-02-12 16:08:33 +05301038 connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
1039 gb_supplies_request_handler);
1040 if (IS_ERR(connection)) {
1041 ret = PTR_ERR(connection);
1042 goto out;
1043 }
1044
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001045 supplies->connection = connection;
Greg Kroah-Hartman0ec30632016-03-22 14:30:35 -04001046 gb_connection_set_data(connection, supplies);
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001047
1048 mutex_init(&supplies->supplies_lock);
1049
Viresh Kumar68b13092016-02-12 16:08:33 +05301050 greybus_set_drvdata(bundle, supplies);
1051
1052 /* We aren't ready to receive an incoming request yet */
1053 ret = gb_connection_enable_tx(connection);
1054 if (ret)
1055 goto error_connection_destroy;
1056
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +00001057 ret = gb_power_supplies_setup(supplies);
1058 if (ret < 0)
Viresh Kumar68b13092016-02-12 16:08:33 +05301059 goto error_connection_disable;
1060
1061 /* We are ready to receive an incoming request now, enable RX as well */
1062 ret = gb_connection_enable(connection);
1063 if (ret)
1064 goto error_connection_disable;
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +00001065
Viresh Kumar7e9fba82016-02-12 16:08:32 +05301066 ret = gb_power_supplies_register(supplies);
1067 if (ret < 0)
Viresh Kumar68b13092016-02-12 16:08:33 +05301068 goto error_connection_disable;
Viresh Kumar7e9fba82016-02-12 16:08:32 +05301069
1070 return 0;
1071
Viresh Kumar68b13092016-02-12 16:08:33 +05301072error_connection_disable:
1073 gb_connection_disable(connection);
1074error_connection_destroy:
1075 gb_connection_destroy(connection);
Viresh Kumar7e9fba82016-02-12 16:08:32 +05301076out:
1077 _gb_power_supplies_release(supplies);
Rui Miguel Silvad9eafd52016-01-08 13:53:45 +00001078 return ret;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -07001079}
1080
Viresh Kumar68b13092016-02-12 16:08:33 +05301081static void gb_power_supply_disconnect(struct gb_bundle *bundle)
Alex Elder697e55d2014-10-20 23:01:04 -05001082{
Viresh Kumar68b13092016-02-12 16:08:33 +05301083 struct gb_power_supplies *supplies = greybus_get_drvdata(bundle);
1084
1085 gb_connection_disable(supplies->connection);
1086 gb_connection_destroy(supplies->connection);
Alex Elder697e55d2014-10-20 23:01:04 -05001087
Rui Miguel Silvaffe2e242015-11-12 15:36:02 +00001088 _gb_power_supplies_release(supplies);
Alex Elder697e55d2014-10-20 23:01:04 -05001089}
1090
Viresh Kumar68b13092016-02-12 16:08:33 +05301091static const struct greybus_bundle_id gb_power_supply_id_table[] = {
1092 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) },
1093 { }
Alex Elder19d03de2014-11-05 16:12:53 -06001094};
Viresh Kumar68b13092016-02-12 16:08:33 +05301095MODULE_DEVICE_TABLE(greybus, gb_power_supply_id_table);
Alex Elder19d03de2014-11-05 16:12:53 -06001096
Viresh Kumar68b13092016-02-12 16:08:33 +05301097static struct greybus_driver gb_power_supply_driver = {
1098 .name = "power_supply",
1099 .probe = gb_power_supply_probe,
1100 .disconnect = gb_power_supply_disconnect,
1101 .id_table = gb_power_supply_id_table,
1102};
1103module_greybus_driver(gb_power_supply_driver);
Alex Elder19d03de2014-11-05 16:12:53 -06001104
Greg Kroah-Hartman7dd26262014-12-24 13:01:42 -08001105MODULE_LICENSE("GPL v2");