summaryrefslogtreecommitdiff
path: root/drivers/staging/greybus/power_supply.c
diff options
context:
space:
mode:
authorRui Miguel Silva <rui.silva@linaro.org>2015-11-12 15:35:59 +0000
committerGreg Kroah-Hartman <gregkh@google.com>2015-11-12 16:06:49 -0800
commit464888410d3499708baf168bc52e13f18d0cf0ae (patch)
tree7d2d0abdc487f046f788e2f62033a62217d91d20 /drivers/staging/greybus/power_supply.c
parent69166d252536b644f82782840cf31e6d685616dc (diff)
greybus: battery: move battery file to power_supply
Rename the battery.c to power_supply.c and update the makefile as the Greybus protocol is being renamed from battery to power_supply. Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/power_supply.c')
-rw-r--r--drivers/staging/greybus/power_supply.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/drivers/staging/greybus/power_supply.c b/drivers/staging/greybus/power_supply.c
new file mode 100644
index 000000000000..e119775fb19b
--- /dev/null
+++ b/drivers/staging/greybus/power_supply.c
@@ -0,0 +1,317 @@
+/*
+ * Battery driver for a Greybus module.
+ *
+ * Copyright 2014 Google Inc.
+ * Copyright 2014 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include "greybus.h"
+
+struct gb_battery {
+ /*
+ * The power supply api changed in 4.1, so handle both the old
+ * and new apis in the same driver for now, until this is merged
+ * upstream, when all of these version checks can be removed.
+ */
+#ifdef DRIVER_OWNS_PSY_STRUCT
+ struct power_supply bat;
+#define to_gb_battery(x) container_of(x, struct gb_battery, bat)
+#else
+ struct power_supply *bat;
+ struct power_supply_desc desc;
+#define to_gb_battery(x) power_supply_get_drvdata(x)
+#endif
+ // FIXME
+ // we will want to keep the battery stats in here as we will be getting
+ // updates from the SVC "on the fly" so we don't have to always go ask
+ // the battery for some information. Hopefully...
+ struct gb_connection *connection;
+
+};
+
+static int get_tech(struct gb_battery *gb)
+{
+ struct gb_battery_technology_response tech_response;
+ u32 technology;
+ int retval;
+
+ retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY,
+ NULL, 0,
+ &tech_response, sizeof(tech_response));
+ if (retval)
+ return retval;
+
+ /*
+ * Map greybus values to power_supply values. Hopefully these are
+ * "identical" which should allow gcc to optimize the code away to
+ * nothing.
+ */
+ technology = le32_to_cpu(tech_response.technology);
+ switch (technology) {
+ case GB_BATTERY_TECH_NiMH:
+ technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
+ break;
+ case GB_BATTERY_TECH_LION:
+ technology = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case GB_BATTERY_TECH_LIPO:
+ technology = POWER_SUPPLY_TECHNOLOGY_LIPO;
+ break;
+ case GB_BATTERY_TECH_LiFe:
+ technology = POWER_SUPPLY_TECHNOLOGY_LiFe;
+ break;
+ case GB_BATTERY_TECH_NiCd:
+ technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
+ break;
+ case GB_BATTERY_TECH_LiMn:
+ technology = POWER_SUPPLY_TECHNOLOGY_LiMn;
+ break;
+ case GB_BATTERY_TECH_UNKNOWN:
+ default:
+ technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+ break;
+ }
+ return technology;
+}
+
+static int get_status(struct gb_battery *gb)
+{
+ struct gb_battery_status_response status_response;
+ u16 battery_status;
+ int retval;
+
+ retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS,
+ NULL, 0,
+ &status_response, sizeof(status_response));
+ if (retval)
+ return retval;
+
+ /*
+ * Map greybus values to power_supply values. Hopefully these are
+ * "identical" which should allow gcc to optimize the code away to
+ * nothing.
+ */
+ battery_status = le16_to_cpu(status_response.battery_status);
+ switch (battery_status) {
+ case GB_BATTERY_STATUS_CHARGING:
+ battery_status = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case GB_BATTERY_STATUS_DISCHARGING:
+ battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ case GB_BATTERY_STATUS_NOT_CHARGING:
+ battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case GB_BATTERY_STATUS_FULL:
+ battery_status = POWER_SUPPLY_STATUS_FULL;
+ break;
+ case GB_BATTERY_STATUS_UNKNOWN:
+ default:
+ battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ }
+ return battery_status;
+}
+
+static int get_max_voltage(struct gb_battery *gb)
+{
+ struct gb_battery_max_voltage_response volt_response;
+ u32 max_voltage;
+ int retval;
+
+ retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE,
+ NULL, 0,
+ &volt_response, sizeof(volt_response));
+ if (retval)
+ return retval;
+
+ max_voltage = le32_to_cpu(volt_response.max_voltage);
+ return max_voltage;
+}
+
+static int get_percent_capacity(struct gb_battery *gb)
+{
+ struct gb_battery_capacity_response capacity_response;
+ u32 capacity;
+ int retval;
+
+ retval = gb_operation_sync(gb->connection,
+ GB_BATTERY_TYPE_PERCENT_CAPACITY,
+ NULL, 0, &capacity_response,
+ sizeof(capacity_response));
+ if (retval)
+ return retval;
+
+ capacity = le32_to_cpu(capacity_response.capacity);
+ return capacity;
+}
+
+static int get_temp(struct gb_battery *gb)
+{
+ struct gb_battery_temperature_response temp_response;
+ u32 temperature;
+ int retval;
+
+ retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE,
+ NULL, 0,
+ &temp_response, sizeof(temp_response));
+ if (retval)
+ return retval;
+
+ temperature = le32_to_cpu(temp_response.temperature);
+ return temperature;
+}
+
+static int get_voltage(struct gb_battery *gb)
+{
+ struct gb_battery_voltage_response voltage_response;
+ u32 voltage;
+ int retval;
+
+ retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE,
+ NULL, 0,
+ &voltage_response, sizeof(voltage_response));
+ if (retval)
+ return retval;
+
+ voltage = le32_to_cpu(voltage_response.voltage);
+ return voltage;
+}
+
+static int get_property(struct power_supply *b,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct gb_battery *gb = to_gb_battery(b);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = get_tech(gb);
+ break;
+
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = get_status(gb);
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = get_max_voltage(gb);
+ break;
+
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = get_percent_capacity(gb);
+ break;
+
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = get_temp(gb);
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = get_voltage(gb);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return (val->intval < 0) ? val->intval : 0;
+}
+
+// FIXME - verify this list, odds are some can be removed and others added.
+static enum power_supply_property battery_props[] = {
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+#ifdef DRIVER_OWNS_PSY_STRUCT
+static int init_and_register(struct gb_connection *connection,
+ struct gb_battery *gb)
+{
+ // FIXME - get a better (i.e. unique) name
+ // FIXME - anything else needs to be set?
+ gb->bat.name = "gb_battery";
+ gb->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ gb->bat.properties = battery_props;
+ gb->bat.num_properties = ARRAY_SIZE(battery_props);
+ gb->bat.get_property = get_property;
+
+ return power_supply_register(&connection->bundle->dev, &gb->bat);
+}
+#else
+static int init_and_register(struct gb_connection *connection,
+ struct gb_battery *gb)
+{
+ struct power_supply_config cfg = {};
+
+ cfg.drv_data = gb;
+
+ // FIXME - get a better (i.e. unique) name
+ // FIXME - anything else needs to be set?
+ gb->desc.name = "gb_battery";
+ gb->desc.type = POWER_SUPPLY_TYPE_BATTERY;
+ gb->desc.properties = battery_props;
+ gb->desc.num_properties = ARRAY_SIZE(battery_props);
+ gb->desc.get_property = get_property;
+
+ gb->bat = power_supply_register(&connection->bundle->dev,
+ &gb->desc, &cfg);
+ if (IS_ERR(gb->bat))
+ return PTR_ERR(gb->bat);
+
+ return 0;
+}
+#endif
+
+static int gb_battery_connection_init(struct gb_connection *connection)
+{
+ struct gb_battery *gb;
+ int retval;
+
+ gb = kzalloc(sizeof(*gb), GFP_KERNEL);
+ if (!gb)
+ return -ENOMEM;
+
+ gb->connection = connection;
+ connection->private = gb;
+
+ retval = init_and_register(connection, gb);
+ if (retval)
+ kfree(gb);
+
+ return retval;
+}
+
+static void gb_battery_connection_exit(struct gb_connection *connection)
+{
+ struct gb_battery *gb = connection->private;
+
+#ifdef DRIVER_OWNS_PSY_STRUCT
+ power_supply_unregister(&gb->bat);
+#else
+ power_supply_unregister(gb->bat);
+#endif
+ kfree(gb);
+}
+
+static struct gb_protocol battery_protocol = {
+ .name = "battery",
+ .id = GREYBUS_PROTOCOL_BATTERY,
+ .major = GB_BATTERY_VERSION_MAJOR,
+ .minor = GB_BATTERY_VERSION_MINOR,
+ .connection_init = gb_battery_connection_init,
+ .connection_exit = gb_battery_connection_exit,
+ .request_recv = NULL, /* no incoming requests */
+};
+
+gb_protocol_driver(&battery_protocol);
+
+MODULE_LICENSE("GPL v2");