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