1eb50fd3aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
246488841SRui Miguel Silva /*
32724be03SRui Miguel Silva  * Power Supply driver for a Greybus module.
446488841SRui Miguel Silva  *
5ffe2e248SRui Miguel Silva  * Copyright 2014-2015 Google Inc.
6ffe2e248SRui Miguel Silva  * Copyright 2014-2015 Linaro Ltd.
746488841SRui Miguel Silva  */
846488841SRui Miguel Silva 
946488841SRui Miguel Silva #include <linux/kernel.h>
1046488841SRui Miguel Silva #include <linux/module.h>
1146488841SRui Miguel Silva #include <linux/power_supply.h>
12ffe2e248SRui Miguel Silva #include <linux/slab.h>
13ec0ad868SGreg Kroah-Hartman #include <linux/greybus.h>
1446488841SRui Miguel Silva 
15ffe2e248SRui Miguel Silva #define PROP_MAX 32
16ffe2e248SRui Miguel Silva 
17ffe2e248SRui Miguel Silva struct gb_power_supply_prop {
18ffe2e248SRui Miguel Silva 	enum power_supply_property	prop;
1947becc55SRui Miguel Silva 	u8				gb_prop;
206e720c27SRui Miguel Silva 	int				val;
216e720c27SRui Miguel Silva 	int				previous_val;
22ffe2e248SRui Miguel Silva 	bool				is_writeable;
23ffe2e248SRui Miguel Silva };
24ffe2e248SRui Miguel Silva 
252724be03SRui Miguel Silva struct gb_power_supply {
26ffe2e248SRui Miguel Silva 	u8				id;
27ff85f723SRui Miguel Silva 	bool				registered;
282724be03SRui Miguel Silva 	struct power_supply		*psy;
2946488841SRui Miguel Silva 	struct power_supply_desc	desc;
30ffe2e248SRui Miguel Silva 	char				name[64];
31ffe2e248SRui Miguel Silva 	struct gb_power_supplies	*supplies;
32ffe2e248SRui Miguel Silva 	struct delayed_work		work;
33ffe2e248SRui Miguel Silva 	char				*manufacturer;
34ffe2e248SRui Miguel Silva 	char				*model_name;
35ffe2e248SRui Miguel Silva 	char				*serial_number;
36ffe2e248SRui Miguel Silva 	u8				type;
37ffe2e248SRui Miguel Silva 	u8				properties_count;
38ffe2e248SRui Miguel Silva 	u8				properties_count_str;
39ffe2e248SRui Miguel Silva 	unsigned long			last_update;
40b5fbe819SRui Miguel Silva 	u8				cache_invalid;
41ffe2e248SRui Miguel Silva 	unsigned int			update_interval;
42ffe2e248SRui Miguel Silva 	bool				changed;
43ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop	*props;
44ffe2e248SRui Miguel Silva 	enum power_supply_property	*props_raw;
454e013b64SPhilip Yang 	bool				pm_acquired;
464e013b64SPhilip Yang 	struct mutex			supply_lock;
4746488841SRui Miguel Silva };
4846488841SRui Miguel Silva 
49ffe2e248SRui Miguel Silva struct gb_power_supplies {
50ffe2e248SRui Miguel Silva 	struct gb_connection	*connection;
51ffe2e248SRui Miguel Silva 	u8			supplies_count;
52ffe2e248SRui Miguel Silva 	struct gb_power_supply	*supply;
53ffe2e248SRui Miguel Silva 	struct mutex		supplies_lock;
54ffe2e248SRui Miguel Silva };
5546488841SRui Miguel Silva 
56722a133aSRui Miguel Silva #define to_gb_power_supply(x) power_supply_get_drvdata(x)
57722a133aSRui Miguel Silva 
58722a133aSRui Miguel Silva /*
59722a133aSRui Miguel Silva  * General power supply properties that could be absent from various reasons,
60722a133aSRui Miguel Silva  * like kernel versions or vendor specific versions
61722a133aSRui Miguel Silva  */
62722a133aSRui Miguel Silva #ifndef POWER_SUPPLY_PROP_VOLTAGE_BOOT
63722a133aSRui Miguel Silva 	#define POWER_SUPPLY_PROP_VOLTAGE_BOOT	-1
64722a133aSRui Miguel Silva #endif
65722a133aSRui Miguel Silva #ifndef POWER_SUPPLY_PROP_CURRENT_BOOT
66722a133aSRui Miguel Silva 	#define POWER_SUPPLY_PROP_CURRENT_BOOT	-1
67722a133aSRui Miguel Silva #endif
68722a133aSRui Miguel Silva #ifndef POWER_SUPPLY_PROP_CALIBRATE
69722a133aSRui Miguel Silva 	#define POWER_SUPPLY_PROP_CALIBRATE	-1
70722a133aSRui Miguel Silva #endif
71722a133aSRui Miguel Silva 
72ffe2e248SRui Miguel Silva /* cache time in milliseconds, if cache_time is set to 0 cache is disable */
73ffe2e248SRui Miguel Silva static unsigned int cache_time = 1000;
74ffe2e248SRui Miguel Silva /*
75ffe2e248SRui Miguel Silva  * update interval initial and maximum value, between the two will
76ffe2e248SRui Miguel Silva  * back-off exponential
77ffe2e248SRui Miguel Silva  */
78ffe2e248SRui Miguel Silva static unsigned int update_interval_init = 1 * HZ;
79ffe2e248SRui Miguel Silva static unsigned int update_interval_max = 30 * HZ;
80ffe2e248SRui Miguel Silva 
81ffe2e248SRui Miguel Silva struct gb_power_supply_changes {
82ffe2e248SRui Miguel Silva 	enum power_supply_property	prop;
83ffe2e248SRui Miguel Silva 	u32				tolerance_change;
84c4582f9dSRui Miguel Silva 	void (*prop_changed)(struct gb_power_supply *gbpsy,
85c4582f9dSRui Miguel Silva 			     struct gb_power_supply_prop *prop);
86ffe2e248SRui Miguel Silva };
87ffe2e248SRui Miguel Silva 
884e013b64SPhilip Yang static void gb_power_supply_state_change(struct gb_power_supply *gbpsy,
894e013b64SPhilip Yang 					 struct gb_power_supply_prop *prop);
904e013b64SPhilip Yang 
91ffe2e248SRui Miguel Silva static const struct gb_power_supply_changes psy_props_changes[] = {
92ffe2e248SRui Miguel Silva 	{	.prop			= GB_POWER_SUPPLY_PROP_STATUS,
93ffe2e248SRui Miguel Silva 		.tolerance_change	= 0,
944e013b64SPhilip Yang 		.prop_changed		= gb_power_supply_state_change,
95ffe2e248SRui Miguel Silva 	},
96ffe2e248SRui Miguel Silva 	{	.prop			= GB_POWER_SUPPLY_PROP_TEMP,
97ffe2e248SRui Miguel Silva 		.tolerance_change	= 500,
98c4582f9dSRui Miguel Silva 		.prop_changed		= NULL,
99ffe2e248SRui Miguel Silva 	},
100ffe2e248SRui Miguel Silva 	{	.prop			= GB_POWER_SUPPLY_PROP_ONLINE,
101ffe2e248SRui Miguel Silva 		.tolerance_change	= 0,
102c4582f9dSRui Miguel Silva 		.prop_changed		= NULL,
103ffe2e248SRui Miguel Silva 	},
104ffe2e248SRui Miguel Silva };
105ffe2e248SRui Miguel Silva 
get_psp_from_gb_prop(int gb_prop,enum power_supply_property * psp)10647becc55SRui Miguel Silva static int get_psp_from_gb_prop(int gb_prop, enum power_supply_property *psp)
10747becc55SRui Miguel Silva {
10847becc55SRui Miguel Silva 	int prop;
10947becc55SRui Miguel Silva 
11047becc55SRui Miguel Silva 	switch (gb_prop) {
11147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_STATUS:
11247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_STATUS;
11347becc55SRui Miguel Silva 		break;
11447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_TYPE:
11547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_TYPE;
11647becc55SRui Miguel Silva 		break;
11747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_HEALTH:
11847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_HEALTH;
11947becc55SRui Miguel Silva 		break;
12047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_PRESENT:
12147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_PRESENT;
12247becc55SRui Miguel Silva 		break;
12347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ONLINE:
12447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ONLINE;
12547becc55SRui Miguel Silva 		break;
12647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_AUTHENTIC:
12747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_AUTHENTIC;
12847becc55SRui Miguel Silva 		break;
12947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TECHNOLOGY:
13047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TECHNOLOGY;
13147becc55SRui Miguel Silva 		break;
13247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CYCLE_COUNT:
13347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CYCLE_COUNT;
13447becc55SRui Miguel Silva 		break;
13547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX:
13647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
13747becc55SRui Miguel Silva 		break;
13847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN:
13947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
14047becc55SRui Miguel Silva 		break;
14147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
14247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
14347becc55SRui Miguel Silva 		break;
14447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
14547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
14647becc55SRui Miguel Silva 		break;
14747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_NOW:
14847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
14947becc55SRui Miguel Silva 		break;
15047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_AVG:
15147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
15247becc55SRui Miguel Silva 		break;
15347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_OCV:
15447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_OCV;
15547becc55SRui Miguel Silva 		break;
15647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_VOLTAGE_BOOT:
15747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_VOLTAGE_BOOT;
15847becc55SRui Miguel Silva 		break;
15947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CURRENT_MAX:
16047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CURRENT_MAX;
16147becc55SRui Miguel Silva 		break;
16247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CURRENT_NOW:
16347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CURRENT_NOW;
16447becc55SRui Miguel Silva 		break;
16547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CURRENT_AVG:
16647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CURRENT_AVG;
16747becc55SRui Miguel Silva 		break;
16847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CURRENT_BOOT:
16947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CURRENT_BOOT;
17047becc55SRui Miguel Silva 		break;
17147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_POWER_NOW:
17247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_POWER_NOW;
17347becc55SRui Miguel Silva 		break;
17447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_POWER_AVG:
17547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_POWER_AVG;
17647becc55SRui Miguel Silva 		break;
17747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
17847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
17947becc55SRui Miguel Silva 		break;
18047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
18147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
18247becc55SRui Miguel Silva 		break;
18347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_FULL:
18447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_FULL;
18547becc55SRui Miguel Silva 		break;
18647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY:
18747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
18847becc55SRui Miguel Silva 		break;
18947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_NOW:
19047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_NOW;
19147becc55SRui Miguel Silva 		break;
19247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_AVG:
19347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_AVG;
19447becc55SRui Miguel Silva 		break;
19547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_COUNTER:
19647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_COUNTER;
19747becc55SRui Miguel Silva 		break;
19847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
19947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT;
20047becc55SRui Miguel Silva 		break;
20147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
20247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
20347becc55SRui Miguel Silva 		break;
20447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
20547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
20647becc55SRui Miguel Silva 		break;
20747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
20847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX;
20947becc55SRui Miguel Silva 		break;
21047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
21147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
21247becc55SRui Miguel Silva 		break;
21347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
21447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX;
21547becc55SRui Miguel Silva 		break;
21647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
21747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
21847becc55SRui Miguel Silva 		break;
21947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
22047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
22147becc55SRui Miguel Silva 		break;
22247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN:
22347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
22447becc55SRui Miguel Silva 		break;
22547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_FULL:
22647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_FULL;
22747becc55SRui Miguel Silva 		break;
22847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY:
22947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
23047becc55SRui Miguel Silva 		break;
23147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_NOW:
23247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_NOW;
23347becc55SRui Miguel Silva 		break;
23447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_ENERGY_AVG:
23547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_ENERGY_AVG;
23647becc55SRui Miguel Silva 		break;
23747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CAPACITY:
23847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CAPACITY;
23947becc55SRui Miguel Silva 		break;
24047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
24147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN;
24247becc55SRui Miguel Silva 		break;
24347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX:
24447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX;
24547becc55SRui Miguel Silva 		break;
24647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CAPACITY_LEVEL:
24747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CAPACITY_LEVEL;
24847becc55SRui Miguel Silva 		break;
24947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP:
25047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP;
25147becc55SRui Miguel Silva 		break;
25247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_MAX:
25347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_MAX;
25447becc55SRui Miguel Silva 		break;
25547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_MIN:
25647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_MIN;
25747becc55SRui Miguel Silva 		break;
25847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
25947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
26047becc55SRui Miguel Silva 		break;
26147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
26247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
26347becc55SRui Miguel Silva 		break;
26447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT:
26547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_AMBIENT;
26647becc55SRui Miguel Silva 		break;
26747becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
26847becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
26947becc55SRui Miguel Silva 		break;
27047becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
27147becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
27247becc55SRui Miguel Silva 		break;
27347becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
27447becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW;
27547becc55SRui Miguel Silva 		break;
27647becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
27747becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG;
27847becc55SRui Miguel Silva 		break;
27947becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
28047becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW;
28147becc55SRui Miguel Silva 		break;
28247becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
28347becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TIME_TO_FULL_AVG;
28447becc55SRui Miguel Silva 		break;
28547becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_TYPE:
28647becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_TYPE;
28747becc55SRui Miguel Silva 		break;
28847becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_SCOPE:
28947becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_SCOPE;
29047becc55SRui Miguel Silva 		break;
29147becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
29247becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT;
29347becc55SRui Miguel Silva 		break;
29447becc55SRui Miguel Silva 	case GB_POWER_SUPPLY_PROP_CALIBRATE:
29547becc55SRui Miguel Silva 		prop = POWER_SUPPLY_PROP_CALIBRATE;
29647becc55SRui Miguel Silva 		break;
29747becc55SRui Miguel Silva 	default:
29847becc55SRui Miguel Silva 		prop = -1;
29947becc55SRui Miguel Silva 		break;
30047becc55SRui Miguel Silva 	}
30147becc55SRui Miguel Silva 
30247becc55SRui Miguel Silva 	if (prop < 0)
30347becc55SRui Miguel Silva 		return prop;
30447becc55SRui Miguel Silva 
30547becc55SRui Miguel Silva 	*psp = (enum power_supply_property)prop;
30647becc55SRui Miguel Silva 
30747becc55SRui Miguel Silva 	return 0;
30847becc55SRui Miguel Silva }
30947becc55SRui Miguel Silva 
get_conn_from_psy(struct gb_power_supply * gbpsy)310ffe2e248SRui Miguel Silva static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy)
311ffe2e248SRui Miguel Silva {
312ffe2e248SRui Miguel Silva 	return gbpsy->supplies->connection;
313ffe2e248SRui Miguel Silva }
314ffe2e248SRui Miguel Silva 
get_psy_prop(struct gb_power_supply * gbpsy,enum power_supply_property psp)315ffe2e248SRui Miguel Silva static struct gb_power_supply_prop *get_psy_prop(struct gb_power_supply *gbpsy,
316ffe2e248SRui Miguel Silva 						 enum power_supply_property psp)
317ffe2e248SRui Miguel Silva {
318ffe2e248SRui Miguel Silva 	int i;
319ffe2e248SRui Miguel Silva 
320ffe2e248SRui Miguel Silva 	for (i = 0; i < gbpsy->properties_count; i++)
321ffe2e248SRui Miguel Silva 		if (gbpsy->props[i].prop == psp)
322ffe2e248SRui Miguel Silva 			return &gbpsy->props[i];
323ffe2e248SRui Miguel Silva 	return NULL;
324ffe2e248SRui Miguel Silva }
325ffe2e248SRui Miguel Silva 
is_psy_prop_writeable(struct gb_power_supply * gbpsy,enum power_supply_property psp)326ffe2e248SRui Miguel Silva static int is_psy_prop_writeable(struct gb_power_supply *gbpsy,
327ffe2e248SRui Miguel Silva 				     enum power_supply_property psp)
328ffe2e248SRui Miguel Silva {
329ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop *prop;
330ffe2e248SRui Miguel Silva 
331ffe2e248SRui Miguel Silva 	prop = get_psy_prop(gbpsy, psp);
332ffe2e248SRui Miguel Silva 	if (!prop)
333ffe2e248SRui Miguel Silva 		return -ENOENT;
334ffe2e248SRui Miguel Silva 	return prop->is_writeable ? 1 : 0;
335ffe2e248SRui Miguel Silva }
336ffe2e248SRui Miguel Silva 
is_prop_valint(enum power_supply_property psp)337ffe2e248SRui Miguel Silva static int is_prop_valint(enum power_supply_property psp)
338ffe2e248SRui Miguel Silva {
339ffe2e248SRui Miguel Silva 	return ((psp < POWER_SUPPLY_PROP_MODEL_NAME) ? 1 : 0);
340ffe2e248SRui Miguel Silva }
341ffe2e248SRui Miguel Silva 
next_interval(struct gb_power_supply * gbpsy)342ffe2e248SRui Miguel Silva static void next_interval(struct gb_power_supply *gbpsy)
343ffe2e248SRui Miguel Silva {
344ffe2e248SRui Miguel Silva 	if (gbpsy->update_interval == update_interval_max)
345ffe2e248SRui Miguel Silva 		return;
346ffe2e248SRui Miguel Silva 
347ffe2e248SRui Miguel Silva 	/* do some exponential back-off in the update interval */
348ffe2e248SRui Miguel Silva 	gbpsy->update_interval *= 2;
349ffe2e248SRui Miguel Silva 	if (gbpsy->update_interval > update_interval_max)
350ffe2e248SRui Miguel Silva 		gbpsy->update_interval = update_interval_max;
351ffe2e248SRui Miguel Silva }
352ffe2e248SRui Miguel Silva 
__gb_power_supply_changed(struct gb_power_supply * gbpsy)353ffe2e248SRui Miguel Silva static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
354ffe2e248SRui Miguel Silva {
355ffe2e248SRui Miguel Silva 	power_supply_changed(gbpsy->psy);
356ffe2e248SRui Miguel Silva }
357ffe2e248SRui Miguel Silva 
gb_power_supply_state_change(struct gb_power_supply * gbpsy,struct gb_power_supply_prop * prop)3584e013b64SPhilip Yang static void gb_power_supply_state_change(struct gb_power_supply *gbpsy,
3594e013b64SPhilip Yang 					 struct gb_power_supply_prop *prop)
3604e013b64SPhilip Yang {
3614e013b64SPhilip Yang 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
3624e013b64SPhilip Yang 	int ret;
3634e013b64SPhilip Yang 
3644e013b64SPhilip Yang 	/*
3654e013b64SPhilip Yang 	 * Check gbpsy->pm_acquired to make sure only one pair of 'get_sync'
3664e013b64SPhilip Yang 	 * and 'put_autosuspend' runtime pm call for state property change.
3674e013b64SPhilip Yang 	 */
3684e013b64SPhilip Yang 	mutex_lock(&gbpsy->supply_lock);
3694e013b64SPhilip Yang 
3704e013b64SPhilip Yang 	if ((prop->val == GB_POWER_SUPPLY_STATUS_CHARGING) &&
3714e013b64SPhilip Yang 	    !gbpsy->pm_acquired) {
3724e013b64SPhilip Yang 		ret = gb_pm_runtime_get_sync(connection->bundle);
3734e013b64SPhilip Yang 		if (ret)
3744e013b64SPhilip Yang 			dev_err(&connection->bundle->dev,
3754e013b64SPhilip Yang 				"Fail to set wake lock for charging state\n");
3764e013b64SPhilip Yang 		else
3774e013b64SPhilip Yang 			gbpsy->pm_acquired = true;
3784e013b64SPhilip Yang 	} else {
3794e013b64SPhilip Yang 		if (gbpsy->pm_acquired) {
3804e013b64SPhilip Yang 			ret = gb_pm_runtime_put_autosuspend(connection->bundle);
3814e013b64SPhilip Yang 			if (ret)
3824e013b64SPhilip Yang 				dev_err(&connection->bundle->dev,
3834e013b64SPhilip Yang 					"Fail to set wake unlock for none charging\n");
3844e013b64SPhilip Yang 			else
3854e013b64SPhilip Yang 				gbpsy->pm_acquired = false;
3864e013b64SPhilip Yang 		}
3874e013b64SPhilip Yang 	}
3884e013b64SPhilip Yang 
3894e013b64SPhilip Yang 	mutex_unlock(&gbpsy->supply_lock);
3904e013b64SPhilip Yang }
3914e013b64SPhilip Yang 
check_changed(struct gb_power_supply * gbpsy,struct gb_power_supply_prop * prop)392ffe2e248SRui Miguel Silva static void check_changed(struct gb_power_supply *gbpsy,
393ffe2e248SRui Miguel Silva 			  struct gb_power_supply_prop *prop)
394ffe2e248SRui Miguel Silva {
395ffe2e248SRui Miguel Silva 	const struct gb_power_supply_changes *psyc;
3966e720c27SRui Miguel Silva 	int val = prop->val;
3976e720c27SRui Miguel Silva 	int prev_val = prop->previous_val;
398c4582f9dSRui Miguel Silva 	bool changed = false;
399ffe2e248SRui Miguel Silva 	int i;
400ffe2e248SRui Miguel Silva 
401ffe2e248SRui Miguel Silva 	for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) {
402ffe2e248SRui Miguel Silva 		psyc = &psy_props_changes[i];
403ffe2e248SRui Miguel Silva 		if (prop->prop == psyc->prop) {
404ffe2e248SRui Miguel Silva 			if (!psyc->tolerance_change)
405c4582f9dSRui Miguel Silva 				changed = true;
406ffe2e248SRui Miguel Silva 			else if (val < prev_val &&
407ffe2e248SRui Miguel Silva 				 prev_val - val > psyc->tolerance_change)
408c4582f9dSRui Miguel Silva 				changed = true;
409ffe2e248SRui Miguel Silva 			else if (val > prev_val &&
410ffe2e248SRui Miguel Silva 				 val - prev_val > psyc->tolerance_change)
411c4582f9dSRui Miguel Silva 				changed = true;
412c4582f9dSRui Miguel Silva 
413c4582f9dSRui Miguel Silva 			if (changed && psyc->prop_changed)
414c4582f9dSRui Miguel Silva 				psyc->prop_changed(gbpsy, prop);
415c4582f9dSRui Miguel Silva 
416c4582f9dSRui Miguel Silva 			if (changed)
417ffe2e248SRui Miguel Silva 				gbpsy->changed = true;
418ffe2e248SRui Miguel Silva 			break;
419ffe2e248SRui Miguel Silva 		}
420ffe2e248SRui Miguel Silva 	}
421ffe2e248SRui Miguel Silva }
422ffe2e248SRui Miguel Silva 
total_props(struct gb_power_supply * gbpsy)423ffe2e248SRui Miguel Silva static int total_props(struct gb_power_supply *gbpsy)
424ffe2e248SRui Miguel Silva {
425ffe2e248SRui Miguel Silva 	/* this return the intval plus the strval properties */
426ffe2e248SRui Miguel Silva 	return (gbpsy->properties_count + gbpsy->properties_count_str);
427ffe2e248SRui Miguel Silva }
428ffe2e248SRui Miguel Silva 
prop_append(struct gb_power_supply * gbpsy,enum power_supply_property prop)429ffe2e248SRui Miguel Silva static void prop_append(struct gb_power_supply *gbpsy,
430ffe2e248SRui Miguel Silva 			enum power_supply_property prop)
431ffe2e248SRui Miguel Silva {
432ffe2e248SRui Miguel Silva 	enum power_supply_property *new_props_raw;
433ffe2e248SRui Miguel Silva 
434ffe2e248SRui Miguel Silva 	gbpsy->properties_count_str++;
435ffe2e248SRui Miguel Silva 	new_props_raw = krealloc(gbpsy->props_raw, total_props(gbpsy) *
436ffe2e248SRui Miguel Silva 				 sizeof(enum power_supply_property),
437ffe2e248SRui Miguel Silva 				 GFP_KERNEL);
438ffe2e248SRui Miguel Silva 	if (!new_props_raw)
439ffe2e248SRui Miguel Silva 		return;
440ffe2e248SRui Miguel Silva 	gbpsy->props_raw = new_props_raw;
441ffe2e248SRui Miguel Silva 	gbpsy->props_raw[total_props(gbpsy) - 1] = prop;
442ffe2e248SRui Miguel Silva }
443ffe2e248SRui Miguel Silva 
__gb_power_supply_set_name(char * init_name,char * name,size_t len)444ffe2e248SRui Miguel Silva static int __gb_power_supply_set_name(char *init_name, char *name, size_t len)
445ffe2e248SRui Miguel Silva {
446ffe2e248SRui Miguel Silva 	unsigned int i = 0;
447ffe2e248SRui Miguel Silva 	int ret = 0;
448ffe2e248SRui Miguel Silva 	struct power_supply *psy;
449ffe2e248SRui Miguel Silva 
450ffe2e248SRui Miguel Silva 	if (!strlen(init_name))
451ffe2e248SRui Miguel Silva 		init_name = "gb_power_supply";
452*a6611144SKumar Kartikeya Dwivedi 	strscpy(name, init_name, len);
453ffe2e248SRui Miguel Silva 
454ffe2e248SRui Miguel Silva 	while ((ret < len) && (psy = power_supply_get_by_name(name))) {
455ffe2e248SRui Miguel Silva 		power_supply_put(psy);
456722a133aSRui Miguel Silva 
457ffe2e248SRui Miguel Silva 		ret = snprintf(name, len, "%s_%u", init_name, ++i);
458ffe2e248SRui Miguel Silva 	}
459ffe2e248SRui Miguel Silva 	if (ret >= len)
460ffe2e248SRui Miguel Silva 		return -ENOMEM;
461ffe2e248SRui Miguel Silva 	return i;
462ffe2e248SRui Miguel Silva }
463ffe2e248SRui Miguel Silva 
_gb_power_supply_append_props(struct gb_power_supply * gbpsy)464ffe2e248SRui Miguel Silva static void _gb_power_supply_append_props(struct gb_power_supply *gbpsy)
465ffe2e248SRui Miguel Silva {
466ffe2e248SRui Miguel Silva 	if (strlen(gbpsy->manufacturer))
467ffe2e248SRui Miguel Silva 		prop_append(gbpsy, POWER_SUPPLY_PROP_MANUFACTURER);
468ffe2e248SRui Miguel Silva 	if (strlen(gbpsy->model_name))
469ffe2e248SRui Miguel Silva 		prop_append(gbpsy, POWER_SUPPLY_PROP_MODEL_NAME);
470ffe2e248SRui Miguel Silva 	if (strlen(gbpsy->serial_number))
471ffe2e248SRui Miguel Silva 		prop_append(gbpsy, POWER_SUPPLY_PROP_SERIAL_NUMBER);
472ffe2e248SRui Miguel Silva }
473ffe2e248SRui Miguel Silva 
gb_power_supply_description_get(struct gb_power_supply * gbpsy)474ffe2e248SRui Miguel Silva static int gb_power_supply_description_get(struct gb_power_supply *gbpsy)
475ffe2e248SRui Miguel Silva {
476ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
477ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_description_request req;
478ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_description_response resp;
479ffe2e248SRui Miguel Silva 	int ret;
480ffe2e248SRui Miguel Silva 
481ffe2e248SRui Miguel Silva 	req.psy_id = gbpsy->id;
482ffe2e248SRui Miguel Silva 
483ffe2e248SRui Miguel Silva 	ret = gb_operation_sync(connection,
484ffe2e248SRui Miguel Silva 				GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION,
485ffe2e248SRui Miguel Silva 				&req, sizeof(req), &resp, sizeof(resp));
486ffe2e248SRui Miguel Silva 	if (ret < 0)
487ffe2e248SRui Miguel Silva 		return ret;
488ffe2e248SRui Miguel Silva 
489ffe2e248SRui Miguel Silva 	gbpsy->manufacturer = kstrndup(resp.manufacturer, PROP_MAX, GFP_KERNEL);
490ffe2e248SRui Miguel Silva 	if (!gbpsy->manufacturer)
491ffe2e248SRui Miguel Silva 		return -ENOMEM;
492ffe2e248SRui Miguel Silva 	gbpsy->model_name = kstrndup(resp.model, PROP_MAX, GFP_KERNEL);
493ffe2e248SRui Miguel Silva 	if (!gbpsy->model_name)
494ffe2e248SRui Miguel Silva 		return -ENOMEM;
495ffe2e248SRui Miguel Silva 	gbpsy->serial_number = kstrndup(resp.serial_number, PROP_MAX,
496ffe2e248SRui Miguel Silva 				       GFP_KERNEL);
497ffe2e248SRui Miguel Silva 	if (!gbpsy->serial_number)
498ffe2e248SRui Miguel Silva 		return -ENOMEM;
499ffe2e248SRui Miguel Silva 
500ffe2e248SRui Miguel Silva 	gbpsy->type = le16_to_cpu(resp.type);
501ffe2e248SRui Miguel Silva 	gbpsy->properties_count = resp.properties_count;
502ffe2e248SRui Miguel Silva 
503ffe2e248SRui Miguel Silva 	return 0;
504ffe2e248SRui Miguel Silva }
505ffe2e248SRui Miguel Silva 
gb_power_supply_prop_descriptors_get(struct gb_power_supply * gbpsy)506ffe2e248SRui Miguel Silva static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy)
507ffe2e248SRui Miguel Silva {
508ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
5099d15134dSRui Miguel Silva 	struct gb_power_supply_get_property_descriptors_request *req;
5109d15134dSRui Miguel Silva 	struct gb_power_supply_get_property_descriptors_response *resp;
5119d15134dSRui Miguel Silva 	struct gb_operation *op;
5129d15134dSRui Miguel Silva 	u8 props_count = gbpsy->properties_count;
51347becc55SRui Miguel Silva 	enum power_supply_property psp;
514ffe2e248SRui Miguel Silva 	int ret;
51547becc55SRui Miguel Silva 	int i, r = 0;
516ffe2e248SRui Miguel Silva 
5179d15134dSRui Miguel Silva 	if (props_count == 0)
518ffe2e248SRui Miguel Silva 		return 0;
519ffe2e248SRui Miguel Silva 
5209d15134dSRui Miguel Silva 	op = gb_operation_create(connection,
521ffe2e248SRui Miguel Silva 				 GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS,
522827c085bSGustavo A. R. Silva 				 sizeof(*req),
523827c085bSGustavo A. R. Silva 				 struct_size(resp, props, props_count),
5249d15134dSRui Miguel Silva 				 GFP_KERNEL);
5259d15134dSRui Miguel Silva 	if (!op)
5269d15134dSRui Miguel Silva 		return -ENOMEM;
5279d15134dSRui Miguel Silva 
5289d15134dSRui Miguel Silva 	req = op->request->payload;
5299d15134dSRui Miguel Silva 	req->psy_id = gbpsy->id;
5309d15134dSRui Miguel Silva 
5319d15134dSRui Miguel Silva 	ret = gb_operation_request_send_sync(op);
532ffe2e248SRui Miguel Silva 	if (ret < 0)
5339d15134dSRui Miguel Silva 		goto out_put_operation;
5349d15134dSRui Miguel Silva 
5359d15134dSRui Miguel Silva 	resp = op->response->payload;
536ffe2e248SRui Miguel Silva 
53747becc55SRui Miguel Silva 	/* validate received properties */
53847becc55SRui Miguel Silva 	for (i = 0; i < props_count; i++) {
53947becc55SRui Miguel Silva 		ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
54047becc55SRui Miguel Silva 		if (ret < 0) {
54147becc55SRui Miguel Silva 			dev_warn(&connection->bundle->dev,
54247becc55SRui Miguel Silva 				 "greybus property %u it is not supported by this kernel, dropped\n",
54347becc55SRui Miguel Silva 				 resp->props[i].property);
54447becc55SRui Miguel Silva 			gbpsy->properties_count--;
54547becc55SRui Miguel Silva 		}
54647becc55SRui Miguel Silva 	}
54747becc55SRui Miguel Silva 
548ffe2e248SRui Miguel Silva 	gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props),
549ffe2e248SRui Miguel Silva 			      GFP_KERNEL);
5509d15134dSRui Miguel Silva 	if (!gbpsy->props) {
5519d15134dSRui Miguel Silva 		ret = -ENOMEM;
5529d15134dSRui Miguel Silva 		goto out_put_operation;
5539d15134dSRui Miguel Silva 	}
554ffe2e248SRui Miguel Silva 
5559d15134dSRui Miguel Silva 	gbpsy->props_raw = kcalloc(gbpsy->properties_count,
556ffe2e248SRui Miguel Silva 				   sizeof(*gbpsy->props_raw), GFP_KERNEL);
5579d15134dSRui Miguel Silva 	if (!gbpsy->props_raw) {
5589d15134dSRui Miguel Silva 		ret = -ENOMEM;
5599d15134dSRui Miguel Silva 		goto out_put_operation;
5609d15134dSRui Miguel Silva 	}
5619d15134dSRui Miguel Silva 
56247becc55SRui Miguel Silva 	/* Store available properties, skip the ones we do not support */
56347becc55SRui Miguel Silva 	for (i = 0; i < props_count; i++) {
56447becc55SRui Miguel Silva 		ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
56547becc55SRui Miguel Silva 		if (ret < 0) {
56647becc55SRui Miguel Silva 			r++;
56747becc55SRui Miguel Silva 			continue;
56847becc55SRui Miguel Silva 		}
56947becc55SRui Miguel Silva 		gbpsy->props[i - r].prop = psp;
57047becc55SRui Miguel Silva 		gbpsy->props[i - r].gb_prop = resp->props[i].property;
57147becc55SRui Miguel Silva 		gbpsy->props_raw[i - r] = psp;
5729d15134dSRui Miguel Silva 		if (resp->props[i].is_writeable)
57347becc55SRui Miguel Silva 			gbpsy->props[i - r].is_writeable = true;
574ffe2e248SRui Miguel Silva 	}
57546488841SRui Miguel Silva 
57646488841SRui Miguel Silva 	/*
577ffe2e248SRui Miguel Silva 	 * now append the properties that we already got information in the
578ffe2e248SRui Miguel Silva 	 * get_description operation. (char * ones)
57946488841SRui Miguel Silva 	 */
580ffe2e248SRui Miguel Silva 	_gb_power_supply_append_props(gbpsy);
581ffe2e248SRui Miguel Silva 
58247becc55SRui Miguel Silva 	ret = 0;
5839d15134dSRui Miguel Silva out_put_operation:
5849d15134dSRui Miguel Silva 	gb_operation_put(op);
5859d15134dSRui Miguel Silva 
5869d15134dSRui Miguel Silva 	return ret;
58746488841SRui Miguel Silva }
58846488841SRui Miguel Silva 
__gb_power_supply_property_update(struct gb_power_supply * gbpsy,enum power_supply_property psp)589ffe2e248SRui Miguel Silva static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy,
590ffe2e248SRui Miguel Silva 					     enum power_supply_property psp)
59146488841SRui Miguel Silva {
592ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
593ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop *prop;
594ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_property_request req;
595ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_property_response resp;
5966e720c27SRui Miguel Silva 	int val;
597ffe2e248SRui Miguel Silva 	int ret;
59846488841SRui Miguel Silva 
599ffe2e248SRui Miguel Silva 	prop = get_psy_prop(gbpsy, psp);
600ffe2e248SRui Miguel Silva 	if (!prop)
601ffe2e248SRui Miguel Silva 		return -EINVAL;
602ffe2e248SRui Miguel Silva 	req.psy_id = gbpsy->id;
60347becc55SRui Miguel Silva 	req.property = prop->gb_prop;
604ffe2e248SRui Miguel Silva 
605ffe2e248SRui Miguel Silva 	ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY,
606ffe2e248SRui Miguel Silva 				&req, sizeof(req), &resp, sizeof(resp));
607ffe2e248SRui Miguel Silva 	if (ret < 0)
608ffe2e248SRui Miguel Silva 		return ret;
609ffe2e248SRui Miguel Silva 
610ffe2e248SRui Miguel Silva 	val = le32_to_cpu(resp.prop_val);
611ffe2e248SRui Miguel Silva 	if (val == prop->val)
612ffe2e248SRui Miguel Silva 		return 0;
613ffe2e248SRui Miguel Silva 
614ffe2e248SRui Miguel Silva 	prop->previous_val = prop->val;
615ffe2e248SRui Miguel Silva 	prop->val = val;
616ffe2e248SRui Miguel Silva 
617ffe2e248SRui Miguel Silva 	check_changed(gbpsy, prop);
618ffe2e248SRui Miguel Silva 
619ffe2e248SRui Miguel Silva 	return 0;
620ffe2e248SRui Miguel Silva }
621ffe2e248SRui Miguel Silva 
__gb_power_supply_property_get(struct gb_power_supply * gbpsy,enum power_supply_property psp,union power_supply_propval * val)622ffe2e248SRui Miguel Silva static int __gb_power_supply_property_get(struct gb_power_supply *gbpsy,
623ffe2e248SRui Miguel Silva 					  enum power_supply_property psp,
624ffe2e248SRui Miguel Silva 					  union power_supply_propval *val)
625ffe2e248SRui Miguel Silva {
626ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop *prop;
627ffe2e248SRui Miguel Silva 
628ffe2e248SRui Miguel Silva 	prop = get_psy_prop(gbpsy, psp);
629ffe2e248SRui Miguel Silva 	if (!prop)
630ffe2e248SRui Miguel Silva 		return -EINVAL;
631ffe2e248SRui Miguel Silva 
632ffe2e248SRui Miguel Silva 	val->intval = prop->val;
633ffe2e248SRui Miguel Silva 	return 0;
634ffe2e248SRui Miguel Silva }
635ffe2e248SRui Miguel Silva 
__gb_power_supply_property_strval_get(struct gb_power_supply * gbpsy,enum power_supply_property psp,union power_supply_propval * val)636ffe2e248SRui Miguel Silva static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy,
637ffe2e248SRui Miguel Silva 						enum power_supply_property psp,
638ffe2e248SRui Miguel Silva 						union power_supply_propval *val)
639ffe2e248SRui Miguel Silva {
640ffe2e248SRui Miguel Silva 	switch (psp) {
641ffe2e248SRui Miguel Silva 	case POWER_SUPPLY_PROP_MODEL_NAME:
642f921fb13SRui Miguel Silva 		val->strval = gbpsy->model_name;
643ffe2e248SRui Miguel Silva 		break;
644ffe2e248SRui Miguel Silva 	case POWER_SUPPLY_PROP_MANUFACTURER:
645f921fb13SRui Miguel Silva 		val->strval = gbpsy->manufacturer;
646ffe2e248SRui Miguel Silva 		break;
647ffe2e248SRui Miguel Silva 	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
648f921fb13SRui Miguel Silva 		val->strval = gbpsy->serial_number;
649ffe2e248SRui Miguel Silva 		break;
650ffe2e248SRui Miguel Silva 	default:
651ffe2e248SRui Miguel Silva 		break;
652ffe2e248SRui Miguel Silva 	}
653ffe2e248SRui Miguel Silva 
654ffe2e248SRui Miguel Silva 	return 0;
655ffe2e248SRui Miguel Silva }
656ffe2e248SRui Miguel Silva 
_gb_power_supply_property_get(struct gb_power_supply * gbpsy,enum power_supply_property psp,union power_supply_propval * val)657ffe2e248SRui Miguel Silva static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy,
658ffe2e248SRui Miguel Silva 					 enum power_supply_property psp,
659ffe2e248SRui Miguel Silva 					 union power_supply_propval *val)
660ffe2e248SRui Miguel Silva {
661ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
662ffe2e248SRui Miguel Silva 	int ret;
66346488841SRui Miguel Silva 
66446488841SRui Miguel Silva 	/*
665ffe2e248SRui Miguel Silva 	 * Properties of type const char *, were already fetched on
666ffe2e248SRui Miguel Silva 	 * get_description operation and should be cached in gb
66746488841SRui Miguel Silva 	 */
668ffe2e248SRui Miguel Silva 	if (is_prop_valint(psp))
669ffe2e248SRui Miguel Silva 		ret = __gb_power_supply_property_get(gbpsy, psp, val);
670ffe2e248SRui Miguel Silva 	else
671ffe2e248SRui Miguel Silva 		ret = __gb_power_supply_property_strval_get(gbpsy, psp, val);
672ffe2e248SRui Miguel Silva 
673ffe2e248SRui Miguel Silva 	if (ret < 0)
674ffe2e248SRui Miguel Silva 		dev_err(&connection->bundle->dev, "get property %u\n", psp);
675ffe2e248SRui Miguel Silva 
676ffe2e248SRui Miguel Silva 	return 0;
67746488841SRui Miguel Silva }
67846488841SRui Miguel Silva 
is_cache_valid(struct gb_power_supply * gbpsy)679b5fbe819SRui Miguel Silva static int is_cache_valid(struct gb_power_supply *gbpsy)
680b5fbe819SRui Miguel Silva {
681b5fbe819SRui Miguel Silva 	/* check if cache is good enough or it has expired */
682b5fbe819SRui Miguel Silva 	if (gbpsy->cache_invalid) {
683b5fbe819SRui Miguel Silva 		gbpsy->cache_invalid = 0;
684b5fbe819SRui Miguel Silva 		return 0;
685b5fbe819SRui Miguel Silva 	}
686b5fbe819SRui Miguel Silva 
687b5fbe819SRui Miguel Silva 	if (gbpsy->last_update &&
688b5fbe819SRui Miguel Silva 	    time_is_after_jiffies(gbpsy->last_update +
689b5fbe819SRui Miguel Silva 				  msecs_to_jiffies(cache_time)))
690b5fbe819SRui Miguel Silva 		return 1;
691b5fbe819SRui Miguel Silva 
692b5fbe819SRui Miguel Silva 	return 0;
693b5fbe819SRui Miguel Silva }
694b5fbe819SRui Miguel Silva 
gb_power_supply_status_get(struct gb_power_supply * gbpsy)695ffe2e248SRui Miguel Silva static int gb_power_supply_status_get(struct gb_power_supply *gbpsy)
69646488841SRui Miguel Silva {
6974e013b64SPhilip Yang 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
698ffe2e248SRui Miguel Silva 	int ret = 0;
699ffe2e248SRui Miguel Silva 	int i;
70046488841SRui Miguel Silva 
701b5fbe819SRui Miguel Silva 	if (is_cache_valid(gbpsy))
702ffe2e248SRui Miguel Silva 		return 0;
70346488841SRui Miguel Silva 
7044e013b64SPhilip Yang 	ret = gb_pm_runtime_get_sync(connection->bundle);
7054e013b64SPhilip Yang 	if (ret)
7064e013b64SPhilip Yang 		return ret;
7074e013b64SPhilip Yang 
708ffe2e248SRui Miguel Silva 	for (i = 0; i < gbpsy->properties_count; i++) {
709ffe2e248SRui Miguel Silva 		ret = __gb_power_supply_property_update(gbpsy,
710ffe2e248SRui Miguel Silva 							gbpsy->props[i].prop);
711ffe2e248SRui Miguel Silva 		if (ret < 0)
712ffe2e248SRui Miguel Silva 			break;
71346488841SRui Miguel Silva 	}
71446488841SRui Miguel Silva 
715ffe2e248SRui Miguel Silva 	if (ret == 0)
716ffe2e248SRui Miguel Silva 		gbpsy->last_update = jiffies;
71746488841SRui Miguel Silva 
7184e013b64SPhilip Yang 	gb_pm_runtime_put_autosuspend(connection->bundle);
719ffe2e248SRui Miguel Silva 	return ret;
72046488841SRui Miguel Silva }
72146488841SRui Miguel Silva 
gb_power_supply_status_update(struct gb_power_supply * gbpsy)722ffe2e248SRui Miguel Silva static void gb_power_supply_status_update(struct gb_power_supply *gbpsy)
72346488841SRui Miguel Silva {
724ffe2e248SRui Miguel Silva 	/* check if there a change that need to be reported */
725ffe2e248SRui Miguel Silva 	gb_power_supply_status_get(gbpsy);
72646488841SRui Miguel Silva 
727ffe2e248SRui Miguel Silva 	if (!gbpsy->changed)
728ffe2e248SRui Miguel Silva 		return;
72946488841SRui Miguel Silva 
730ffe2e248SRui Miguel Silva 	gbpsy->update_interval = update_interval_init;
731ffe2e248SRui Miguel Silva 	__gb_power_supply_changed(gbpsy);
732ffe2e248SRui Miguel Silva 	gbpsy->changed = false;
73346488841SRui Miguel Silva }
73446488841SRui Miguel Silva 
gb_power_supply_work(struct work_struct * work)735ffe2e248SRui Miguel Silva static void gb_power_supply_work(struct work_struct *work)
73646488841SRui Miguel Silva {
737ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = container_of(work,
738ffe2e248SRui Miguel Silva 						     struct gb_power_supply,
739ffe2e248SRui Miguel Silva 						     work.work);
74046488841SRui Miguel Silva 
741ffe2e248SRui Miguel Silva 	/*
742ffe2e248SRui Miguel Silva 	 * if the poll interval is not set, disable polling, this is helpful
743ffe2e248SRui Miguel Silva 	 * specially at unregister time.
744ffe2e248SRui Miguel Silva 	 */
745ffe2e248SRui Miguel Silva 	if (!gbpsy->update_interval)
746ffe2e248SRui Miguel Silva 		return;
74746488841SRui Miguel Silva 
748ffe2e248SRui Miguel Silva 	gb_power_supply_status_update(gbpsy);
749ffe2e248SRui Miguel Silva 	next_interval(gbpsy);
750ffe2e248SRui Miguel Silva 	schedule_delayed_work(&gbpsy->work, gbpsy->update_interval);
75146488841SRui Miguel Silva }
75246488841SRui Miguel Silva 
get_property(struct power_supply * b,enum power_supply_property psp,union power_supply_propval * val)75346488841SRui Miguel Silva static int get_property(struct power_supply *b,
75446488841SRui Miguel Silva 			enum power_supply_property psp,
75546488841SRui Miguel Silva 			union power_supply_propval *val)
75646488841SRui Miguel Silva {
757ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = to_gb_power_supply(b);
75846488841SRui Miguel Silva 
759ffe2e248SRui Miguel Silva 	gb_power_supply_status_get(gbpsy);
76046488841SRui Miguel Silva 
761ffe2e248SRui Miguel Silva 	return _gb_power_supply_property_get(gbpsy, psp, val);
762ffe2e248SRui Miguel Silva }
76346488841SRui Miguel Silva 
gb_power_supply_property_set(struct gb_power_supply * gbpsy,enum power_supply_property psp,int val)764ffe2e248SRui Miguel Silva static int gb_power_supply_property_set(struct gb_power_supply *gbpsy,
765ffe2e248SRui Miguel Silva 					enum power_supply_property psp,
766ffe2e248SRui Miguel Silva 					int val)
767ffe2e248SRui Miguel Silva {
768ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
769ffe2e248SRui Miguel Silva 	struct gb_power_supply_prop *prop;
770ffe2e248SRui Miguel Silva 	struct gb_power_supply_set_property_request req;
771ffe2e248SRui Miguel Silva 	int ret;
77246488841SRui Miguel Silva 
7734e013b64SPhilip Yang 	ret = gb_pm_runtime_get_sync(connection->bundle);
7744e013b64SPhilip Yang 	if (ret)
7754e013b64SPhilip Yang 		return ret;
7764e013b64SPhilip Yang 
777ffe2e248SRui Miguel Silva 	prop = get_psy_prop(gbpsy, psp);
7784e013b64SPhilip Yang 	if (!prop) {
7794e013b64SPhilip Yang 		ret = -EINVAL;
7804e013b64SPhilip Yang 		goto out;
7814e013b64SPhilip Yang 	}
7824e013b64SPhilip Yang 
783ffe2e248SRui Miguel Silva 	req.psy_id = gbpsy->id;
78447becc55SRui Miguel Silva 	req.property = prop->gb_prop;
7856e720c27SRui Miguel Silva 	req.prop_val = cpu_to_le32((s32)val);
786ffe2e248SRui Miguel Silva 
787ffe2e248SRui Miguel Silva 	ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY,
788ffe2e248SRui Miguel Silva 				&req, sizeof(req), NULL, 0);
789ffe2e248SRui Miguel Silva 	if (ret < 0)
790ffe2e248SRui Miguel Silva 		goto out;
791ffe2e248SRui Miguel Silva 
792ffe2e248SRui Miguel Silva 	/* cache immediately the new value */
793ffe2e248SRui Miguel Silva 	prop->val = val;
794ffe2e248SRui Miguel Silva 
795ffe2e248SRui Miguel Silva out:
7964e013b64SPhilip Yang 	gb_pm_runtime_put_autosuspend(connection->bundle);
797ffe2e248SRui Miguel Silva 	return ret;
79846488841SRui Miguel Silva }
79946488841SRui Miguel Silva 
set_property(struct power_supply * b,enum power_supply_property psp,const union power_supply_propval * val)800ffe2e248SRui Miguel Silva static int set_property(struct power_supply *b,
801ffe2e248SRui Miguel Silva 			enum power_supply_property psp,
802ffe2e248SRui Miguel Silva 			const union power_supply_propval *val)
803ffe2e248SRui Miguel Silva {
804ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = to_gb_power_supply(b);
805ffe2e248SRui Miguel Silva 
806ffe2e248SRui Miguel Silva 	return gb_power_supply_property_set(gbpsy, psp, val->intval);
80746488841SRui Miguel Silva }
80846488841SRui Miguel Silva 
property_is_writeable(struct power_supply * b,enum power_supply_property psp)809ffe2e248SRui Miguel Silva static int property_is_writeable(struct power_supply *b,
810ffe2e248SRui Miguel Silva 				 enum power_supply_property psp)
811ffe2e248SRui Miguel Silva {
812ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = to_gb_power_supply(b);
813ffe2e248SRui Miguel Silva 
814ffe2e248SRui Miguel Silva 	return is_psy_prop_writeable(gbpsy, psp);
815ffe2e248SRui Miguel Silva }
816ffe2e248SRui Miguel Silva 
gb_power_supply_register(struct gb_power_supply * gbpsy)817ffe2e248SRui Miguel Silva static int gb_power_supply_register(struct gb_power_supply *gbpsy)
81846488841SRui Miguel Silva {
819ffe2e248SRui Miguel Silva 	struct gb_connection *connection = get_conn_from_psy(gbpsy);
82046488841SRui Miguel Silva 	struct power_supply_config cfg = {};
82146488841SRui Miguel Silva 
822ffe2e248SRui Miguel Silva 	cfg.drv_data = gbpsy;
82346488841SRui Miguel Silva 
824ffe2e248SRui Miguel Silva 	gbpsy->desc.name		= gbpsy->name;
825ffe2e248SRui Miguel Silva 	gbpsy->desc.type		= gbpsy->type;
826ffe2e248SRui Miguel Silva 	gbpsy->desc.properties		= gbpsy->props_raw;
827ffe2e248SRui Miguel Silva 	gbpsy->desc.num_properties	= total_props(gbpsy);
828ffe2e248SRui Miguel Silva 	gbpsy->desc.get_property	= get_property;
829ffe2e248SRui Miguel Silva 	gbpsy->desc.set_property	= set_property;
830ffe2e248SRui Miguel Silva 	gbpsy->desc.property_is_writeable = property_is_writeable;
83146488841SRui Miguel Silva 
832ffe2e248SRui Miguel Silva 	gbpsy->psy = power_supply_register(&connection->bundle->dev,
833ffe2e248SRui Miguel Silva 					   &gbpsy->desc, &cfg);
83495073cc2SAlex Elder 	return PTR_ERR_OR_ZERO(gbpsy->psy);
83546488841SRui Miguel Silva }
83646488841SRui Miguel Silva 
_gb_power_supply_free(struct gb_power_supply * gbpsy)837ffe2e248SRui Miguel Silva static void _gb_power_supply_free(struct gb_power_supply *gbpsy)
83846488841SRui Miguel Silva {
839ffe2e248SRui Miguel Silva 	kfree(gbpsy->serial_number);
840ffe2e248SRui Miguel Silva 	kfree(gbpsy->model_name);
841ffe2e248SRui Miguel Silva 	kfree(gbpsy->manufacturer);
842ffe2e248SRui Miguel Silva 	kfree(gbpsy->props_raw);
843ffe2e248SRui Miguel Silva 	kfree(gbpsy->props);
844ffe2e248SRui Miguel Silva }
84546488841SRui Miguel Silva 
_gb_power_supply_release(struct gb_power_supply * gbpsy)846ffe2e248SRui Miguel Silva static void _gb_power_supply_release(struct gb_power_supply *gbpsy)
847ffe2e248SRui Miguel Silva {
848ffe2e248SRui Miguel Silva 	gbpsy->update_interval = 0;
849ffe2e248SRui Miguel Silva 
850ffe2e248SRui Miguel Silva 	cancel_delayed_work_sync(&gbpsy->work);
851722a133aSRui Miguel Silva 
852ff85f723SRui Miguel Silva 	if (gbpsy->registered)
853ffe2e248SRui Miguel Silva 		power_supply_unregister(gbpsy->psy);
854ffe2e248SRui Miguel Silva 
855ffe2e248SRui Miguel Silva 	_gb_power_supply_free(gbpsy);
856ffe2e248SRui Miguel Silva }
857ffe2e248SRui Miguel Silva 
_gb_power_supplies_release(struct gb_power_supplies * supplies)858ffe2e248SRui Miguel Silva static void _gb_power_supplies_release(struct gb_power_supplies *supplies)
859ffe2e248SRui Miguel Silva {
860ffe2e248SRui Miguel Silva 	int i;
861ffe2e248SRui Miguel Silva 
8627ccac20dSRui Miguel Silva 	if (!supplies->supply)
8637ccac20dSRui Miguel Silva 		return;
8647ccac20dSRui Miguel Silva 
865ffe2e248SRui Miguel Silva 	mutex_lock(&supplies->supplies_lock);
866ffe2e248SRui Miguel Silva 	for (i = 0; i < supplies->supplies_count; i++)
867ffe2e248SRui Miguel Silva 		_gb_power_supply_release(&supplies->supply[i]);
868accad1baSRui Miguel Silva 	kfree(supplies->supply);
869ffe2e248SRui Miguel Silva 	mutex_unlock(&supplies->supplies_lock);
87023f25ba6SRui Miguel Silva 	kfree(supplies);
871ffe2e248SRui Miguel Silva }
872ffe2e248SRui Miguel Silva 
gb_power_supplies_get_count(struct gb_power_supplies * supplies)873ffe2e248SRui Miguel Silva static int gb_power_supplies_get_count(struct gb_power_supplies *supplies)
874ffe2e248SRui Miguel Silva {
875ffe2e248SRui Miguel Silva 	struct gb_power_supply_get_supplies_response resp;
876ffe2e248SRui Miguel Silva 	int ret;
877ffe2e248SRui Miguel Silva 
878ffe2e248SRui Miguel Silva 	ret = gb_operation_sync(supplies->connection,
879ffe2e248SRui Miguel Silva 				GB_POWER_SUPPLY_TYPE_GET_SUPPLIES,
880ffe2e248SRui Miguel Silva 				NULL, 0, &resp, sizeof(resp));
881ffe2e248SRui Miguel Silva 	if (ret < 0)
882ffe2e248SRui Miguel Silva 		return ret;
883ffe2e248SRui Miguel Silva 
884ffe2e248SRui Miguel Silva 	if  (!resp.supplies_count)
885ffe2e248SRui Miguel Silva 		return -EINVAL;
886ffe2e248SRui Miguel Silva 
887ffe2e248SRui Miguel Silva 	supplies->supplies_count = resp.supplies_count;
888ffe2e248SRui Miguel Silva 
889ffe2e248SRui Miguel Silva 	return ret;
890ffe2e248SRui Miguel Silva }
891ffe2e248SRui Miguel Silva 
gb_power_supply_config(struct gb_power_supplies * supplies,int id)892ffe2e248SRui Miguel Silva static int gb_power_supply_config(struct gb_power_supplies *supplies, int id)
893ffe2e248SRui Miguel Silva {
894ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy = &supplies->supply[id];
895ffe2e248SRui Miguel Silva 	int ret;
896ffe2e248SRui Miguel Silva 
897ffe2e248SRui Miguel Silva 	gbpsy->supplies = supplies;
898ffe2e248SRui Miguel Silva 	gbpsy->id = id;
899ffe2e248SRui Miguel Silva 
900ffe2e248SRui Miguel Silva 	ret = gb_power_supply_description_get(gbpsy);
901ffe2e248SRui Miguel Silva 	if (ret < 0)
9027e9fba8dSViresh Kumar 		return ret;
903ffe2e248SRui Miguel Silva 
9046ac9166dSRui Miguel Silva 	return gb_power_supply_prop_descriptors_get(gbpsy);
9057e9fba8dSViresh Kumar }
9067e9fba8dSViresh Kumar 
gb_power_supply_enable(struct gb_power_supply * gbpsy)9077e9fba8dSViresh Kumar static int gb_power_supply_enable(struct gb_power_supply *gbpsy)
9087e9fba8dSViresh Kumar {
9097e9fba8dSViresh Kumar 	int ret;
910ffe2e248SRui Miguel Silva 
9116ac9166dSRui Miguel Silva 	/* guarantee that we have an unique name, before register */
9126ac9166dSRui Miguel Silva 	ret =  __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name,
9136ac9166dSRui Miguel Silva 					  sizeof(gbpsy->name));
9146ac9166dSRui Miguel Silva 	if (ret < 0)
9156ac9166dSRui Miguel Silva 		return ret;
9166ac9166dSRui Miguel Silva 
9174e013b64SPhilip Yang 	mutex_init(&gbpsy->supply_lock);
9184e013b64SPhilip Yang 
919ffe2e248SRui Miguel Silva 	ret = gb_power_supply_register(gbpsy);
920ffe2e248SRui Miguel Silva 	if (ret < 0)
9217e9fba8dSViresh Kumar 		return ret;
922ffe2e248SRui Miguel Silva 
923ffe2e248SRui Miguel Silva 	gbpsy->update_interval = update_interval_init;
924ffe2e248SRui Miguel Silva 	INIT_DELAYED_WORK(&gbpsy->work, gb_power_supply_work);
925ffe2e248SRui Miguel Silva 	schedule_delayed_work(&gbpsy->work, 0);
926ffe2e248SRui Miguel Silva 
9277e9fba8dSViresh Kumar 	/* everything went fine, mark it for release code to know */
928ff85f723SRui Miguel Silva 	gbpsy->registered = true;
9297e9fba8dSViresh Kumar 
9307e9fba8dSViresh Kumar 	return 0;
931ffe2e248SRui Miguel Silva }
932ffe2e248SRui Miguel Silva 
gb_power_supplies_setup(struct gb_power_supplies * supplies)933ffe2e248SRui Miguel Silva static int gb_power_supplies_setup(struct gb_power_supplies *supplies)
934ffe2e248SRui Miguel Silva {
935ffe2e248SRui Miguel Silva 	struct gb_connection *connection = supplies->connection;
936ffe2e248SRui Miguel Silva 	int ret;
937ffe2e248SRui Miguel Silva 	int i;
938ffe2e248SRui Miguel Silva 
939ffe2e248SRui Miguel Silva 	mutex_lock(&supplies->supplies_lock);
940ffe2e248SRui Miguel Silva 
941ffe2e248SRui Miguel Silva 	ret = gb_power_supplies_get_count(supplies);
942ffe2e248SRui Miguel Silva 	if (ret < 0)
943ffe2e248SRui Miguel Silva 		goto out;
944ffe2e248SRui Miguel Silva 
94503d261deSJB Van Puyvelde 	supplies->supply = kcalloc(supplies->supplies_count,
946ffe2e248SRui Miguel Silva 				     sizeof(struct gb_power_supply),
947ffe2e248SRui Miguel Silva 				     GFP_KERNEL);
948ffe2e248SRui Miguel Silva 
949e0d91ff1SJohan Hovold 	if (!supplies->supply) {
950e0d91ff1SJohan Hovold 		ret = -ENOMEM;
951e0d91ff1SJohan Hovold 		goto out;
952e0d91ff1SJohan Hovold 	}
95346488841SRui Miguel Silva 
954ffe2e248SRui Miguel Silva 	for (i = 0; i < supplies->supplies_count; i++) {
955ffe2e248SRui Miguel Silva 		ret = gb_power_supply_config(supplies, i);
956ffe2e248SRui Miguel Silva 		if (ret < 0) {
957ffe2e248SRui Miguel Silva 			dev_err(&connection->bundle->dev,
958ffe2e248SRui Miguel Silva 				"Fail to configure supplies devices\n");
959ffe2e248SRui Miguel Silva 			goto out;
960ffe2e248SRui Miguel Silva 		}
961ffe2e248SRui Miguel Silva 	}
962ffe2e248SRui Miguel Silva out:
963ffe2e248SRui Miguel Silva 	mutex_unlock(&supplies->supplies_lock);
964ffe2e248SRui Miguel Silva 	return ret;
965ffe2e248SRui Miguel Silva }
96646488841SRui Miguel Silva 
gb_power_supplies_register(struct gb_power_supplies * supplies)9677e9fba8dSViresh Kumar static int gb_power_supplies_register(struct gb_power_supplies *supplies)
9687e9fba8dSViresh Kumar {
9697e9fba8dSViresh Kumar 	struct gb_connection *connection = supplies->connection;
9707e9fba8dSViresh Kumar 	int ret = 0;
9717e9fba8dSViresh Kumar 	int i;
9727e9fba8dSViresh Kumar 
9737e9fba8dSViresh Kumar 	mutex_lock(&supplies->supplies_lock);
9747e9fba8dSViresh Kumar 
9757e9fba8dSViresh Kumar 	for (i = 0; i < supplies->supplies_count; i++) {
9767e9fba8dSViresh Kumar 		ret = gb_power_supply_enable(&supplies->supply[i]);
9777e9fba8dSViresh Kumar 		if (ret < 0) {
9787e9fba8dSViresh Kumar 			dev_err(&connection->bundle->dev,
9797e9fba8dSViresh Kumar 				"Fail to enable supplies devices\n");
9807e9fba8dSViresh Kumar 			break;
9817e9fba8dSViresh Kumar 		}
9827e9fba8dSViresh Kumar 	}
9837e9fba8dSViresh Kumar 
9847e9fba8dSViresh Kumar 	mutex_unlock(&supplies->supplies_lock);
9857e9fba8dSViresh Kumar 	return ret;
9867e9fba8dSViresh Kumar }
9877e9fba8dSViresh Kumar 
gb_supplies_request_handler(struct gb_operation * op)98868b1309bSViresh Kumar static int gb_supplies_request_handler(struct gb_operation *op)
989ffe2e248SRui Miguel Silva {
990ffe2e248SRui Miguel Silva 	struct gb_connection *connection = op->connection;
9910ec30632SGreg Kroah-Hartman 	struct gb_power_supplies *supplies = gb_connection_get_data(connection);
992ffe2e248SRui Miguel Silva 	struct gb_power_supply *gbpsy;
993ffe2e248SRui Miguel Silva 	struct gb_message *request;
994ffe2e248SRui Miguel Silva 	struct gb_power_supply_event_request *payload;
995ffe2e248SRui Miguel Silva 	u8 psy_id;
996ffe2e248SRui Miguel Silva 	u8 event;
997ffe2e248SRui Miguel Silva 	int ret = 0;
99846488841SRui Miguel Silva 
99968b1309bSViresh Kumar 	if (op->type != GB_POWER_SUPPLY_TYPE_EVENT) {
1000ffe2e248SRui Miguel Silva 		dev_err(&connection->bundle->dev,
100168b1309bSViresh Kumar 			"Unsupported unsolicited event: %u\n", op->type);
1002ffe2e248SRui Miguel Silva 		return -EINVAL;
1003ffe2e248SRui Miguel Silva 	}
1004ffe2e248SRui Miguel Silva 
1005ffe2e248SRui Miguel Silva 	request = op->request;
1006ffe2e248SRui Miguel Silva 
1007ffe2e248SRui Miguel Silva 	if (request->payload_size < sizeof(*payload)) {
1008ffe2e248SRui Miguel Silva 		dev_err(&connection->bundle->dev,
1009ffe2e248SRui Miguel Silva 			"Wrong event size received (%zu < %zu)\n",
1010ffe2e248SRui Miguel Silva 			request->payload_size, sizeof(*payload));
1011ffe2e248SRui Miguel Silva 		return -EINVAL;
1012ffe2e248SRui Miguel Silva 	}
1013ffe2e248SRui Miguel Silva 
1014ffe2e248SRui Miguel Silva 	payload = request->payload;
1015ffe2e248SRui Miguel Silva 	psy_id = payload->psy_id;
1016ffe2e248SRui Miguel Silva 	mutex_lock(&supplies->supplies_lock);
1017adb57cffSRui Miguel Silva 	if (psy_id >= supplies->supplies_count ||
1018adb57cffSRui Miguel Silva 	    !supplies->supply[psy_id].registered) {
1019ffe2e248SRui Miguel Silva 		dev_err(&connection->bundle->dev,
1020ffe2e248SRui Miguel Silva 			"Event received for unconfigured power_supply id: %d\n",
1021ffe2e248SRui Miguel Silva 			psy_id);
1022ffe2e248SRui Miguel Silva 		ret = -EINVAL;
1023ffe2e248SRui Miguel Silva 		goto out_unlock;
1024ffe2e248SRui Miguel Silva 	}
1025ffe2e248SRui Miguel Silva 
1026ffe2e248SRui Miguel Silva 	event = payload->event;
1027ffe2e248SRui Miguel Silva 	/*
1028ffe2e248SRui Miguel Silva 	 * we will only handle events after setup is done and before release is
1029ffe2e248SRui Miguel Silva 	 * running. For that just check update_interval.
1030ffe2e248SRui Miguel Silva 	 */
1031ffe2e248SRui Miguel Silva 	gbpsy = &supplies->supply[psy_id];
10325f66d62eSRui Miguel Silva 	if (!gbpsy->update_interval) {
1033ffe2e248SRui Miguel Silva 		ret = -ESHUTDOWN;
1034ffe2e248SRui Miguel Silva 		goto out_unlock;
1035ffe2e248SRui Miguel Silva 	}
1036ffe2e248SRui Miguel Silva 
1037b5fbe819SRui Miguel Silva 	if (event & GB_POWER_SUPPLY_UPDATE) {
1038b5fbe819SRui Miguel Silva 		/*
1039b5fbe819SRui Miguel Silva 		 * we need to make sure we invalidate cache, if not no new
1040b5fbe819SRui Miguel Silva 		 * values for the properties will be fetch and the all propose
1041b5fbe819SRui Miguel Silva 		 * of this event is missed
1042b5fbe819SRui Miguel Silva 		 */
1043b5fbe819SRui Miguel Silva 		gbpsy->cache_invalid = 1;
1044ffe2e248SRui Miguel Silva 		gb_power_supply_status_update(gbpsy);
1045b5fbe819SRui Miguel Silva 	}
1046ffe2e248SRui Miguel Silva 
1047ffe2e248SRui Miguel Silva out_unlock:
1048ffe2e248SRui Miguel Silva 	mutex_unlock(&supplies->supplies_lock);
1049ffe2e248SRui Miguel Silva 	return ret;
1050ffe2e248SRui Miguel Silva }
1051ffe2e248SRui Miguel Silva 
gb_power_supply_probe(struct gb_bundle * bundle,const struct greybus_bundle_id * id)105268b1309bSViresh Kumar static int gb_power_supply_probe(struct gb_bundle *bundle,
105368b1309bSViresh Kumar 				 const struct greybus_bundle_id *id)
1054ffe2e248SRui Miguel Silva {
105568b1309bSViresh Kumar 	struct greybus_descriptor_cport *cport_desc;
105668b1309bSViresh Kumar 	struct gb_connection *connection;
1057ffe2e248SRui Miguel Silva 	struct gb_power_supplies *supplies;
1058d9eafd58SRui Miguel Silva 	int ret;
1059ffe2e248SRui Miguel Silva 
106068b1309bSViresh Kumar 	if (bundle->num_cports != 1)
106168b1309bSViresh Kumar 		return -ENODEV;
106268b1309bSViresh Kumar 
106368b1309bSViresh Kumar 	cport_desc = &bundle->cport_desc[0];
106468b1309bSViresh Kumar 	if (cport_desc->protocol_id != GREYBUS_PROTOCOL_POWER_SUPPLY)
106568b1309bSViresh Kumar 		return -ENODEV;
106668b1309bSViresh Kumar 
1067ffe2e248SRui Miguel Silva 	supplies = kzalloc(sizeof(*supplies), GFP_KERNEL);
1068ffe2e248SRui Miguel Silva 	if (!supplies)
1069ffe2e248SRui Miguel Silva 		return -ENOMEM;
1070ffe2e248SRui Miguel Silva 
107168b1309bSViresh Kumar 	connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
107268b1309bSViresh Kumar 					  gb_supplies_request_handler);
107368b1309bSViresh Kumar 	if (IS_ERR(connection)) {
107468b1309bSViresh Kumar 		ret = PTR_ERR(connection);
107568b1309bSViresh Kumar 		goto out;
107668b1309bSViresh Kumar 	}
107768b1309bSViresh Kumar 
1078ffe2e248SRui Miguel Silva 	supplies->connection = connection;
10790ec30632SGreg Kroah-Hartman 	gb_connection_set_data(connection, supplies);
1080ffe2e248SRui Miguel Silva 
1081ffe2e248SRui Miguel Silva 	mutex_init(&supplies->supplies_lock);
1082ffe2e248SRui Miguel Silva 
108368b1309bSViresh Kumar 	greybus_set_drvdata(bundle, supplies);
108468b1309bSViresh Kumar 
108568b1309bSViresh Kumar 	/* We aren't ready to receive an incoming request yet */
108668b1309bSViresh Kumar 	ret = gb_connection_enable_tx(connection);
108768b1309bSViresh Kumar 	if (ret)
108868b1309bSViresh Kumar 		goto error_connection_destroy;
108968b1309bSViresh Kumar 
1090d9eafd58SRui Miguel Silva 	ret = gb_power_supplies_setup(supplies);
1091d9eafd58SRui Miguel Silva 	if (ret < 0)
109268b1309bSViresh Kumar 		goto error_connection_disable;
109368b1309bSViresh Kumar 
109468b1309bSViresh Kumar 	/* We are ready to receive an incoming request now, enable RX as well */
109568b1309bSViresh Kumar 	ret = gb_connection_enable(connection);
109668b1309bSViresh Kumar 	if (ret)
109768b1309bSViresh Kumar 		goto error_connection_disable;
1098d9eafd58SRui Miguel Silva 
10997e9fba8dSViresh Kumar 	ret = gb_power_supplies_register(supplies);
11007e9fba8dSViresh Kumar 	if (ret < 0)
110168b1309bSViresh Kumar 		goto error_connection_disable;
11027e9fba8dSViresh Kumar 
11034e013b64SPhilip Yang 	gb_pm_runtime_put_autosuspend(bundle);
11047e9fba8dSViresh Kumar 	return 0;
11057e9fba8dSViresh Kumar 
110668b1309bSViresh Kumar error_connection_disable:
110768b1309bSViresh Kumar 	gb_connection_disable(connection);
110868b1309bSViresh Kumar error_connection_destroy:
110968b1309bSViresh Kumar 	gb_connection_destroy(connection);
11107e9fba8dSViresh Kumar out:
11117e9fba8dSViresh Kumar 	_gb_power_supplies_release(supplies);
1112d9eafd58SRui Miguel Silva 	return ret;
111346488841SRui Miguel Silva }
111446488841SRui Miguel Silva 
gb_power_supply_disconnect(struct gb_bundle * bundle)111568b1309bSViresh Kumar static void gb_power_supply_disconnect(struct gb_bundle *bundle)
111646488841SRui Miguel Silva {
111768b1309bSViresh Kumar 	struct gb_power_supplies *supplies = greybus_get_drvdata(bundle);
111868b1309bSViresh Kumar 
111968b1309bSViresh Kumar 	gb_connection_disable(supplies->connection);
112068b1309bSViresh Kumar 	gb_connection_destroy(supplies->connection);
112146488841SRui Miguel Silva 
1122ffe2e248SRui Miguel Silva 	_gb_power_supplies_release(supplies);
112346488841SRui Miguel Silva }
112446488841SRui Miguel Silva 
112568b1309bSViresh Kumar static const struct greybus_bundle_id gb_power_supply_id_table[] = {
112668b1309bSViresh Kumar 	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) },
112768b1309bSViresh Kumar 	{ }
112846488841SRui Miguel Silva };
112968b1309bSViresh Kumar MODULE_DEVICE_TABLE(greybus, gb_power_supply_id_table);
113046488841SRui Miguel Silva 
113168b1309bSViresh Kumar static struct greybus_driver gb_power_supply_driver = {
113268b1309bSViresh Kumar 	.name		= "power_supply",
113368b1309bSViresh Kumar 	.probe		= gb_power_supply_probe,
113468b1309bSViresh Kumar 	.disconnect	= gb_power_supply_disconnect,
113568b1309bSViresh Kumar 	.id_table	= gb_power_supply_id_table,
113668b1309bSViresh Kumar };
113768b1309bSViresh Kumar module_greybus_driver(gb_power_supply_driver);
113846488841SRui Miguel Silva 
113946488841SRui Miguel Silva MODULE_LICENSE("GPL v2");
1140