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 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 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 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 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 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 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 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 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 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 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 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 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"; 452ffe2e248SRui Miguel Silva strlcpy(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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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