aboutsummaryrefslogtreecommitdiffstats
path: root/greybus_power.patch
diff options
Diffstat (limited to 'greybus_power.patch')
-rw-r--r--greybus_power.patch1148
1 files changed, 1148 insertions, 0 deletions
diff --git a/greybus_power.patch b/greybus_power.patch
new file mode 100644
index 00000000000000..d1826b130a3e83
--- /dev/null
+++ b/greybus_power.patch
@@ -0,0 +1,1148 @@
+---
+ drivers/greybus/power_supply.c | 1141 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 1141 insertions(+)
+
+--- /dev/null
++++ b/drivers/greybus/power_supply.c
+@@ -0,0 +1,1141 @@
++/*
++ * Power Supply driver for a Greybus module.
++ *
++ * Copyright 2014-2015 Google Inc.
++ * Copyright 2014-2015 Linaro Ltd.
++ *
++ * Released under the GPLv2 only.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/power_supply.h>
++#include <linux/slab.h>
++
++#include "greybus.h"
++
++#define PROP_MAX 32
++
++struct gb_power_supply_prop {
++ enum power_supply_property prop;
++ u8 gb_prop;
++ int val;
++ int previous_val;
++ bool is_writeable;
++};
++
++struct gb_power_supply {
++ u8 id;
++ bool registered;
++ struct power_supply *psy;
++ struct power_supply_desc desc;
++ char name[64];
++ struct gb_power_supplies *supplies;
++ struct delayed_work work;
++ char *manufacturer;
++ char *model_name;
++ char *serial_number;
++ u8 type;
++ u8 properties_count;
++ u8 properties_count_str;
++ unsigned long last_update;
++ u8 cache_invalid;
++ unsigned int update_interval;
++ bool changed;
++ struct gb_power_supply_prop *props;
++ enum power_supply_property *props_raw;
++ bool pm_acquired;
++ struct mutex supply_lock;
++};
++
++struct gb_power_supplies {
++ struct gb_connection *connection;
++ u8 supplies_count;
++ struct gb_power_supply *supply;
++ struct mutex supplies_lock;
++};
++
++#define to_gb_power_supply(x) power_supply_get_drvdata(x)
++
++/*
++ * General power supply properties that could be absent from various reasons,
++ * like kernel versions or vendor specific versions
++ */
++#ifndef POWER_SUPPLY_PROP_VOLTAGE_BOOT
++ #define POWER_SUPPLY_PROP_VOLTAGE_BOOT -1
++#endif
++#ifndef POWER_SUPPLY_PROP_CURRENT_BOOT
++ #define POWER_SUPPLY_PROP_CURRENT_BOOT -1
++#endif
++#ifndef POWER_SUPPLY_PROP_CALIBRATE
++ #define POWER_SUPPLY_PROP_CALIBRATE -1
++#endif
++
++/* cache time in milliseconds, if cache_time is set to 0 cache is disable */
++static unsigned int cache_time = 1000;
++/*
++ * update interval initial and maximum value, between the two will
++ * back-off exponential
++ */
++static unsigned int update_interval_init = 1 * HZ;
++static unsigned int update_interval_max = 30 * HZ;
++
++struct gb_power_supply_changes {
++ enum power_supply_property prop;
++ u32 tolerance_change;
++ void (*prop_changed)(struct gb_power_supply *gbpsy,
++ struct gb_power_supply_prop *prop);
++};
++
++static void gb_power_supply_state_change(struct gb_power_supply *gbpsy,
++ struct gb_power_supply_prop *prop);
++
++static const struct gb_power_supply_changes psy_props_changes[] = {
++ { .prop = GB_POWER_SUPPLY_PROP_STATUS,
++ .tolerance_change = 0,
++ .prop_changed = gb_power_supply_state_change,
++ },
++ { .prop = GB_POWER_SUPPLY_PROP_TEMP,
++ .tolerance_change = 500,
++ .prop_changed = NULL,
++ },
++ { .prop = GB_POWER_SUPPLY_PROP_ONLINE,
++ .tolerance_change = 0,
++ .prop_changed = NULL,
++ },
++};
++
++static int get_psp_from_gb_prop(int gb_prop, enum power_supply_property *psp)
++{
++ int prop;
++
++ switch (gb_prop) {
++ case GB_POWER_SUPPLY_PROP_STATUS:
++ prop = POWER_SUPPLY_PROP_STATUS;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_TYPE:
++ prop = POWER_SUPPLY_PROP_CHARGE_TYPE;
++ break;
++ case GB_POWER_SUPPLY_PROP_HEALTH:
++ prop = POWER_SUPPLY_PROP_HEALTH;
++ break;
++ case GB_POWER_SUPPLY_PROP_PRESENT:
++ prop = POWER_SUPPLY_PROP_PRESENT;
++ break;
++ case GB_POWER_SUPPLY_PROP_ONLINE:
++ prop = POWER_SUPPLY_PROP_ONLINE;
++ break;
++ case GB_POWER_SUPPLY_PROP_AUTHENTIC:
++ prop = POWER_SUPPLY_PROP_AUTHENTIC;
++ break;
++ case GB_POWER_SUPPLY_PROP_TECHNOLOGY:
++ prop = POWER_SUPPLY_PROP_TECHNOLOGY;
++ break;
++ case GB_POWER_SUPPLY_PROP_CYCLE_COUNT:
++ prop = POWER_SUPPLY_PROP_CYCLE_COUNT;
++ break;
++ case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX:
++ prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
++ break;
++ case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN:
++ prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
++ break;
++ case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
++ prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
++ break;
++ case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
++ prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
++ break;
++ case GB_POWER_SUPPLY_PROP_VOLTAGE_NOW:
++ prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
++ break;
++ case GB_POWER_SUPPLY_PROP_VOLTAGE_AVG:
++ prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
++ break;
++ case GB_POWER_SUPPLY_PROP_VOLTAGE_OCV:
++ prop = POWER_SUPPLY_PROP_VOLTAGE_OCV;
++ break;
++ case GB_POWER_SUPPLY_PROP_VOLTAGE_BOOT:
++ prop = POWER_SUPPLY_PROP_VOLTAGE_BOOT;
++ break;
++ case GB_POWER_SUPPLY_PROP_CURRENT_MAX:
++ prop = POWER_SUPPLY_PROP_CURRENT_MAX;
++ break;
++ case GB_POWER_SUPPLY_PROP_CURRENT_NOW:
++ prop = POWER_SUPPLY_PROP_CURRENT_NOW;
++ break;
++ case GB_POWER_SUPPLY_PROP_CURRENT_AVG:
++ prop = POWER_SUPPLY_PROP_CURRENT_AVG;
++ break;
++ case GB_POWER_SUPPLY_PROP_CURRENT_BOOT:
++ prop = POWER_SUPPLY_PROP_CURRENT_BOOT;
++ break;
++ case GB_POWER_SUPPLY_PROP_POWER_NOW:
++ prop = POWER_SUPPLY_PROP_POWER_NOW;
++ break;
++ case GB_POWER_SUPPLY_PROP_POWER_AVG:
++ prop = POWER_SUPPLY_PROP_POWER_AVG;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
++ prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
++ prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_FULL:
++ prop = POWER_SUPPLY_PROP_CHARGE_FULL;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY:
++ prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_NOW:
++ prop = POWER_SUPPLY_PROP_CHARGE_NOW;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_AVG:
++ prop = POWER_SUPPLY_PROP_CHARGE_AVG;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_COUNTER:
++ prop = POWER_SUPPLY_PROP_CHARGE_COUNTER;
++ break;
++ case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
++ prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT;
++ break;
++ case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
++ prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
++ break;
++ case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
++ prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
++ break;
++ case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
++ prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
++ prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
++ prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX;
++ break;
++ case GB_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
++ prop = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
++ break;
++ case GB_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
++ prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
++ break;
++ case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN:
++ prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
++ break;
++ case GB_POWER_SUPPLY_PROP_ENERGY_FULL:
++ prop = POWER_SUPPLY_PROP_ENERGY_FULL;
++ break;
++ case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY:
++ prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
++ break;
++ case GB_POWER_SUPPLY_PROP_ENERGY_NOW:
++ prop = POWER_SUPPLY_PROP_ENERGY_NOW;
++ break;
++ case GB_POWER_SUPPLY_PROP_ENERGY_AVG:
++ prop = POWER_SUPPLY_PROP_ENERGY_AVG;
++ break;
++ case GB_POWER_SUPPLY_PROP_CAPACITY:
++ prop = POWER_SUPPLY_PROP_CAPACITY;
++ break;
++ case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
++ prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN;
++ break;
++ case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX:
++ prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX;
++ break;
++ case GB_POWER_SUPPLY_PROP_CAPACITY_LEVEL:
++ prop = POWER_SUPPLY_PROP_CAPACITY_LEVEL;
++ break;
++ case GB_POWER_SUPPLY_PROP_TEMP:
++ prop = POWER_SUPPLY_PROP_TEMP;
++ break;
++ case GB_POWER_SUPPLY_PROP_TEMP_MAX:
++ prop = POWER_SUPPLY_PROP_TEMP_MAX;
++ break;
++ case GB_POWER_SUPPLY_PROP_TEMP_MIN:
++ prop = POWER_SUPPLY_PROP_TEMP_MIN;
++ break;
++ case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
++ prop = POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
++ break;
++ case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
++ prop = POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
++ break;
++ case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT:
++ prop = POWER_SUPPLY_PROP_TEMP_AMBIENT;
++ break;
++ case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
++ prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
++ break;
++ case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
++ prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
++ break;
++ case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
++ prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW;
++ break;
++ case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
++ prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG;
++ break;
++ case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
++ prop = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW;
++ break;
++ case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
++ prop = POWER_SUPPLY_PROP_TIME_TO_FULL_AVG;
++ break;
++ case GB_POWER_SUPPLY_PROP_TYPE:
++ prop = POWER_SUPPLY_PROP_TYPE;
++ break;
++ case GB_POWER_SUPPLY_PROP_SCOPE:
++ prop = POWER_SUPPLY_PROP_SCOPE;
++ break;
++ case GB_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
++ prop = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT;
++ break;
++ case GB_POWER_SUPPLY_PROP_CALIBRATE:
++ prop = POWER_SUPPLY_PROP_CALIBRATE;
++ break;
++ default:
++ prop = -1;
++ break;
++ }
++
++ if (prop < 0)
++ return prop;
++
++ *psp = (enum power_supply_property)prop;
++
++ return 0;
++}
++
++static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy)
++{
++ return gbpsy->supplies->connection;
++}
++
++static struct gb_power_supply_prop *get_psy_prop(struct gb_power_supply *gbpsy,
++ enum power_supply_property psp)
++{
++ int i;
++
++ for (i = 0; i < gbpsy->properties_count; i++)
++ if (gbpsy->props[i].prop == psp)
++ return &gbpsy->props[i];
++ return NULL;
++}
++
++static int is_psy_prop_writeable(struct gb_power_supply *gbpsy,
++ enum power_supply_property psp)
++{
++ struct gb_power_supply_prop *prop;
++
++ prop = get_psy_prop(gbpsy, psp);
++ if (!prop)
++ return -ENOENT;
++ return prop->is_writeable ? 1 : 0;
++}
++
++static int is_prop_valint(enum power_supply_property psp)
++{
++ return ((psp < POWER_SUPPLY_PROP_MODEL_NAME) ? 1 : 0);
++}
++
++static void next_interval(struct gb_power_supply *gbpsy)
++{
++ if (gbpsy->update_interval == update_interval_max)
++ return;
++
++ /* do some exponential back-off in the update interval */
++ gbpsy->update_interval *= 2;
++ if (gbpsy->update_interval > update_interval_max)
++ gbpsy->update_interval = update_interval_max;
++}
++
++static void __gb_power_supply_changed(struct gb_power_supply *gbpsy)
++{
++ power_supply_changed(gbpsy->psy);
++}
++
++static void gb_power_supply_state_change(struct gb_power_supply *gbpsy,
++ struct gb_power_supply_prop *prop)
++{
++ struct gb_connection *connection = get_conn_from_psy(gbpsy);
++ int ret;
++
++ /*
++ * Check gbpsy->pm_acquired to make sure only one pair of 'get_sync'
++ * and 'put_autosuspend' runtime pm call for state property change.
++ */
++ mutex_lock(&gbpsy->supply_lock);
++
++ if ((prop->val == GB_POWER_SUPPLY_STATUS_CHARGING) &&
++ !gbpsy->pm_acquired) {
++ ret = gb_pm_runtime_get_sync(connection->bundle);
++ if (ret)
++ dev_err(&connection->bundle->dev,
++ "Fail to set wake lock for charging state\n");
++ else
++ gbpsy->pm_acquired = true;
++ } else {
++ if (gbpsy->pm_acquired) {
++ ret = gb_pm_runtime_put_autosuspend(connection->bundle);
++ if (ret)
++ dev_err(&connection->bundle->dev,
++ "Fail to set wake unlock for none charging\n");
++ else
++ gbpsy->pm_acquired = false;
++ }
++ }
++
++ mutex_unlock(&gbpsy->supply_lock);
++}
++
++static void check_changed(struct gb_power_supply *gbpsy,
++ struct gb_power_supply_prop *prop)
++{
++ const struct gb_power_supply_changes *psyc;
++ int val = prop->val;
++ int prev_val = prop->previous_val;
++ bool changed = false;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) {
++ psyc = &psy_props_changes[i];
++ if (prop->prop == psyc->prop) {
++ if (!psyc->tolerance_change)
++ changed = true;
++ else if (val < prev_val &&
++ prev_val - val > psyc->tolerance_change)
++ changed = true;
++ else if (val > prev_val &&
++ val - prev_val > psyc->tolerance_change)
++ changed = true;
++
++ if (changed && psyc->prop_changed)
++ psyc->prop_changed(gbpsy, prop);
++
++ if (changed)
++ gbpsy->changed = true;
++ break;
++ }
++ }
++}
++
++static int total_props(struct gb_power_supply *gbpsy)
++{
++ /* this return the intval plus the strval properties */
++ return (gbpsy->properties_count + gbpsy->properties_count_str);
++}
++
++static void prop_append(struct gb_power_supply *gbpsy,
++ enum power_supply_property prop)
++{
++ enum power_supply_property *new_props_raw;
++
++ gbpsy->properties_count_str++;
++ new_props_raw = krealloc(gbpsy->props_raw, total_props(gbpsy) *
++ sizeof(enum power_supply_property),
++ GFP_KERNEL);
++ if (!new_props_raw)
++ return;
++ gbpsy->props_raw = new_props_raw;
++ gbpsy->props_raw[total_props(gbpsy) - 1] = prop;
++}
++
++static int __gb_power_supply_set_name(char *init_name, char *name, size_t len)
++{
++ unsigned int i = 0;
++ int ret = 0;
++ struct power_supply *psy;
++
++ if (!strlen(init_name))
++ init_name = "gb_power_supply";
++ strlcpy(name, init_name, len);
++
++ while ((ret < len) && (psy = power_supply_get_by_name(name))) {
++ power_supply_put(psy);
++
++ ret = snprintf(name, len, "%s_%u", init_name, ++i);
++ }
++ if (ret >= len)
++ return -ENOMEM;
++ return i;
++}
++
++static void _gb_power_supply_append_props(struct gb_power_supply *gbpsy)
++{
++ if (strlen(gbpsy->manufacturer))
++ prop_append(gbpsy, POWER_SUPPLY_PROP_MANUFACTURER);
++ if (strlen(gbpsy->model_name))
++ prop_append(gbpsy, POWER_SUPPLY_PROP_MODEL_NAME);
++ if (strlen(gbpsy->serial_number))
++ prop_append(gbpsy, POWER_SUPPLY_PROP_SERIAL_NUMBER);
++}
++
++static int gb_power_supply_description_get(struct gb_power_supply *gbpsy)
++{
++ struct gb_connection *connection = get_conn_from_psy(gbpsy);
++ struct gb_power_supply_get_description_request req;
++ struct gb_power_supply_get_description_response resp;
++ int ret;
++
++ req.psy_id = gbpsy->id;
++
++ ret = gb_operation_sync(connection,
++ GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION,
++ &req, sizeof(req), &resp, sizeof(resp));
++ if (ret < 0)
++ return ret;
++
++ gbpsy->manufacturer = kstrndup(resp.manufacturer, PROP_MAX, GFP_KERNEL);
++ if (!gbpsy->manufacturer)
++ return -ENOMEM;
++ gbpsy->model_name = kstrndup(resp.model, PROP_MAX, GFP_KERNEL);
++ if (!gbpsy->model_name)
++ return -ENOMEM;
++ gbpsy->serial_number = kstrndup(resp.serial_number, PROP_MAX,
++ GFP_KERNEL);
++ if (!gbpsy->serial_number)
++ return -ENOMEM;
++
++ gbpsy->type = le16_to_cpu(resp.type);
++ gbpsy->properties_count = resp.properties_count;
++
++ return 0;
++}
++
++static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy)
++{
++ struct gb_connection *connection = get_conn_from_psy(gbpsy);
++ struct gb_power_supply_get_property_descriptors_request *req;
++ struct gb_power_supply_get_property_descriptors_response *resp;
++ struct gb_operation *op;
++ u8 props_count = gbpsy->properties_count;
++ enum power_supply_property psp;
++ int ret;
++ int i, r = 0;
++
++ if (props_count == 0)
++ return 0;
++
++ op = gb_operation_create(connection,
++ GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS,
++ sizeof(req), sizeof(*resp) + props_count *
++ sizeof(struct gb_power_supply_props_desc),
++ GFP_KERNEL);
++ if (!op)
++ return -ENOMEM;
++
++ req = op->request->payload;
++ req->psy_id = gbpsy->id;
++
++ ret = gb_operation_request_send_sync(op);
++ if (ret < 0)
++ goto out_put_operation;
++
++ resp = op->response->payload;
++
++ /* validate received properties */
++ for (i = 0; i < props_count; i++) {
++ ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
++ if (ret < 0) {
++ dev_warn(&connection->bundle->dev,
++ "greybus property %u it is not supported by this kernel, dropped\n",
++ resp->props[i].property);
++ gbpsy->properties_count--;
++ }
++ }
++
++ gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props),
++ GFP_KERNEL);
++ if (!gbpsy->props) {
++ ret = -ENOMEM;
++ goto out_put_operation;
++ }
++
++ gbpsy->props_raw = kcalloc(gbpsy->properties_count,
++ sizeof(*gbpsy->props_raw), GFP_KERNEL);
++ if (!gbpsy->props_raw) {
++ ret = -ENOMEM;
++ goto out_put_operation;
++ }
++
++ /* Store available properties, skip the ones we do not support */
++ for (i = 0; i < props_count; i++) {
++ ret = get_psp_from_gb_prop(resp->props[i].property, &psp);
++ if (ret < 0) {
++ r++;
++ continue;
++ }
++ gbpsy->props[i - r].prop = psp;
++ gbpsy->props[i - r].gb_prop = resp->props[i].property;
++ gbpsy->props_raw[i - r] = psp;
++ if (resp->props[i].is_writeable)
++ gbpsy->props[i - r].is_writeable = true;
++ }
++
++ /*
++ * now append the properties that we already got information in the
++ * get_description operation. (char * ones)
++ */
++ _gb_power_supply_append_props(gbpsy);
++
++ ret = 0;
++out_put_operation:
++ gb_operation_put(op);
++
++ return ret;
++}
++
++static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy,
++ enum power_supply_property psp)
++{
++ struct gb_connection *connection = get_conn_from_psy(gbpsy);
++ struct gb_power_supply_prop *prop;
++ struct gb_power_supply_get_property_request req;
++ struct gb_power_supply_get_property_response resp;
++ int val;
++ int ret;
++
++ prop = get_psy_prop(gbpsy, psp);
++ if (!prop)
++ return -EINVAL;
++ req.psy_id = gbpsy->id;
++ req.property = prop->gb_prop;
++
++ ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY,
++ &req, sizeof(req), &resp, sizeof(resp));
++ if (ret < 0)
++ return ret;
++
++ val = le32_to_cpu(resp.prop_val);
++ if (val == prop->val)
++ return 0;
++
++ prop->previous_val = prop->val;
++ prop->val = val;
++
++ check_changed(gbpsy, prop);
++
++ return 0;
++}
++
++static int __gb_power_supply_property_get(struct gb_power_supply *gbpsy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct gb_power_supply_prop *prop;
++
++ prop = get_psy_prop(gbpsy, psp);
++ if (!prop)
++ return -EINVAL;
++
++ val->intval = prop->val;
++ return 0;
++}
++
++static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ switch (psp) {
++ case POWER_SUPPLY_PROP_MODEL_NAME:
++ val->strval = gbpsy->model_name;
++ break;
++ case POWER_SUPPLY_PROP_MANUFACTURER:
++ val->strval = gbpsy->manufacturer;
++ break;
++ case POWER_SUPPLY_PROP_SERIAL_NUMBER:
++ val->strval = gbpsy->serial_number;
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct gb_connection *connection = get_conn_from_psy(gbpsy);
++ int ret;
++
++ /*
++ * Properties of type const char *, were already fetched on
++ * get_description operation and should be cached in gb
++ */
++ if (is_prop_valint(psp))
++ ret = __gb_power_supply_property_get(gbpsy, psp, val);
++ else
++ ret = __gb_power_supply_property_strval_get(gbpsy, psp, val);
++
++ if (ret < 0)
++ dev_err(&connection->bundle->dev, "get property %u\n", psp);
++
++ return 0;
++}
++
++static int is_cache_valid(struct gb_power_supply *gbpsy)
++{
++ /* check if cache is good enough or it has expired */
++ if (gbpsy->cache_invalid) {
++ gbpsy->cache_invalid = 0;
++ return 0;
++ }
++
++ if (gbpsy->last_update &&
++ time_is_after_jiffies(gbpsy->last_update +
++ msecs_to_jiffies(cache_time)))
++ return 1;
++
++ return 0;
++}
++
++static int gb_power_supply_status_get(struct gb_power_supply *gbpsy)
++{
++ struct gb_connection *connection = get_conn_from_psy(gbpsy);
++ int ret = 0;
++ int i;
++
++ if (is_cache_valid(gbpsy))
++ return 0;
++
++ ret = gb_pm_runtime_get_sync(connection->bundle);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < gbpsy->properties_count; i++) {
++ ret = __gb_power_supply_property_update(gbpsy,
++ gbpsy->props[i].prop);
++ if (ret < 0)
++ break;
++ }
++
++ if (ret == 0)
++ gbpsy->last_update = jiffies;
++
++ gb_pm_runtime_put_autosuspend(connection->bundle);
++ return ret;
++}
++
++static void gb_power_supply_status_update(struct gb_power_supply *gbpsy)
++{
++ /* check if there a change that need to be reported */
++ gb_power_supply_status_get(gbpsy);
++
++ if (!gbpsy->changed)
++ return;
++
++ gbpsy->update_interval = update_interval_init;
++ __gb_power_supply_changed(gbpsy);
++ gbpsy->changed = false;
++}
++
++static void gb_power_supply_work(struct work_struct *work)
++{
++ struct gb_power_supply *gbpsy = container_of(work,
++ struct gb_power_supply,
++ work.work);
++
++ /*
++ * if the poll interval is not set, disable polling, this is helpful
++ * specially at unregister time.
++ */
++ if (!gbpsy->update_interval)
++ return;
++
++ gb_power_supply_status_update(gbpsy);
++ next_interval(gbpsy);
++ schedule_delayed_work(&gbpsy->work, gbpsy->update_interval);
++}
++
++static int get_property(struct power_supply *b,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct gb_power_supply *gbpsy = to_gb_power_supply(b);
++
++ gb_power_supply_status_get(gbpsy);
++
++ return _gb_power_supply_property_get(gbpsy, psp, val);
++}
++
++static int gb_power_supply_property_set(struct gb_power_supply *gbpsy,
++ enum power_supply_property psp,
++ int val)
++{
++ struct gb_connection *connection = get_conn_from_psy(gbpsy);
++ struct gb_power_supply_prop *prop;
++ struct gb_power_supply_set_property_request req;
++ int ret;
++
++ ret = gb_pm_runtime_get_sync(connection->bundle);
++ if (ret)
++ return ret;
++
++ prop = get_psy_prop(gbpsy, psp);
++ if (!prop) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ req.psy_id = gbpsy->id;
++ req.property = prop->gb_prop;
++ req.prop_val = cpu_to_le32((s32)val);
++
++ ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY,
++ &req, sizeof(req), NULL, 0);
++ if (ret < 0)
++ goto out;
++
++ /* cache immediately the new value */
++ prop->val = val;
++
++out:
++ gb_pm_runtime_put_autosuspend(connection->bundle);
++ return ret;
++}
++
++static int set_property(struct power_supply *b,
++ enum power_supply_property psp,
++ const union power_supply_propval *val)
++{
++ struct gb_power_supply *gbpsy = to_gb_power_supply(b);
++
++ return gb_power_supply_property_set(gbpsy, psp, val->intval);
++}
++
++static int property_is_writeable(struct power_supply *b,
++ enum power_supply_property psp)
++{
++ struct gb_power_supply *gbpsy = to_gb_power_supply(b);
++
++ return is_psy_prop_writeable(gbpsy, psp);
++}
++
++static int gb_power_supply_register(struct gb_power_supply *gbpsy)
++{
++ struct gb_connection *connection = get_conn_from_psy(gbpsy);
++ struct power_supply_config cfg = {};
++
++ cfg.drv_data = gbpsy;
++
++ gbpsy->desc.name = gbpsy->name;
++ gbpsy->desc.type = gbpsy->type;
++ gbpsy->desc.properties = gbpsy->props_raw;
++ gbpsy->desc.num_properties = total_props(gbpsy);
++ gbpsy->desc.get_property = get_property;
++ gbpsy->desc.set_property = set_property;
++ gbpsy->desc.property_is_writeable = property_is_writeable;
++
++ gbpsy->psy = power_supply_register(&connection->bundle->dev,
++ &gbpsy->desc, &cfg);
++ return PTR_ERR_OR_ZERO(gbpsy->psy);
++}
++
++static void _gb_power_supply_free(struct gb_power_supply *gbpsy)
++{
++ kfree(gbpsy->serial_number);
++ kfree(gbpsy->model_name);
++ kfree(gbpsy->manufacturer);
++ kfree(gbpsy->props_raw);
++ kfree(gbpsy->props);
++}
++
++static void _gb_power_supply_release(struct gb_power_supply *gbpsy)
++{
++ gbpsy->update_interval = 0;
++
++ cancel_delayed_work_sync(&gbpsy->work);
++
++ if (gbpsy->registered)
++ power_supply_unregister(gbpsy->psy);
++
++ _gb_power_supply_free(gbpsy);
++}
++
++static void _gb_power_supplies_release(struct gb_power_supplies *supplies)
++{
++ int i;
++
++ if (!supplies->supply)
++ return;
++
++ mutex_lock(&supplies->supplies_lock);
++ for (i = 0; i < supplies->supplies_count; i++)
++ _gb_power_supply_release(&supplies->supply[i]);
++ kfree(supplies->supply);
++ mutex_unlock(&supplies->supplies_lock);
++ kfree(supplies);
++}
++
++static int gb_power_supplies_get_count(struct gb_power_supplies *supplies)
++{
++ struct gb_power_supply_get_supplies_response resp;
++ int ret;
++
++ ret = gb_operation_sync(supplies->connection,
++ GB_POWER_SUPPLY_TYPE_GET_SUPPLIES,
++ NULL, 0, &resp, sizeof(resp));
++ if (ret < 0)
++ return ret;
++
++ if (!resp.supplies_count)
++ return -EINVAL;
++
++ supplies->supplies_count = resp.supplies_count;
++
++ return ret;
++}
++
++static int gb_power_supply_config(struct gb_power_supplies *supplies, int id)
++{
++ struct gb_power_supply *gbpsy = &supplies->supply[id];
++ int ret;
++
++ gbpsy->supplies = supplies;
++ gbpsy->id = id;
++
++ ret = gb_power_supply_description_get(gbpsy);
++ if (ret < 0)
++ return ret;
++
++ return gb_power_supply_prop_descriptors_get(gbpsy);
++}
++
++static int gb_power_supply_enable(struct gb_power_supply *gbpsy)
++{
++ int ret;
++
++ /* guarantee that we have an unique name, before register */
++ ret = __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name,
++ sizeof(gbpsy->name));
++ if (ret < 0)
++ return ret;
++
++ mutex_init(&gbpsy->supply_lock);
++
++ ret = gb_power_supply_register(gbpsy);
++ if (ret < 0)
++ return ret;
++
++ gbpsy->update_interval = update_interval_init;
++ INIT_DELAYED_WORK(&gbpsy->work, gb_power_supply_work);
++ schedule_delayed_work(&gbpsy->work, 0);
++
++ /* everything went fine, mark it for release code to know */
++ gbpsy->registered = true;
++
++ return 0;
++}
++
++static int gb_power_supplies_setup(struct gb_power_supplies *supplies)
++{
++ struct gb_connection *connection = supplies->connection;
++ int ret;
++ int i;
++
++ mutex_lock(&supplies->supplies_lock);
++
++ ret = gb_power_supplies_get_count(supplies);
++ if (ret < 0)
++ goto out;
++
++ supplies->supply = kzalloc(supplies->supplies_count *
++ sizeof(struct gb_power_supply),
++ GFP_KERNEL);
++
++ if (!supplies->supply) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ for (i = 0; i < supplies->supplies_count; i++) {
++ ret = gb_power_supply_config(supplies, i);
++ if (ret < 0) {
++ dev_err(&connection->bundle->dev,
++ "Fail to configure supplies devices\n");
++ goto out;
++ }
++ }
++out:
++ mutex_unlock(&supplies->supplies_lock);
++ return ret;
++}
++
++static int gb_power_supplies_register(struct gb_power_supplies *supplies)
++{
++ struct gb_connection *connection = supplies->connection;
++ int ret = 0;
++ int i;
++
++ mutex_lock(&supplies->supplies_lock);
++
++ for (i = 0; i < supplies->supplies_count; i++) {
++ ret = gb_power_supply_enable(&supplies->supply[i]);
++ if (ret < 0) {
++ dev_err(&connection->bundle->dev,
++ "Fail to enable supplies devices\n");
++ break;
++ }
++ }
++
++ mutex_unlock(&supplies->supplies_lock);
++ return ret;
++}
++
++static int gb_supplies_request_handler(struct gb_operation *op)
++{
++ struct gb_connection *connection = op->connection;
++ struct gb_power_supplies *supplies = gb_connection_get_data(connection);
++ struct gb_power_supply *gbpsy;
++ struct gb_message *request;
++ struct gb_power_supply_event_request *payload;
++ u8 psy_id;
++ u8 event;
++ int ret = 0;
++
++ if (op->type != GB_POWER_SUPPLY_TYPE_EVENT) {
++ dev_err(&connection->bundle->dev,
++ "Unsupported unsolicited event: %u\n", op->type);
++ return -EINVAL;
++ }
++
++ request = op->request;
++
++ if (request->payload_size < sizeof(*payload)) {
++ dev_err(&connection->bundle->dev,
++ "Wrong event size received (%zu < %zu)\n",
++ request->payload_size, sizeof(*payload));
++ return -EINVAL;
++ }
++
++ payload = request->payload;
++ psy_id = payload->psy_id;
++ mutex_lock(&supplies->supplies_lock);
++ if (psy_id >= supplies->supplies_count ||
++ !supplies->supply[psy_id].registered) {
++ dev_err(&connection->bundle->dev,
++ "Event received for unconfigured power_supply id: %d\n",
++ psy_id);
++ ret = -EINVAL;
++ goto out_unlock;
++ }
++
++ event = payload->event;
++ /*
++ * we will only handle events after setup is done and before release is
++ * running. For that just check update_interval.
++ */
++ gbpsy = &supplies->supply[psy_id];
++ if (!gbpsy->update_interval) {
++ ret = -ESHUTDOWN;
++ goto out_unlock;
++ }
++
++ if (event & GB_POWER_SUPPLY_UPDATE) {
++ /*
++ * we need to make sure we invalidate cache, if not no new
++ * values for the properties will be fetch and the all propose
++ * of this event is missed
++ */
++ gbpsy->cache_invalid = 1;
++ gb_power_supply_status_update(gbpsy);
++ }
++
++out_unlock:
++ mutex_unlock(&supplies->supplies_lock);
++ return ret;
++}
++
++static int gb_power_supply_probe(struct gb_bundle *bundle,
++ const struct greybus_bundle_id *id)
++{
++ struct greybus_descriptor_cport *cport_desc;
++ struct gb_connection *connection;
++ struct gb_power_supplies *supplies;
++ int ret;
++
++ if (bundle->num_cports != 1)
++ return -ENODEV;
++
++ cport_desc = &bundle->cport_desc[0];
++ if (cport_desc->protocol_id != GREYBUS_PROTOCOL_POWER_SUPPLY)
++ return -ENODEV;
++
++ supplies = kzalloc(sizeof(*supplies), GFP_KERNEL);
++ if (!supplies)
++ return -ENOMEM;
++
++ connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
++ gb_supplies_request_handler);
++ if (IS_ERR(connection)) {
++ ret = PTR_ERR(connection);
++ goto out;
++ }
++
++ supplies->connection = connection;
++ gb_connection_set_data(connection, supplies);
++
++ mutex_init(&supplies->supplies_lock);
++
++ greybus_set_drvdata(bundle, supplies);
++
++ /* We aren't ready to receive an incoming request yet */
++ ret = gb_connection_enable_tx(connection);
++ if (ret)
++ goto error_connection_destroy;
++
++ ret = gb_power_supplies_setup(supplies);
++ if (ret < 0)
++ goto error_connection_disable;
++
++ /* We are ready to receive an incoming request now, enable RX as well */
++ ret = gb_connection_enable(connection);
++ if (ret)
++ goto error_connection_disable;
++
++ ret = gb_power_supplies_register(supplies);
++ if (ret < 0)
++ goto error_connection_disable;
++
++ gb_pm_runtime_put_autosuspend(bundle);
++ return 0;
++
++error_connection_disable:
++ gb_connection_disable(connection);
++error_connection_destroy:
++ gb_connection_destroy(connection);
++out:
++ _gb_power_supplies_release(supplies);
++ return ret;
++}
++
++static void gb_power_supply_disconnect(struct gb_bundle *bundle)
++{
++ struct gb_power_supplies *supplies = greybus_get_drvdata(bundle);
++
++ gb_connection_disable(supplies->connection);
++ gb_connection_destroy(supplies->connection);
++
++ _gb_power_supplies_release(supplies);
++}
++
++static const struct greybus_bundle_id gb_power_supply_id_table[] = {
++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) },
++ { }
++};
++MODULE_DEVICE_TABLE(greybus, gb_power_supply_id_table);
++
++static struct greybus_driver gb_power_supply_driver = {
++ .name = "power_supply",
++ .probe = gb_power_supply_probe,
++ .disconnect = gb_power_supply_disconnect,
++ .id_table = gb_power_supply_id_table,
++};
++module_greybus_driver(gb_power_supply_driver);
++
++MODULE_LICENSE("GPL v2");