146488841SRui Miguel Silva /*
22724be03SRui Miguel Silva  * Power Supply driver for a Greybus module.
346488841SRui Miguel Silva  *
4ffe2e248SRui Miguel Silva  * Copyright 2014-2015 Google Inc.
5ffe2e248SRui Miguel Silva  * Copyright 2014-2015 Linaro Ltd.
646488841SRui Miguel Silva  *
746488841SRui Miguel Silva  * Released under the GPLv2 only.
846488841SRui Miguel Silva  */
946488841SRui Miguel Silva 
1046488841SRui Miguel Silva #include <linux/kernel.h>
1146488841SRui Miguel Silva #include <linux/module.h>
1246488841SRui Miguel Silva #include <linux/power_supply.h>
13ffe2e248SRui Miguel Silva #include <linux/slab.h>
14ffe2e248SRui Miguel Silva 
1546488841SRui Miguel Silva #include "greybus.h"
1646488841SRui Miguel Silva 
17ffe2e248SRui Miguel Silva #define PROP_MAX 32
18ffe2e248SRui Miguel Silva 
19ffe2e248SRui Miguel Silva struct gb_power_supply_prop {
20ffe2e248SRui Miguel Silva 	enum power_supply_property	prop;
2147becc55SRui Miguel Silva 	u8				gb_prop;
226e720c27SRui Miguel Silva 	int				val;
236e720c27SRui Miguel Silva 	int				previous_val;
24ffe2e248SRui Miguel Silva 	bool				is_writeable;
25ffe2e248SRui Miguel Silva };
26ffe2e248SRui Miguel Silva 
272724be03SRui Miguel Silva struct gb_power_supply {
28ffe2e248SRui Miguel Silva 	u8				id;
29ff85f723SRui Miguel Silva 	bool				registered;
30f8811c76SSandeep Patil #ifndef CORE_OWNS_PSY_STRUCT
312724be03SRui Miguel Silva 	struct power_supply		psy;
322724be03SRui Miguel Silva #define to_gb_power_supply(x) container_of(x, struct gb_power_supply, psy)
3346488841SRui Miguel Silva #else
342724be03SRui Miguel Silva 	struct power_supply		*psy;
3546488841SRui Miguel Silva 	struct power_supply_desc	desc;
362724be03SRui Miguel Silva #define to_gb_power_supply(x) power_supply_get_drvdata(x)
3746488841SRui Miguel Silva #endif
38ffe2e248SRui Miguel Silva 	char				name[64];
39ffe2e248SRui Miguel Silva 	struct gb_power_supplies	*supplies;
40ffe2e248SRui Miguel Silva 	struct delayed_work		work;
41ffe2e248SRui Miguel Silva 	char				*manufacturer;
42ffe2e248SRui Miguel Silva 	char				*model_name;
43ffe2e248SRui Miguel Silva 	char				*serial_number;
44ffe2e248SRui Miguel Silva 	u8				type;
45ffe2e248SRui Miguel Silva 	u8				properties_count;
46ffe2e248SRui Miguel Silva 	u8				properties_count_str;
47ffe2e248SRui Miguel Silva 	unsigned long			last_update;
48b5fbe819SRui Miguel Silva 	u8				cache_invalid;
49ffe2e248SRui Miguel Silva 	unsigned int			update_interval;
50ffe2e248SRui Miguel Silva 	bool				changed;
51ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop	*props;
52ffe2e248SRui Miguel Silva 	enum power_supply_property	*props_raw;
5346488841SRui Miguel Silva };
5446488841SRui Miguel Silva 
55ffe2e248SRui Miguel Silva struct gb_power_supplies {
56ffe2e248SRui Miguel Silva 	struct gb_connection	*connection;
57ffe2e248SRui Miguel Silva 	u8			supplies_count;
58ffe2e248SRui Miguel Silva 	struct gb_power_supply	*supply;
59ffe2e248SRui Miguel Silva 	struct mutex		supplies_lock;
60ffe2e248SRui Miguel Silva };
6146488841SRui Miguel Silva 
62ffe2e248SRui Miguel Silva /* cache time in milliseconds, if cache_time is set to 0 cache is disable */
63ffe2e248SRui Miguel Silva static unsigned int cache_time = 1000;
64ffe2e248SRui Miguel Silva /*
65ffe2e248SRui Miguel Silva  * update interval initial and maximum value, between the two will
66ffe2e248SRui Miguel Silva  * back-off exponential
67ffe2e248SRui Miguel Silva  */
68ffe2e248SRui Miguel Silva static unsigned int update_interval_init = 1 * HZ;
69ffe2e248SRui Miguel Silva static unsigned int update_interval_max = 30 * HZ;
70ffe2e248SRui Miguel Silva 
71ffe2e248SRui Miguel Silva struct gb_power_supply_changes {
72ffe2e248SRui Miguel Silva 	enum power_supply_property	prop;
73ffe2e248SRui Miguel Silva 	u32				tolerance_change;
74c4582f9dSRui Miguel Silva 	void (*prop_changed)(struct gb_power_supply *gbpsy,
75c4582f9dSRui Miguel Silva 			     struct gb_power_supply_prop *prop);
76ffe2e248SRui Miguel Silva };
77ffe2e248SRui Miguel Silva 
78ffe2e248SRui Miguel Silva static const struct gb_power_supply_changes psy_props_changes[] = {
79ffe2e248SRui Miguel Silva 	{	.prop			= GB_POWER_SUPPLY_PROP_STATUS,
80ffe2e248SRui Miguel Silva 		.tolerance_change	= 0,
81c4582f9dSRui Miguel Silva 		.prop_changed		= NULL,
82ffe2e248SRui Miguel Silva 	},
83ffe2e248SRui Miguel Silva 	{	.prop			= GB_POWER_SUPPLY_PROP_TEMP,
84ffe2e248SRui Miguel Silva 		.tolerance_change	= 500,
85c4582f9dSRui Miguel Silva 		.prop_changed		= NULL,
86ffe2e248SRui Miguel Silva 	},
87ffe2e248SRui Miguel Silva 	{	.prop			= GB_POWER_SUPPLY_PROP_ONLINE,
88ffe2e248SRui Miguel Silva 		.tolerance_change	= 0,
89c4582f9dSRui Miguel Silva 		.prop_changed		= NULL,
90ffe2e248SRui Miguel Silva 	},
91ffe2e248SRui Miguel Silva };
92ffe2e248SRui Miguel Silva 
9347becc55SRui Miguel Silva static int get_psp_from_gb_prop(int gb_prop, enum power_supply_property *psp)
9447becc55SRui Miguel Silva {
9547becc55SRui Miguel Silva 	int prop;
9647becc55SRui Miguel Silva 
9747becc55SRui Miguel Silva 	switch (gb_prop) {
9847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_STATUS:
9947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_STATUS;
10047becc55SRui Miguel Silva 		break;
10147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_TYPE:
10247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_TYPE;
10347becc55SRui Miguel Silva 		break;
10447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_HEALTH:
10547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_HEALTH;
10647becc55SRui Miguel Silva 		break;
10747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_PRESENT:
10847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_PRESENT;
10947becc55SRui Miguel Silva 		break;
11047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ONLINE:
11147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ONLINE;
11247becc55SRui Miguel Silva 		break;
11347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_AUTHENTIC:
11447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_AUTHENTIC;
11547becc55SRui Miguel Silva 		break;
11647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TECHNOLOGY:
11747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TECHNOLOGY;
11847becc55SRui Miguel Silva 		break;
11947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CYCLE_COUNT:
12047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CYCLE_COUNT;
12147becc55SRui Miguel Silva 		break;
12247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX:
12347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
12447becc55SRui Miguel Silva 		break;
12547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN:
12647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
12747becc55SRui Miguel Silva 		break;
12847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
12947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
13047becc55SRui Miguel Silva 		break;
13147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
13247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
13347becc55SRui Miguel Silva 		break;
13447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_NOW:
13547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
13647becc55SRui Miguel Silva 		break;
13747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_AVG:
13847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
13947becc55SRui Miguel Silva 		break;
14047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_OCV:
14147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_OCV;
14247becc55SRui Miguel Silva 		break;
14347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_BOOT:
14447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_BOOT;
14547becc55SRui Miguel Silva 		break;
14647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CURRENT_MAX:
14747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CURRENT_MAX;
14847becc55SRui Miguel Silva 		break;
14947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CURRENT_NOW:
15047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CURRENT_NOW;
15147becc55SRui Miguel Silva 		break;
15247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CURRENT_AVG:
15347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CURRENT_AVG;
15447becc55SRui Miguel Silva 		break;
15547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CURRENT_BOOT:
15647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CURRENT_BOOT;
15747becc55SRui Miguel Silva 		break;
15847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_POWER_NOW:
15947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_POWER_NOW;
16047becc55SRui Miguel Silva 		break;
16147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_POWER_AVG:
16247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_POWER_AVG;
16347becc55SRui Miguel Silva 		break;
16447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
16547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
16647becc55SRui Miguel Silva 		break;
16747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
16847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
16947becc55SRui Miguel Silva 		break;
17047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_FULL:
17147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_FULL;
17247becc55SRui Miguel Silva 		break;
17347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY:
17447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
17547becc55SRui Miguel Silva 		break;
17647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_NOW:
17747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_NOW;
17847becc55SRui Miguel Silva 		break;
17947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_AVG:
18047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_AVG;
18147becc55SRui Miguel Silva 		break;
18247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_COUNTER:
18347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_COUNTER;
18447becc55SRui Miguel Silva 		break;
18547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
18647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT;
18747becc55SRui Miguel Silva 		break;
18847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
18947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
19047becc55SRui Miguel Silva 		break;
19147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
19247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
19347becc55SRui Miguel Silva 		break;
19447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
19547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX;
19647becc55SRui Miguel Silva 		break;
19747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
19847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
19947becc55SRui Miguel Silva 		break;
20047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
20147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX;
20247becc55SRui Miguel Silva 		break;
20347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
20447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
20547becc55SRui Miguel Silva 		break;
20647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
20747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
20847becc55SRui Miguel Silva 		break;
20947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN:
21047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
21147becc55SRui Miguel Silva 		break;
21247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_FULL:
21347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_FULL;
21447becc55SRui Miguel Silva 		break;
21547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY:
21647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
21747becc55SRui Miguel Silva 		break;
21847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_NOW:
21947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_NOW;
22047becc55SRui Miguel Silva 		break;
22147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_AVG:
22247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_AVG;
22347becc55SRui Miguel Silva 		break;
22447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CAPACITY:
22547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CAPACITY;
22647becc55SRui Miguel Silva 		break;
22747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
22847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN;
22947becc55SRui Miguel Silva 		break;
23047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX:
23147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX;
23247becc55SRui Miguel Silva 		break;
23347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CAPACITY_LEVEL:
23447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CAPACITY_LEVEL;
23547becc55SRui Miguel Silva 		break;
23647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP:
23747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP;
23847becc55SRui Miguel Silva 		break;
23947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_MAX:
24047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_MAX;
24147becc55SRui Miguel Silva 		break;
24247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_MIN:
24347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_MIN;
24447becc55SRui Miguel Silva 		break;
24547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
24647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
24747becc55SRui Miguel Silva 		break;
24847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
24947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
25047becc55SRui Miguel Silva 		break;
25147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT:
25247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_AMBIENT;
25347becc55SRui Miguel Silva 		break;
25447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
25547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
25647becc55SRui Miguel Silva 		break;
25747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
25847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
25947becc55SRui Miguel Silva 		break;
26047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
26147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW;
26247becc55SRui Miguel Silva 		break;
26347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
26447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG;
26547becc55SRui Miguel Silva 		break;
26647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
26747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW;
26847becc55SRui Miguel Silva 		break;
26947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
27047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TIME_TO_FULL_AVG;
27147becc55SRui Miguel Silva 		break;
27247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TYPE:
27347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TYPE;
27447becc55SRui Miguel Silva 		break;
27547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_SCOPE:
27647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_SCOPE;
27747becc55SRui Miguel Silva 		break;
27847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
27947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT;
28047becc55SRui Miguel Silva 		break;
28147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CALIBRATE:
28247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CALIBRATE;
28347becc55SRui Miguel Silva 		break;
28447becc55SRui Miguel Silva 	default:
28547becc55SRui Miguel Silva 		prop = -1;
28647becc55SRui Miguel Silva 		break;
28747becc55SRui Miguel Silva 	}
28847becc55SRui Miguel Silva 
28947becc55SRui Miguel Silva 	if (prop < 0)
29047becc55SRui Miguel Silva 		return prop;
29147becc55SRui Miguel Silva 
29247becc55SRui Miguel Silva 	*psp = (enum power_supply_property)prop;
29347becc55SRui Miguel Silva 
29447becc55SRui Miguel Silva 	return 0;
29547becc55SRui Miguel Silva }
29647becc55SRui Miguel Silva 
297ffe2e248SRui Miguel Silva static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy)
298ffe2e248SRui Miguel Silva {
299ffe2e248SRui Miguel Silva 	return gbpsy->supplies->connection;
300ffe2e248SRui Miguel Silva }
301ffe2e248SRui Miguel Silva 
302ffe2e248SRui Miguel Silva static struct gb_power_supply_prop *get_psy_prop(struct gb_power_supply *gbpsy,
303ffe2e248SRui Miguel Silva 						 enum power_supply_property psp)
304ffe2e248SRui Miguel Silva {
305ffe2e248SRui Miguel Silva 	int i;
306ffe2e248SRui Miguel Silva 
307ffe2e248SRui Miguel Silva 	for (i = 0; i < gbpsy->properties_count; i++)
308ffe2e248SRui Miguel Silva 		if (gbpsy->props[i].prop == psp)
309ffe2e248SRui Miguel Silva 			return &gbpsy->props[i];
310ffe2e248SRui Miguel Silva 	return NULL;
311ffe2e248SRui Miguel Silva }
312ffe2e248SRui Miguel Silva 
313ffe2e248SRui Miguel Silva static int is_psy_prop_writeable(struct gb_power_supply *gbpsy,
314ffe2e248SRui Miguel Silva 				     enum power_supply_property psp)
315ffe2e248SRui Miguel Silva {
316ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop *prop;
317ffe2e248SRui Miguel Silva 
318ffe2e248SRui Miguel Silva 	prop = get_psy_prop(gbpsy, psp);
319ffe2e248SRui Miguel Silva 	if (!prop)
320ffe2e248SRui Miguel Silva 		return -ENOENT;
321ffe2e248SRui Miguel Silva 	return prop->is_writeable ? 1 : 0;
322ffe2e248SRui Miguel Silva }
323ffe2e248SRui Miguel Silva 
324ffe2e248SRui Miguel Silva static int is_prop_valint(enum power_supply_property psp)
325ffe2e248SRui Miguel Silva {
326ffe2e248SRui Miguel Silva 	return ((psp < POWER_SUPPLY_PROP_MODEL_NAME) ? 1 : 0);
327ffe2e248SRui Miguel Silva }
328ffe2e248SRui Miguel Silva 
329ffe2e248SRui Miguel Silva static void next_interval(struct gb_power_supply *gbpsy)
330ffe2e248SRui Miguel Silva {
331ffe2e248SRui Miguel Silva 	if (gbpsy->update_interval == update_interval_max)
332ffe2e248SRui Miguel Silva 		return;
333ffe2e248SRui Miguel Silva 
334ffe2e248SRui Miguel Silva 	/* do some exponential back-off in the update interval */
335ffe2e248SRui Miguel Silva 	gbpsy->update_interval *= 2;
336ffe2e248SRui Miguel Silva 	if (gbpsy->update_interval > update_interval_max)
337ffe2e248SRui Miguel Silva 		gbpsy->update_interval = update_interval_max;
338ffe2e248SRui Miguel Silva }
339ffe2e248SRui Miguel Silva 
340f8811c76SSandeep Patil #ifndef CORE_OWNS_PSY_STRUCT
341ffe2e248SRui Miguel Silva static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
342ffe2e248SRui Miguel Silva {
343ffe2e248SRui Miguel Silva 	power_supply_changed(&gbpsy->psy);
344ffe2e248SRui Miguel Silva }
345ffe2e248SRui Miguel Silva #else
346ffe2e248SRui Miguel Silva static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
347ffe2e248SRui Miguel Silva {
348ffe2e248SRui Miguel Silva 	power_supply_changed(gbpsy->psy);
349ffe2e248SRui Miguel Silva }
350ffe2e248SRui Miguel Silva #endif
351ffe2e248SRui Miguel Silva 
352ffe2e248SRui Miguel Silva static void check_changed(struct gb_power_supply *gbpsy,
353ffe2e248SRui Miguel Silva 			  struct gb_power_supply_prop *prop)
354ffe2e248SRui Miguel Silva {
355ffe2e248SRui Miguel Silva 	const struct gb_power_supply_changes *psyc;
3566e720c27SRui Miguel Silva 	int val = prop->val;
3576e720c27SRui Miguel Silva 	int prev_val = prop->previous_val;
358c4582f9dSRui Miguel Silva 	bool changed = false;
359ffe2e248SRui Miguel Silva 	int i;
360ffe2e248SRui Miguel Silva 
361ffe2e248SRui Miguel Silva 	for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) {
362ffe2e248SRui Miguel Silva 		psyc = &psy_props_changes[i];
363ffe2e248SRui Miguel Silva 		if (prop->prop == psyc->prop) {
364ffe2e248SRui Miguel Silva 			if (!psyc->tolerance_change)
365c4582f9dSRui Miguel Silva 				changed = true;
366ffe2e248SRui Miguel Silva 			else if (val < prev_val &&
367ffe2e248SRui Miguel Silva 				 prev_val - val > psyc->tolerance_change)
368c4582f9dSRui Miguel Silva 				changed = true;
369ffe2e248SRui Miguel Silva 			else if (val > prev_val &&
370ffe2e248SRui Miguel Silva 				 val - prev_val > psyc->tolerance_change)
371c4582f9dSRui Miguel Silva 				changed = true;
372c4582f9dSRui Miguel Silva 
373c4582f9dSRui Miguel Silva 			if (changed && psyc->prop_changed)
374c4582f9dSRui Miguel Silva 				psyc->prop_changed(gbpsy, prop);
375c4582f9dSRui Miguel Silva 
376c4582f9dSRui Miguel Silva 			if (changed)
377ffe2e248SRui Miguel Silva 				gbpsy->changed = true;
378ffe2e248SRui Miguel Silva 			break;
379ffe2e248SRui Miguel Silva 		}
380ffe2e248SRui Miguel Silva 	}
381ffe2e248SRui Miguel Silva }
382ffe2e248SRui Miguel Silva 
383ffe2e248SRui Miguel Silva static int total_props(struct gb_power_supply *gbpsy)
384ffe2e248SRui Miguel Silva {
385ffe2e248SRui Miguel Silva 	/* this return the intval plus the strval properties */
386ffe2e248SRui Miguel Silva 	return (gbpsy->properties_count + gbpsy->properties_count_str);
387ffe2e248SRui Miguel Silva }
388ffe2e248SRui Miguel Silva 
389ffe2e248SRui Miguel Silva static void prop_append(struct gb_power_supply *gbpsy,
390ffe2e248SRui Miguel Silva 			enum power_supply_property prop)
391ffe2e248SRui Miguel Silva {
392ffe2e248SRui Miguel Silva 	enum power_supply_property *new_props_raw;
393ffe2e248SRui Miguel Silva 
394ffe2e248SRui Miguel Silva 	gbpsy->properties_count_str++;
395ffe2e248SRui Miguel Silva 	new_props_raw = krealloc(gbpsy->props_raw, total_props(gbpsy) *
396ffe2e248SRui Miguel Silva 				 sizeof(enum power_supply_property),
397ffe2e248SRui Miguel Silva 				 GFP_KERNEL);
398ffe2e248SRui Miguel Silva 	if (!new_props_raw)
399ffe2e248SRui Miguel Silva 		return;
400ffe2e248SRui Miguel Silva 	gbpsy->props_raw = new_props_raw;
401ffe2e248SRui Miguel Silva 	gbpsy->props_raw[total_props(gbpsy) - 1] = prop;
402ffe2e248SRui Miguel Silva }
403ffe2e248SRui Miguel Silva 
404ffe2e248SRui Miguel Silva static int __gb_power_supply_set_name(char *init_name, char *name, size_t len)
405ffe2e248SRui Miguel Silva {
406ffe2e248SRui Miguel Silva 	unsigned int i = 0;
407ffe2e248SRui Miguel Silva 	int ret = 0;
408ffe2e248SRui Miguel Silva 	struct power_supply *psy;
409ffe2e248SRui Miguel Silva 
410ffe2e248SRui Miguel Silva 	if (!strlen(init_name))
411ffe2e248SRui Miguel Silva 		init_name = "gb_power_supply";
412ffe2e248SRui Miguel Silva 	strlcpy(name, init_name, len);
413ffe2e248SRui Miguel Silva 
414ffe2e248SRui Miguel Silva 	while ((ret < len) && (psy = power_supply_get_by_name(name))) {
415ffe2e248SRui Miguel Silva #ifdef PSY_HAVE_PUT
416ffe2e248SRui Miguel Silva 		power_supply_put(psy);
417ffe2e248SRui Miguel Silva #endif
418ffe2e248SRui Miguel Silva 		ret = snprintf(name, len, "%s_%u", init_name, ++i);
419ffe2e248SRui Miguel Silva 	}
420ffe2e248SRui Miguel Silva 	if (ret >= len)
421ffe2e248SRui Miguel Silva 		return -ENOMEM;
422ffe2e248SRui Miguel Silva 	return i;
423ffe2e248SRui Miguel Silva }
424ffe2e248SRui Miguel Silva 
425ffe2e248SRui Miguel Silva static void _gb_power_supply_append_props(struct gb_power_supply *gbpsy)
426ffe2e248SRui Miguel Silva {
427ffe2e248SRui Miguel Silva 	if (strlen(gbpsy->manufacturer))
428ffe2e248SRui Miguel Silva 		prop_append(gbpsy, POWER_SUPPLY_PROP_MANUFACTURER);
429ffe2e248SRui Miguel Silva 	if (strlen(gbpsy->model_name))
430ffe2e248SRui Miguel Silva 		prop_append(gbpsy, POWER_SUPPLY_PROP_MODEL_NAME);
431ffe2e248SRui Miguel Silva 	if (strlen(gbpsy->serial_number))
432ffe2e248SRui Miguel Silva 		prop_append(gbpsy, POWER_SUPPLY_PROP_SERIAL_NUMBER);
433ffe2e248SRui Miguel Silva }
434ffe2e248SRui Miguel Silva 
435ffe2e248SRui Miguel Silva static int gb_power_supply_description_get(struct gb_power_supply *gbpsy)
436ffe2e248SRui Miguel Silva {
437ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
438ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_description_request req;
439ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_description_response resp;
440ffe2e248SRui Miguel Silva 	int ret;
441ffe2e248SRui Miguel Silva 
442ffe2e248SRui Miguel Silva 	req.psy_id = gbpsy->id;
443ffe2e248SRui Miguel Silva 
444ffe2e248SRui Miguel Silva 	ret = gb_operation_sync(connection,
445ffe2e248SRui Miguel Silva 				GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION,
446ffe2e248SRui Miguel Silva 				&req, sizeof(req), &resp, sizeof(resp));
447ffe2e248SRui Miguel Silva 	if (ret < 0)
448ffe2e248SRui Miguel Silva 		return ret;
449ffe2e248SRui Miguel Silva 
450ffe2e248SRui Miguel Silva 	gbpsy->manufacturer = kstrndup(resp.manufacturer, PROP_MAX, GFP_KERNEL);
451ffe2e248SRui Miguel Silva 	if (!gbpsy->manufacturer)
452ffe2e248SRui Miguel Silva 		return -ENOMEM;
453ffe2e248SRui Miguel Silva 	gbpsy->model_name = kstrndup(resp.model, PROP_MAX, GFP_KERNEL);
454ffe2e248SRui Miguel Silva 	if (!gbpsy->model_name)
455ffe2e248SRui Miguel Silva 		return -ENOMEM;
456ffe2e248SRui Miguel Silva 	gbpsy->serial_number = kstrndup(resp.serial_number, PROP_MAX,
457ffe2e248SRui Miguel Silva 				       GFP_KERNEL);
458ffe2e248SRui Miguel Silva 	if (!gbpsy->serial_number)
459ffe2e248SRui Miguel Silva 		return -ENOMEM;
460ffe2e248SRui Miguel Silva 
461ffe2e248SRui Miguel Silva 	gbpsy->type = le16_to_cpu(resp.type);
462ffe2e248SRui Miguel Silva 	gbpsy->properties_count = resp.properties_count;
463ffe2e248SRui Miguel Silva 
464ffe2e248SRui Miguel Silva 	return 0;
465ffe2e248SRui Miguel Silva }
466ffe2e248SRui Miguel Silva 
467ffe2e248SRui Miguel Silva static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy)
468ffe2e248SRui Miguel Silva {
469ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
4709d15134dSRui Miguel Silva 	struct gb_power_supply_get_property_descriptors_request *req;
4719d15134dSRui Miguel Silva 	struct gb_power_supply_get_property_descriptors_response *resp;
4729d15134dSRui Miguel Silva 	struct gb_operation *op;
4739d15134dSRui Miguel Silva 	u8 props_count = gbpsy->properties_count;
47447becc55SRui Miguel Silva 	enum power_supply_property psp;
475ffe2e248SRui Miguel Silva 	int ret;
47647becc55SRui Miguel Silva 	int i, r = 0;
477ffe2e248SRui Miguel Silva 
4789d15134dSRui Miguel Silva 	if (props_count == 0)
479ffe2e248SRui Miguel Silva 		return 0;
480ffe2e248SRui Miguel Silva 
4819d15134dSRui Miguel Silva 	op = gb_operation_create(connection,
482ffe2e248SRui Miguel Silva 				 GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS,
4839d15134dSRui Miguel Silva 				 sizeof(req), sizeof(*resp) + props_count *
4849d15134dSRui Miguel Silva 				 sizeof(struct gb_power_supply_props_desc),
4859d15134dSRui Miguel Silva 				 GFP_KERNEL);
4869d15134dSRui Miguel Silva 	if (!op)
4879d15134dSRui Miguel Silva 		return -ENOMEM;
4889d15134dSRui Miguel Silva 
4899d15134dSRui Miguel Silva 	req = op->request->payload;
4909d15134dSRui Miguel Silva 	req->psy_id = gbpsy->id;
4919d15134dSRui Miguel Silva 
4929d15134dSRui Miguel Silva 	ret = gb_operation_request_send_sync(op);
493ffe2e248SRui Miguel Silva 	if (ret < 0)
4949d15134dSRui Miguel Silva 		goto out_put_operation;
4959d15134dSRui Miguel Silva 
4969d15134dSRui Miguel Silva 	resp = op->response->payload;
497ffe2e248SRui Miguel Silva 
49847becc55SRui Miguel Silva 	/* validate received properties */
49947becc55SRui Miguel Silva 	for (i = 0; i < props_count; i++) {
50047becc55SRui Miguel Silva 		ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
50147becc55SRui Miguel Silva 		if (ret < 0) {
50247becc55SRui Miguel Silva 			dev_warn(&connection->bundle->dev,
50347becc55SRui Miguel Silva 				 "greybus property %u it is not supported by this kernel, dropped\n",
50447becc55SRui Miguel Silva 				 resp->props[i].property);
50547becc55SRui Miguel Silva 			gbpsy->properties_count--;
50647becc55SRui Miguel Silva 		}
50747becc55SRui Miguel Silva 	}
50847becc55SRui Miguel Silva 
509ffe2e248SRui Miguel Silva 	gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props),
510ffe2e248SRui Miguel Silva 			      GFP_KERNEL);
5119d15134dSRui Miguel Silva 	if (!gbpsy->props) {
5129d15134dSRui Miguel Silva 		ret = -ENOMEM;
5139d15134dSRui Miguel Silva 		goto out_put_operation;
5149d15134dSRui Miguel Silva 	}
515ffe2e248SRui Miguel Silva 
5169d15134dSRui Miguel Silva 	gbpsy->props_raw = kcalloc(gbpsy->properties_count,
517ffe2e248SRui Miguel Silva 				   sizeof(*gbpsy->props_raw), GFP_KERNEL);
5189d15134dSRui Miguel Silva 	if (!gbpsy->props_raw) {
5199d15134dSRui Miguel Silva 		ret = -ENOMEM;
5209d15134dSRui Miguel Silva 		goto out_put_operation;
5219d15134dSRui Miguel Silva 	}
5229d15134dSRui Miguel Silva 
52347becc55SRui Miguel Silva 	/* Store available properties, skip the ones we do not support */
52447becc55SRui Miguel Silva 	for (i = 0; i < props_count; i++) {
52547becc55SRui Miguel Silva 		ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
52647becc55SRui Miguel Silva 		if (ret < 0) {
52747becc55SRui Miguel Silva 			r++;
52847becc55SRui Miguel Silva 			continue;
52947becc55SRui Miguel Silva 		}
53047becc55SRui Miguel Silva 		gbpsy->props[i - r].prop = psp;
53147becc55SRui Miguel Silva 		gbpsy->props[i - r].gb_prop = resp->props[i].property;
53247becc55SRui Miguel Silva 		gbpsy->props_raw[i - r] = psp;
5339d15134dSRui Miguel Silva 		if (resp->props[i].is_writeable)
53447becc55SRui Miguel Silva 			gbpsy->props[i - r].is_writeable = true;
535ffe2e248SRui Miguel Silva 	}
53646488841SRui Miguel Silva 
53746488841SRui Miguel Silva 	/*
538ffe2e248SRui Miguel Silva 	 * now append the properties that we already got information in the
539ffe2e248SRui Miguel Silva 	 * get_description operation. (char * ones)
54046488841SRui Miguel Silva 	 */
541ffe2e248SRui Miguel Silva 	_gb_power_supply_append_props(gbpsy);
542ffe2e248SRui Miguel Silva 
54347becc55SRui Miguel Silva 	ret = 0;
5449d15134dSRui Miguel Silva out_put_operation:
5459d15134dSRui Miguel Silva 	gb_operation_put(op);
5469d15134dSRui Miguel Silva 
5479d15134dSRui Miguel Silva 	return ret;
54846488841SRui Miguel Silva }
54946488841SRui Miguel Silva 
550ffe2e248SRui Miguel Silva static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy,
551ffe2e248SRui Miguel Silva 					     enum power_supply_property psp)
55246488841SRui Miguel Silva {
553ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
554ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop *prop;
555ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_property_request req;
556ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_property_response resp;
5576e720c27SRui Miguel Silva 	int val;
558ffe2e248SRui Miguel Silva 	int ret;
55946488841SRui Miguel Silva 
560ffe2e248SRui Miguel Silva 	prop = get_psy_prop(gbpsy, psp);
561ffe2e248SRui Miguel Silva 	if (!prop)
562ffe2e248SRui Miguel Silva 		return -EINVAL;
563ffe2e248SRui Miguel Silva 	req.psy_id = gbpsy->id;
56447becc55SRui Miguel Silva 	req.property = prop->gb_prop;
565ffe2e248SRui Miguel Silva 
566ffe2e248SRui Miguel Silva 	ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY,
567ffe2e248SRui Miguel Silva 				&req, sizeof(req), &resp, sizeof(resp));
568ffe2e248SRui Miguel Silva 	if (ret < 0)
569ffe2e248SRui Miguel Silva 		return ret;
570ffe2e248SRui Miguel Silva 
571ffe2e248SRui Miguel Silva 	val = le32_to_cpu(resp.prop_val);
572ffe2e248SRui Miguel Silva 	if (val == prop->val)
573ffe2e248SRui Miguel Silva 		return 0;
574ffe2e248SRui Miguel Silva 
575ffe2e248SRui Miguel Silva 	prop->previous_val = prop->val;
576ffe2e248SRui Miguel Silva 	prop->val = val;
577ffe2e248SRui Miguel Silva 
578ffe2e248SRui Miguel Silva 	check_changed(gbpsy, prop);
579ffe2e248SRui Miguel Silva 
580ffe2e248SRui Miguel Silva 	return 0;
581ffe2e248SRui Miguel Silva }
582ffe2e248SRui Miguel Silva 
583ffe2e248SRui Miguel Silva static int __gb_power_supply_property_get(struct gb_power_supply *gbpsy,
584ffe2e248SRui Miguel Silva 					  enum power_supply_property psp,
585ffe2e248SRui Miguel Silva 					  union power_supply_propval *val)
586ffe2e248SRui Miguel Silva {
587ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop *prop;
588ffe2e248SRui Miguel Silva 
589ffe2e248SRui Miguel Silva 	prop = get_psy_prop(gbpsy, psp);
590ffe2e248SRui Miguel Silva 	if (!prop)
591ffe2e248SRui Miguel Silva 		return -EINVAL;
592ffe2e248SRui Miguel Silva 
593ffe2e248SRui Miguel Silva 	val->intval = prop->val;
594ffe2e248SRui Miguel Silva 	return 0;
595ffe2e248SRui Miguel Silva }
596ffe2e248SRui Miguel Silva 
597ffe2e248SRui Miguel Silva static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy,
598ffe2e248SRui Miguel Silva 						enum power_supply_property psp,
599ffe2e248SRui Miguel Silva 						union power_supply_propval *val)
600ffe2e248SRui Miguel Silva {
601ffe2e248SRui Miguel Silva 	switch (psp) {
602ffe2e248SRui Miguel Silva 	case POWER_SUPPLY_PROP_MODEL_NAME:
603f921fb13SRui Miguel Silva 		val->strval = gbpsy->model_name;
604ffe2e248SRui Miguel Silva 		break;
605ffe2e248SRui Miguel Silva 	case POWER_SUPPLY_PROP_MANUFACTURER:
606f921fb13SRui Miguel Silva 		val->strval = gbpsy->manufacturer;
607ffe2e248SRui Miguel Silva 		break;
608ffe2e248SRui Miguel Silva 	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
609f921fb13SRui Miguel Silva 		val->strval = gbpsy->serial_number;
610ffe2e248SRui Miguel Silva 		break;
611ffe2e248SRui Miguel Silva 	default:
612ffe2e248SRui Miguel Silva 		break;
613ffe2e248SRui Miguel Silva 	}
614ffe2e248SRui Miguel Silva 
615ffe2e248SRui Miguel Silva 	return 0;
616ffe2e248SRui Miguel Silva }
617ffe2e248SRui Miguel Silva 
618ffe2e248SRui Miguel Silva static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy,
619ffe2e248SRui Miguel Silva 					 enum power_supply_property psp,
620ffe2e248SRui Miguel Silva 					 union power_supply_propval *val)
621ffe2e248SRui Miguel Silva {
622ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
623ffe2e248SRui Miguel Silva 	int ret;
62446488841SRui Miguel Silva 
62546488841SRui Miguel Silva 	/*
626ffe2e248SRui Miguel Silva 	 * Properties of type const char *, were already fetched on
627ffe2e248SRui Miguel Silva 	 * get_description operation and should be cached in gb
62846488841SRui Miguel Silva 	 */
629ffe2e248SRui Miguel Silva 	if (is_prop_valint(psp))
630ffe2e248SRui Miguel Silva 		ret = __gb_power_supply_property_get(gbpsy, psp, val);
631ffe2e248SRui Miguel Silva 	else
632ffe2e248SRui Miguel Silva 		ret = __gb_power_supply_property_strval_get(gbpsy, psp, val);
633ffe2e248SRui Miguel Silva 
634ffe2e248SRui Miguel Silva 	if (ret < 0)
635ffe2e248SRui Miguel Silva 		dev_err(&connection->bundle->dev, "get property %u\n", psp);
636ffe2e248SRui Miguel Silva 
637ffe2e248SRui Miguel Silva 	return 0;
63846488841SRui Miguel Silva }
63946488841SRui Miguel Silva 
640b5fbe819SRui Miguel Silva static int is_cache_valid(struct gb_power_supply *gbpsy)
641b5fbe819SRui Miguel Silva {
642b5fbe819SRui Miguel Silva 	/* check if cache is good enough or it has expired */
643b5fbe819SRui Miguel Silva 	if (gbpsy->cache_invalid) {
644b5fbe819SRui Miguel Silva 		gbpsy->cache_invalid = 0;
645b5fbe819SRui Miguel Silva 		return 0;
646b5fbe819SRui Miguel Silva 	}
647b5fbe819SRui Miguel Silva 
648b5fbe819SRui Miguel Silva 	if (gbpsy->last_update &&
649b5fbe819SRui Miguel Silva 	    time_is_after_jiffies(gbpsy->last_update +
650b5fbe819SRui Miguel Silva 				  msecs_to_jiffies(cache_time)))
651b5fbe819SRui Miguel Silva 		return 1;
652b5fbe819SRui Miguel Silva 
653b5fbe819SRui Miguel Silva 	return 0;
654b5fbe819SRui Miguel Silva }
655b5fbe819SRui Miguel Silva 
656ffe2e248SRui Miguel Silva static int gb_power_supply_status_get(struct gb_power_supply *gbpsy)
65746488841SRui Miguel Silva {
658ffe2e248SRui Miguel Silva 	int ret = 0;
659ffe2e248SRui Miguel Silva 	int i;
66046488841SRui Miguel Silva 
661b5fbe819SRui Miguel Silva 	if (is_cache_valid(gbpsy))
662ffe2e248SRui Miguel Silva 		return 0;
66346488841SRui Miguel Silva 
664ffe2e248SRui Miguel Silva 	for (i = 0; i < gbpsy->properties_count; i++) {
665ffe2e248SRui Miguel Silva 		ret = __gb_power_supply_property_update(gbpsy,
666ffe2e248SRui Miguel Silva 							gbpsy->props[i].prop);
667ffe2e248SRui Miguel Silva 		if (ret < 0)
668ffe2e248SRui Miguel Silva 			break;
66946488841SRui Miguel Silva 	}
67046488841SRui Miguel Silva 
671ffe2e248SRui Miguel Silva 	if (ret == 0)
672ffe2e248SRui Miguel Silva 		gbpsy->last_update = jiffies;
67346488841SRui Miguel Silva 
674ffe2e248SRui Miguel Silva 	return ret;
67546488841SRui Miguel Silva }
67646488841SRui Miguel Silva 
677ffe2e248SRui Miguel Silva static void gb_power_supply_status_update(struct gb_power_supply *gbpsy)
67846488841SRui Miguel Silva {
679ffe2e248SRui Miguel Silva 	/* check if there a change that need to be reported */
680ffe2e248SRui Miguel Silva 	gb_power_supply_status_get(gbpsy);
68146488841SRui Miguel Silva 
682ffe2e248SRui Miguel Silva 	if (!gbpsy->changed)
683ffe2e248SRui Miguel Silva 		return;
68446488841SRui Miguel Silva 
685ffe2e248SRui Miguel Silva 	gbpsy->update_interval = update_interval_init;
686ffe2e248SRui Miguel Silva 	__gb_power_supply_changed(gbpsy);
687ffe2e248SRui Miguel Silva 	gbpsy->changed = false;
68846488841SRui Miguel Silva }
68946488841SRui Miguel Silva 
690ffe2e248SRui Miguel Silva static void gb_power_supply_work(struct work_struct *work)
69146488841SRui Miguel Silva {
692ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = container_of(work,
693ffe2e248SRui Miguel Silva 						     struct gb_power_supply,
694ffe2e248SRui Miguel Silva 						     work.work);
69546488841SRui Miguel Silva 
696ffe2e248SRui Miguel Silva 	/*
697ffe2e248SRui Miguel Silva 	 * if the poll interval is not set, disable polling, this is helpful
698ffe2e248SRui Miguel Silva 	 * specially at unregister time.
699ffe2e248SRui Miguel Silva 	 */
700ffe2e248SRui Miguel Silva 	if (!gbpsy->update_interval)
701ffe2e248SRui Miguel Silva 		return;
70246488841SRui Miguel Silva 
703ffe2e248SRui Miguel Silva 	gb_power_supply_status_update(gbpsy);
704ffe2e248SRui Miguel Silva 	next_interval(gbpsy);
705ffe2e248SRui Miguel Silva 	schedule_delayed_work(&gbpsy->work, gbpsy->update_interval);
70646488841SRui Miguel Silva }
70746488841SRui Miguel Silva 
70846488841SRui Miguel Silva static int get_property(struct power_supply *b,
70946488841SRui Miguel Silva 			enum power_supply_property psp,
71046488841SRui Miguel Silva 			union power_supply_propval *val)
71146488841SRui Miguel Silva {
712ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = to_gb_power_supply(b);
71346488841SRui Miguel Silva 
714ffe2e248SRui Miguel Silva 	gb_power_supply_status_get(gbpsy);
71546488841SRui Miguel Silva 
716ffe2e248SRui Miguel Silva 	return _gb_power_supply_property_get(gbpsy, psp, val);
717ffe2e248SRui Miguel Silva }
71846488841SRui Miguel Silva 
719ffe2e248SRui Miguel Silva static int gb_power_supply_property_set(struct gb_power_supply *gbpsy,
720ffe2e248SRui Miguel Silva 					enum power_supply_property psp,
721ffe2e248SRui Miguel Silva 					int val)
722ffe2e248SRui Miguel Silva {
723ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
724ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop *prop;
725ffe2e248SRui Miguel Silva 	struct gb_power_supply_set_property_request req;
726ffe2e248SRui Miguel Silva 	int ret;
72746488841SRui Miguel Silva 
728ffe2e248SRui Miguel Silva 	prop = get_psy_prop(gbpsy, psp);
729ffe2e248SRui Miguel Silva 	if (!prop)
73046488841SRui Miguel Silva 		return -EINVAL;
731ffe2e248SRui Miguel Silva 	req.psy_id = gbpsy->id;
73247becc55SRui Miguel Silva 	req.property = prop->gb_prop;
7336e720c27SRui Miguel Silva 	req.prop_val = cpu_to_le32((s32)val);
734ffe2e248SRui Miguel Silva 
735ffe2e248SRui Miguel Silva 	ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY,
736ffe2e248SRui Miguel Silva 				&req, sizeof(req), NULL, 0);
737ffe2e248SRui Miguel Silva 	if (ret < 0)
738ffe2e248SRui Miguel Silva 		goto out;
739ffe2e248SRui Miguel Silva 
740ffe2e248SRui Miguel Silva 	/* cache immediately the new value */
741ffe2e248SRui Miguel Silva 	prop->val = val;
742ffe2e248SRui Miguel Silva 
743ffe2e248SRui Miguel Silva out:
744ffe2e248SRui Miguel Silva 	return ret;
74546488841SRui Miguel Silva }
74646488841SRui Miguel Silva 
747ffe2e248SRui Miguel Silva static int set_property(struct power_supply *b,
748ffe2e248SRui Miguel Silva 			enum power_supply_property psp,
749ffe2e248SRui Miguel Silva 			const union power_supply_propval *val)
750ffe2e248SRui Miguel Silva {
751ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = to_gb_power_supply(b);
752ffe2e248SRui Miguel Silva 
753ffe2e248SRui Miguel Silva 	return gb_power_supply_property_set(gbpsy, psp, val->intval);
75446488841SRui Miguel Silva }
75546488841SRui Miguel Silva 
756ffe2e248SRui Miguel Silva static int property_is_writeable(struct power_supply *b,
757ffe2e248SRui Miguel Silva 				 enum power_supply_property psp)
758ffe2e248SRui Miguel Silva {
759ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = to_gb_power_supply(b);
760ffe2e248SRui Miguel Silva 
761ffe2e248SRui Miguel Silva 	return is_psy_prop_writeable(gbpsy, psp);
762ffe2e248SRui Miguel Silva }
763ffe2e248SRui Miguel Silva 
764f8811c76SSandeep Patil #ifndef CORE_OWNS_PSY_STRUCT
765ffe2e248SRui Miguel Silva static int gb_power_supply_register(struct gb_power_supply *gbpsy)
76646488841SRui Miguel Silva {
767ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
76846488841SRui Miguel Silva 
769ffe2e248SRui Miguel Silva 	gbpsy->psy.name			= gbpsy->name;
770ffe2e248SRui Miguel Silva 	gbpsy->psy.type			= gbpsy->type;
771ffe2e248SRui Miguel Silva 	gbpsy->psy.properties		= gbpsy->props_raw;
772ffe2e248SRui Miguel Silva 	gbpsy->psy.num_properties	= total_props(gbpsy);
773ffe2e248SRui Miguel Silva 	gbpsy->psy.get_property		= get_property;
774ffe2e248SRui Miguel Silva 	gbpsy->psy.set_property		= set_property;
775ffe2e248SRui Miguel Silva 	gbpsy->psy.property_is_writeable = property_is_writeable;
776ffe2e248SRui Miguel Silva 
777ffe2e248SRui Miguel Silva 	return power_supply_register(&connection->bundle->dev,
778ffe2e248SRui Miguel Silva 				     &gbpsy->psy);
77946488841SRui Miguel Silva }
78046488841SRui Miguel Silva #else
781ffe2e248SRui Miguel Silva static int gb_power_supply_register(struct gb_power_supply *gbpsy)
78246488841SRui Miguel Silva {
783ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
78446488841SRui Miguel Silva 	struct power_supply_config cfg = {};
78546488841SRui Miguel Silva 
786ffe2e248SRui Miguel Silva 	cfg.drv_data = gbpsy;
78746488841SRui Miguel Silva 
788ffe2e248SRui Miguel Silva 	gbpsy->desc.name		= gbpsy->name;
789ffe2e248SRui Miguel Silva 	gbpsy->desc.type		= gbpsy->type;
790ffe2e248SRui Miguel Silva 	gbpsy->desc.properties		= gbpsy->props_raw;
791ffe2e248SRui Miguel Silva 	gbpsy->desc.num_properties	= total_props(gbpsy);
792ffe2e248SRui Miguel Silva 	gbpsy->desc.get_property	= get_property;
793ffe2e248SRui Miguel Silva 	gbpsy->desc.set_property	= set_property;
794ffe2e248SRui Miguel Silva 	gbpsy->desc.property_is_writeable = property_is_writeable;
79546488841SRui Miguel Silva 
796ffe2e248SRui Miguel Silva 	gbpsy->psy = power_supply_register(&connection->bundle->dev,
797ffe2e248SRui Miguel Silva 					   &gbpsy->desc, &cfg);
79895073cc2SAlex Elder 	return PTR_ERR_OR_ZERO(gbpsy->psy);
79946488841SRui Miguel Silva }
80046488841SRui Miguel Silva #endif
80146488841SRui Miguel Silva 
802ffe2e248SRui Miguel Silva static void _gb_power_supply_free(struct gb_power_supply *gbpsy)
80346488841SRui Miguel Silva {
804ffe2e248SRui Miguel Silva 	kfree(gbpsy->serial_number);
805ffe2e248SRui Miguel Silva 	kfree(gbpsy->model_name);
806ffe2e248SRui Miguel Silva 	kfree(gbpsy->manufacturer);
807ffe2e248SRui Miguel Silva 	kfree(gbpsy->props_raw);
808ffe2e248SRui Miguel Silva 	kfree(gbpsy->props);
809ffe2e248SRui Miguel Silva }
81046488841SRui Miguel Silva 
811ffe2e248SRui Miguel Silva static void _gb_power_supply_release(struct gb_power_supply *gbpsy)
812ffe2e248SRui Miguel Silva {
813ffe2e248SRui Miguel Silva 	gbpsy->update_interval = 0;
814ffe2e248SRui Miguel Silva 
815ffe2e248SRui Miguel Silva 	cancel_delayed_work_sync(&gbpsy->work);
816f8811c76SSandeep Patil #ifndef CORE_OWNS_PSY_STRUCT
817ff85f723SRui Miguel Silva 	if (gbpsy->registered)
818ffe2e248SRui Miguel Silva 		power_supply_unregister(&gbpsy->psy);
819ffe2e248SRui Miguel Silva #else
820ff85f723SRui Miguel Silva 	if (gbpsy->registered)
821ffe2e248SRui Miguel Silva 		power_supply_unregister(gbpsy->psy);
822ffe2e248SRui Miguel Silva #endif
823ffe2e248SRui Miguel Silva 
824ffe2e248SRui Miguel Silva 	_gb_power_supply_free(gbpsy);
825ffe2e248SRui Miguel Silva }
826ffe2e248SRui Miguel Silva 
827ffe2e248SRui Miguel Silva static void _gb_power_supplies_release(struct gb_power_supplies *supplies)
828ffe2e248SRui Miguel Silva {
829ffe2e248SRui Miguel Silva 	int i;
830ffe2e248SRui Miguel Silva 
8317ccac20dSRui Miguel Silva 	if (!supplies->supply)
8327ccac20dSRui Miguel Silva 		return;
8337ccac20dSRui Miguel Silva 
834ffe2e248SRui Miguel Silva 	mutex_lock(&supplies->supplies_lock);
835ffe2e248SRui Miguel Silva 	for (i = 0; i < supplies->supplies_count; i++)
836ffe2e248SRui Miguel Silva 		_gb_power_supply_release(&supplies->supply[i]);
837accad1baSRui Miguel Silva 	kfree(supplies->supply);
838ffe2e248SRui Miguel Silva 	mutex_unlock(&supplies->supplies_lock);
83923f25ba6SRui Miguel Silva 	kfree(supplies);
840ffe2e248SRui Miguel Silva }
841ffe2e248SRui Miguel Silva 
842ffe2e248SRui Miguel Silva static int gb_power_supplies_get_count(struct gb_power_supplies *supplies)
843ffe2e248SRui Miguel Silva {
844ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_supplies_response resp;
845ffe2e248SRui Miguel Silva 	int ret;
846ffe2e248SRui Miguel Silva 
847ffe2e248SRui Miguel Silva 	ret = gb_operation_sync(supplies->connection,
848ffe2e248SRui Miguel Silva 				GB_POWER_SUPPLY_TYPE_GET_SUPPLIES,
849ffe2e248SRui Miguel Silva 				NULL, 0, &resp, sizeof(resp));
850ffe2e248SRui Miguel Silva 	if (ret < 0)
851ffe2e248SRui Miguel Silva 		return ret;
852ffe2e248SRui Miguel Silva 
853ffe2e248SRui Miguel Silva 	if  (!resp.supplies_count)
854ffe2e248SRui Miguel Silva 		return -EINVAL;
855ffe2e248SRui Miguel Silva 
856ffe2e248SRui Miguel Silva 	supplies->supplies_count = resp.supplies_count;
857ffe2e248SRui Miguel Silva 
858ffe2e248SRui Miguel Silva 	return ret;
859ffe2e248SRui Miguel Silva }
860ffe2e248SRui Miguel Silva 
861ffe2e248SRui Miguel Silva static int gb_power_supply_config(struct gb_power_supplies *supplies, int id)
862ffe2e248SRui Miguel Silva {
863ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = &supplies->supply[id];
864ffe2e248SRui Miguel Silva 	int ret;
865ffe2e248SRui Miguel Silva 
866ffe2e248SRui Miguel Silva 	gbpsy->supplies = supplies;
867ffe2e248SRui Miguel Silva 	gbpsy->id = id;
868ffe2e248SRui Miguel Silva 
869ffe2e248SRui Miguel Silva 	ret = gb_power_supply_description_get(gbpsy);
870ffe2e248SRui Miguel Silva 	if (ret < 0)
8717e9fba8dSViresh Kumar 		return ret;
872ffe2e248SRui Miguel Silva 
8736ac9166dSRui Miguel Silva 	return gb_power_supply_prop_descriptors_get(gbpsy);
8747e9fba8dSViresh Kumar }
8757e9fba8dSViresh Kumar 
8767e9fba8dSViresh Kumar static int gb_power_supply_enable(struct gb_power_supply *gbpsy)
8777e9fba8dSViresh Kumar {
8787e9fba8dSViresh Kumar 	int ret;
879ffe2e248SRui Miguel Silva 
8806ac9166dSRui Miguel Silva 	/* guarantee that we have an unique name, before register */
8816ac9166dSRui Miguel Silva 	ret =  __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name,
8826ac9166dSRui Miguel Silva 					  sizeof(gbpsy->name));
8836ac9166dSRui Miguel Silva 	if (ret < 0)
8846ac9166dSRui Miguel Silva 		return ret;
8856ac9166dSRui Miguel Silva 
886ffe2e248SRui Miguel Silva 	ret = gb_power_supply_register(gbpsy);
887ffe2e248SRui Miguel Silva 	if (ret < 0)
8887e9fba8dSViresh Kumar 		return ret;
889ffe2e248SRui Miguel Silva 
890ffe2e248SRui Miguel Silva 	gbpsy->update_interval = update_interval_init;
891ffe2e248SRui Miguel Silva 	INIT_DELAYED_WORK(&gbpsy->work, gb_power_supply_work);
892ffe2e248SRui Miguel Silva 	schedule_delayed_work(&gbpsy->work, 0);
893ffe2e248SRui Miguel Silva 
8947e9fba8dSViresh Kumar 	/* everything went fine, mark it for release code to know */
895ff85f723SRui Miguel Silva 	gbpsy->registered = true;
8967e9fba8dSViresh Kumar 
8977e9fba8dSViresh Kumar 	return 0;
898ffe2e248SRui Miguel Silva }
899ffe2e248SRui Miguel Silva 
900ffe2e248SRui Miguel Silva static int gb_power_supplies_setup(struct gb_power_supplies *supplies)
901ffe2e248SRui Miguel Silva {
902ffe2e248SRui Miguel Silva 	struct gb_connection *connection = supplies->connection;
903ffe2e248SRui Miguel Silva 	int ret;
904ffe2e248SRui Miguel Silva 	int i;
905ffe2e248SRui Miguel Silva 
906ffe2e248SRui Miguel Silva 	mutex_lock(&supplies->supplies_lock);
907ffe2e248SRui Miguel Silva 
908ffe2e248SRui Miguel Silva 	ret = gb_power_supplies_get_count(supplies);
909ffe2e248SRui Miguel Silva 	if (ret < 0)
910ffe2e248SRui Miguel Silva 		goto out;
911ffe2e248SRui Miguel Silva 
912ffe2e248SRui Miguel Silva 	supplies->supply = kzalloc(supplies->supplies_count *
913ffe2e248SRui Miguel Silva 				     sizeof(struct gb_power_supply),
914ffe2e248SRui Miguel Silva 				     GFP_KERNEL);
915ffe2e248SRui Miguel Silva 
916e0d91ff1SJohan Hovold 	if (!supplies->supply) {
917e0d91ff1SJohan Hovold 		ret = -ENOMEM;
918e0d91ff1SJohan Hovold 		goto out;
919e0d91ff1SJohan Hovold 	}
92046488841SRui Miguel Silva 
921ffe2e248SRui Miguel Silva 	for (i = 0; i < supplies->supplies_count; i++) {
922ffe2e248SRui Miguel Silva 		ret = gb_power_supply_config(supplies, i);
923ffe2e248SRui Miguel Silva 		if (ret < 0) {
924ffe2e248SRui Miguel Silva 			dev_err(&connection->bundle->dev,
925ffe2e248SRui Miguel Silva 				"Fail to configure supplies devices\n");
926ffe2e248SRui Miguel Silva 			goto out;
927ffe2e248SRui Miguel Silva 		}
928ffe2e248SRui Miguel Silva 	}
929ffe2e248SRui Miguel Silva out:
930ffe2e248SRui Miguel Silva 	mutex_unlock(&supplies->supplies_lock);
931ffe2e248SRui Miguel Silva 	return ret;
932ffe2e248SRui Miguel Silva }
93346488841SRui Miguel Silva 
9347e9fba8dSViresh Kumar static int gb_power_supplies_register(struct gb_power_supplies *supplies)
9357e9fba8dSViresh Kumar {
9367e9fba8dSViresh Kumar 	struct gb_connection *connection = supplies->connection;
9377e9fba8dSViresh Kumar 	int ret = 0;
9387e9fba8dSViresh Kumar 	int i;
9397e9fba8dSViresh Kumar 
9407e9fba8dSViresh Kumar 	mutex_lock(&supplies->supplies_lock);
9417e9fba8dSViresh Kumar 
9427e9fba8dSViresh Kumar 	for (i = 0; i < supplies->supplies_count; i++) {
9437e9fba8dSViresh Kumar 		ret = gb_power_supply_enable(&supplies->supply[i]);
9447e9fba8dSViresh Kumar 		if (ret < 0) {
9457e9fba8dSViresh Kumar 			dev_err(&connection->bundle->dev,
9467e9fba8dSViresh Kumar 				"Fail to enable supplies devices\n");
9477e9fba8dSViresh Kumar 			break;
9487e9fba8dSViresh Kumar 		}
9497e9fba8dSViresh Kumar 	}
9507e9fba8dSViresh Kumar 
9517e9fba8dSViresh Kumar 	mutex_unlock(&supplies->supplies_lock);
9527e9fba8dSViresh Kumar 	return ret;
9537e9fba8dSViresh Kumar }
9547e9fba8dSViresh Kumar 
95568b1309bSViresh Kumar static int gb_supplies_request_handler(struct gb_operation *op)
956ffe2e248SRui Miguel Silva {
957ffe2e248SRui Miguel Silva 	struct gb_connection *connection = op->connection;
9580ec30632SGreg Kroah-Hartman 	struct gb_power_supplies *supplies = gb_connection_get_data(connection);
959ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy;
960ffe2e248SRui Miguel Silva 	struct gb_message *request;
961ffe2e248SRui Miguel Silva 	struct gb_power_supply_event_request *payload;
962ffe2e248SRui Miguel Silva 	u8 psy_id;
963ffe2e248SRui Miguel Silva 	u8 event;
964ffe2e248SRui Miguel Silva 	int ret = 0;
96546488841SRui Miguel Silva 
96668b1309bSViresh Kumar 	if (op->type != GB_POWER_SUPPLY_TYPE_EVENT) {
967ffe2e248SRui Miguel Silva 		dev_err(&connection->bundle->dev,
96868b1309bSViresh Kumar 			"Unsupported unsolicited event: %u\n", op->type);
969ffe2e248SRui Miguel Silva 		return -EINVAL;
970ffe2e248SRui Miguel Silva 	}
971ffe2e248SRui Miguel Silva 
972ffe2e248SRui Miguel Silva 	request = op->request;
973ffe2e248SRui Miguel Silva 
974ffe2e248SRui Miguel Silva 	if (request->payload_size < sizeof(*payload)) {
975ffe2e248SRui Miguel Silva 		dev_err(&connection->bundle->dev,
976ffe2e248SRui Miguel Silva 			"Wrong event size received (%zu < %zu)\n",
977ffe2e248SRui Miguel Silva 			request->payload_size, sizeof(*payload));
978ffe2e248SRui Miguel Silva 		return -EINVAL;
979ffe2e248SRui Miguel Silva 	}
980ffe2e248SRui Miguel Silva 
981ffe2e248SRui Miguel Silva 	payload = request->payload;
982ffe2e248SRui Miguel Silva 	psy_id = payload->psy_id;
983ffe2e248SRui Miguel Silva 	mutex_lock(&supplies->supplies_lock);
984adb57cffSRui Miguel Silva 	if (psy_id >= supplies->supplies_count ||
985adb57cffSRui Miguel Silva 	    !supplies->supply[psy_id].registered) {
986ffe2e248SRui Miguel Silva 		dev_err(&connection->bundle->dev,
987ffe2e248SRui Miguel Silva 			"Event received for unconfigured power_supply id: %d\n",
988ffe2e248SRui Miguel Silva 			psy_id);
989ffe2e248SRui Miguel Silva 		ret = -EINVAL;
990ffe2e248SRui Miguel Silva 		goto out_unlock;
991ffe2e248SRui Miguel Silva 	}
992ffe2e248SRui Miguel Silva 
993ffe2e248SRui Miguel Silva 	event = payload->event;
994ffe2e248SRui Miguel Silva 	/*
995ffe2e248SRui Miguel Silva 	 * we will only handle events after setup is done and before release is
996ffe2e248SRui Miguel Silva 	 * running. For that just check update_interval.
997ffe2e248SRui Miguel Silva 	 */
998ffe2e248SRui Miguel Silva 	gbpsy = &supplies->supply[psy_id];
9995f66d62eSRui Miguel Silva 	if (!gbpsy->update_interval) {
1000ffe2e248SRui Miguel Silva 		ret = -ESHUTDOWN;
1001ffe2e248SRui Miguel Silva 		goto out_unlock;
1002ffe2e248SRui Miguel Silva 	}
1003ffe2e248SRui Miguel Silva 
1004b5fbe819SRui Miguel Silva 	if (event & GB_POWER_SUPPLY_UPDATE) {
1005b5fbe819SRui Miguel Silva 		/*
1006b5fbe819SRui Miguel Silva 		 * we need to make sure we invalidate cache, if not no new
1007b5fbe819SRui Miguel Silva 		 * values for the properties will be fetch and the all propose
1008b5fbe819SRui Miguel Silva 		 * of this event is missed
1009b5fbe819SRui Miguel Silva 		 */
1010b5fbe819SRui Miguel Silva 		gbpsy->cache_invalid = 1;
1011ffe2e248SRui Miguel Silva 		gb_power_supply_status_update(gbpsy);
1012b5fbe819SRui Miguel Silva 	}
1013ffe2e248SRui Miguel Silva 
1014ffe2e248SRui Miguel Silva out_unlock:
1015ffe2e248SRui Miguel Silva 	mutex_unlock(&supplies->supplies_lock);
1016ffe2e248SRui Miguel Silva 	return ret;
1017ffe2e248SRui Miguel Silva }
1018ffe2e248SRui Miguel Silva 
101968b1309bSViresh Kumar static int gb_power_supply_probe(struct gb_bundle *bundle,
102068b1309bSViresh Kumar 				 const struct greybus_bundle_id *id)
1021ffe2e248SRui Miguel Silva {
102268b1309bSViresh Kumar 	struct greybus_descriptor_cport *cport_desc;
102368b1309bSViresh Kumar 	struct gb_connection *connection;
1024ffe2e248SRui Miguel Silva 	struct gb_power_supplies *supplies;
1025d9eafd58SRui Miguel Silva 	int ret;
1026ffe2e248SRui Miguel Silva 
102768b1309bSViresh Kumar 	if (bundle->num_cports != 1)
102868b1309bSViresh Kumar 		return -ENODEV;
102968b1309bSViresh Kumar 
103068b1309bSViresh Kumar 	cport_desc = &bundle->cport_desc[0];
103168b1309bSViresh Kumar 	if (cport_desc->protocol_id != GREYBUS_PROTOCOL_POWER_SUPPLY)
103268b1309bSViresh Kumar 		return -ENODEV;
103368b1309bSViresh Kumar 
1034ffe2e248SRui Miguel Silva 	supplies = kzalloc(sizeof(*supplies), GFP_KERNEL);
1035ffe2e248SRui Miguel Silva 	if (!supplies)
1036ffe2e248SRui Miguel Silva 		return -ENOMEM;
1037ffe2e248SRui Miguel Silva 
103868b1309bSViresh Kumar 	connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
103968b1309bSViresh Kumar 					  gb_supplies_request_handler);
104068b1309bSViresh Kumar 	if (IS_ERR(connection)) {
104168b1309bSViresh Kumar 		ret = PTR_ERR(connection);
104268b1309bSViresh Kumar 		goto out;
104368b1309bSViresh Kumar 	}
104468b1309bSViresh Kumar 
1045ffe2e248SRui Miguel Silva 	supplies->connection = connection;
10460ec30632SGreg Kroah-Hartman 	gb_connection_set_data(connection, supplies);
1047ffe2e248SRui Miguel Silva 
1048ffe2e248SRui Miguel Silva 	mutex_init(&supplies->supplies_lock);
1049ffe2e248SRui Miguel Silva 
105068b1309bSViresh Kumar 	greybus_set_drvdata(bundle, supplies);
105168b1309bSViresh Kumar 
105268b1309bSViresh Kumar 	/* We aren't ready to receive an incoming request yet */
105368b1309bSViresh Kumar 	ret = gb_connection_enable_tx(connection);
105468b1309bSViresh Kumar 	if (ret)
105568b1309bSViresh Kumar 		goto error_connection_destroy;
105668b1309bSViresh Kumar 
1057d9eafd58SRui Miguel Silva 	ret = gb_power_supplies_setup(supplies);
1058d9eafd58SRui Miguel Silva 	if (ret < 0)
105968b1309bSViresh Kumar 		goto error_connection_disable;
106068b1309bSViresh Kumar 
106168b1309bSViresh Kumar 	/* We are ready to receive an incoming request now, enable RX as well */
106268b1309bSViresh Kumar 	ret = gb_connection_enable(connection);
106368b1309bSViresh Kumar 	if (ret)
106468b1309bSViresh Kumar 		goto error_connection_disable;
1065d9eafd58SRui Miguel Silva 
10667e9fba8dSViresh Kumar 	ret = gb_power_supplies_register(supplies);
10677e9fba8dSViresh Kumar 	if (ret < 0)
106868b1309bSViresh Kumar 		goto error_connection_disable;
10697e9fba8dSViresh Kumar 
10707e9fba8dSViresh Kumar 	return 0;
10717e9fba8dSViresh Kumar 
107268b1309bSViresh Kumar error_connection_disable:
107368b1309bSViresh Kumar 	gb_connection_disable(connection);
107468b1309bSViresh Kumar error_connection_destroy:
107568b1309bSViresh Kumar 	gb_connection_destroy(connection);
10767e9fba8dSViresh Kumar out:
10777e9fba8dSViresh Kumar 	_gb_power_supplies_release(supplies);
1078d9eafd58SRui Miguel Silva 	return ret;
107946488841SRui Miguel Silva }
108046488841SRui Miguel Silva 
108168b1309bSViresh Kumar static void gb_power_supply_disconnect(struct gb_bundle *bundle)
108246488841SRui Miguel Silva {
108368b1309bSViresh Kumar 	struct gb_power_supplies *supplies = greybus_get_drvdata(bundle);
108468b1309bSViresh Kumar 
108568b1309bSViresh Kumar 	gb_connection_disable(supplies->connection);
108668b1309bSViresh Kumar 	gb_connection_destroy(supplies->connection);
108746488841SRui Miguel Silva 
1088ffe2e248SRui Miguel Silva 	_gb_power_supplies_release(supplies);
108946488841SRui Miguel Silva }
109046488841SRui Miguel Silva 
109168b1309bSViresh Kumar static const struct greybus_bundle_id gb_power_supply_id_table[] = {
109268b1309bSViresh Kumar 	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) },
109368b1309bSViresh Kumar 	{ }
109446488841SRui Miguel Silva };
109568b1309bSViresh Kumar MODULE_DEVICE_TABLE(greybus, gb_power_supply_id_table);
109646488841SRui Miguel Silva 
109768b1309bSViresh Kumar static struct greybus_driver gb_power_supply_driver = {
109868b1309bSViresh Kumar 	.name		= "power_supply",
109968b1309bSViresh Kumar 	.probe		= gb_power_supply_probe,
110068b1309bSViresh Kumar 	.disconnect	= gb_power_supply_disconnect,
110168b1309bSViresh Kumar 	.id_table	= gb_power_supply_id_table,
110268b1309bSViresh Kumar };
110368b1309bSViresh Kumar module_greybus_driver(gb_power_supply_driver);
110446488841SRui Miguel Silva 
110546488841SRui Miguel Silva MODULE_LICENSE("GPL v2");
1106