summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/battery-gb.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman2014-10-21 08:31:24 +0200
committerGreg Kroah-Hartman2014-10-21 08:31:24 +0200
commitc0855bfdd65dd5c100d5c63daab5897a117d8b49 (patch)
treee2f6d8ce6757e70d66c3738024a3dc43b3c1d59f /drivers/staging/greybus/battery-gb.c
parentgreybus: leverage ap_disconnect() in ap_probe() (diff)
downloadkernel-qcow2-linux-c0855bfdd65dd5c100d5c63daab5897a117d8b49.tar.gz
kernel-qcow2-linux-c0855bfdd65dd5c100d5c63daab5897a117d8b49.tar.xz
kernel-qcow2-linux-c0855bfdd65dd5c100d5c63daab5897a117d8b49.zip
greybus: battery-gb: Add battery communication with the module
This adds support to talk to the battery to get the various requests made to it, based on the battery protocol defined in the Greybus Specification. Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
Diffstat (limited to 'drivers/staging/greybus/battery-gb.c')
-rw-r--r--drivers/staging/greybus/battery-gb.c231
1 files changed, 219 insertions, 12 deletions
diff --git a/drivers/staging/greybus/battery-gb.c b/drivers/staging/greybus/battery-gb.c
index 09c3290e1eaf..eca1d0948705 100644
--- a/drivers/staging/greybus/battery-gb.c
+++ b/drivers/staging/greybus/battery-gb.c
@@ -25,45 +25,245 @@ struct gb_battery {
};
#define to_gb_battery(x) container_of(x, struct gb_battery, bat)
+/* Version of the Greybus battery protocol we support */
+#define GB_BATTERY_VERSION_MAJOR 0x00
+#define GB_BATTERY_VERSION_MINOR 0x01
+
+/* Greybus battery request types */
+#define GB_BATTERY_TYPE_INVALID 0x00
+#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01
+#define GB_BATTERY_TYPE_TECHNOLOGY 0x02
+#define GB_BATTERY_TYPE_STATUS 0x03
+#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04
+#define GB_BATTERY_TYPE_CAPACITY 0x05
+#define GB_BATTERY_TYPE_TEMPERATURE 0x06
+#define GB_BATTERY_TYPE_VOLTAGE 0x07
+
+struct gb_battery_proto_version_response {
+ __u8 status;
+ __u8 major;
+ __u8 minor;
+};
+
+/* Should match up with battery types in linux/power_supply.h */
+#define GB_BATTERY_TECH_UNKNOWN 0x0000
+#define GB_BATTERY_TECH_NiMH 0x0001
+#define GB_BATTERY_TECH_LION 0x0002
+#define GB_BATTERY_TECH_LIPO 0x0003
+#define GB_BATTERY_TECH_LiFe 0x0004
+#define GB_BATTERY_TECH_NiCd 0x0005
+#define GB_BATTERY_TECH_LiMn 0x0006
+
+struct gb_battery_technology_request {
+ __u8 status;
+ __le32 technology;
+};
+
+/* Should match up with battery status in linux/power_supply.h */
+#define GB_BATTERY_STATUS_UNKNOWN 0x0000
+#define GB_BATTERY_STATUS_CHARGING 0x0001
+#define GB_BATTERY_STATUS_DISCHARGING 0x0002
+#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003
+#define GB_BATTERY_STATUS_FULL 0x0004
+
+struct gb_battery_status_request {
+ __u8 status;
+ __le16 battery_status;
+};
+
+struct gb_battery_max_voltage_request {
+ __u8 status;
+ __le32 max_voltage;
+};
+
+struct gb_battery_capacity_request {
+ __u8 status;
+ __le32 capacity;
+};
+
+struct gb_battery_temperature_request {
+ __u8 status;
+ __le32 temperature;
+};
+
+struct gb_battery_voltage_request {
+ __u8 status;
+ __le32 voltage;
+};
+
+
static const struct greybus_module_id id_table[] = {
{ GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */
{ }, /* terminating NULL entry */
};
+static int battery_operation(struct gb_battery *gb, int type,
+ void *response, int response_size)
+{
+ struct gb_connection *connection = gb->connection;
+ struct gb_operation *operation;
+ struct gb_battery_technology_request *fake_request;
+ u8 *local_response;
+ int ret;
+
+ local_response = kmalloc(response_size, GFP_KERNEL);
+ if (!local_response)
+ return -ENOMEM;
+
+ operation = gb_operation_create(connection, type, 0, response_size);
+ if (!operation) {
+ kfree(local_response);
+ return -ENOMEM;
+ }
+
+ /* Synchronous operation--no callback */
+ ret = gb_operation_request_send(operation, NULL);
+ if (ret) {
+ pr_err("version operation failed (%d)\n", ret);
+ goto out;
+ }
+
+ /*
+ * We only want to look at the status, and all requests have the same
+ * layout for where the status is, so cast this to a random request so
+ * we can see the status easier.
+ */
+ fake_request = (struct gb_battery_technology_request *)local_response;
+ if (fake_request->status) {
+ gb_connection_err(connection, "version response %hhu",
+ fake_request->status);
+ ret = -EIO;
+ } else {
+ /* Good request, so copy to the caller's buffer */
+ memcpy(response, local_response, response_size);
+ }
+out:
+ gb_operation_destroy(operation);
+
+ return ret;
+}
+
+/*
+ * This request only uses the connection field, and if successful,
+ * fills in the major and minor protocol version of the target.
+ */
+static int get_version(struct gb_battery *gb)
+{
+ struct gb_battery_proto_version_response version_request;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_PROTOCOL_VERSION,
+ &version_request, sizeof(version_request));
+ if (retval)
+ return retval;
+
+ if (version_request.major > GB_BATTERY_VERSION_MAJOR) {
+ pr_err("unsupported major version (%hhu > %hhu)\n",
+ version_request.major, GB_BATTERY_VERSION_MAJOR);
+ return -ENOTSUPP;
+ }
+
+ gb->version_major = version_request.major;
+ gb->version_minor = version_request.minor;
+ return 0;
+}
+
static int get_tech(struct gb_battery *gb)
{
- // FIXME - guess!
- return POWER_SUPPLY_TECHNOLOGY_NiMH;
+ struct gb_battery_technology_request tech_request;
+ u32 technology;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_TECHNOLOGY,
+ &tech_request, sizeof(tech_request));
+ if (retval)
+ return retval;
+
+ /*
+ * We have a one-to-one mapping of tech types to power_supply
+ * status, so just return that value.
+ */
+ technology = le32_to_cpu(tech_request.technology);
+ return technology;
}
static int get_status(struct gb_battery *gb)
{
- // FIXME!!!
- return 0;
+ struct gb_battery_status_request status_request;
+ u16 battery_status;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_STATUS,
+ &status_request, sizeof(status_request));
+ if (retval)
+ return retval;
+
+ /*
+ * We have a one-to-one mapping of battery status to power_supply
+ * status, so just return that value.
+ */
+ battery_status = le16_to_cpu(status_request.battery_status);
+ return battery_status;
}
static int get_max_voltage(struct gb_battery *gb)
{
- // FIXME!!!
- return 4700000;
+ struct gb_battery_max_voltage_request volt_request;
+ u32 max_voltage;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_MAX_VOLTAGE,
+ &volt_request, sizeof(volt_request));
+ if (retval)
+ return retval;
+
+ max_voltage = le32_to_cpu(volt_request.max_voltage);
+ return max_voltage;
}
static int get_capacity(struct gb_battery *gb)
{
- // FIXME!!!
- return 0;
+ struct gb_battery_capacity_request capacity_request;
+ u32 capacity;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_CAPACITY,
+ &capacity_request, sizeof(capacity_request));
+ if (retval)
+ return retval;
+
+ capacity = le32_to_cpu(capacity_request.capacity);
+ return capacity;
}
static int get_temp(struct gb_battery *gb)
{
- // FIXME!!!
- return 0;
+ struct gb_battery_temperature_request temp_request;
+ u32 temperature;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_TEMPERATURE,
+ &temp_request, sizeof(temp_request));
+ if (retval)
+ return retval;
+
+ temperature = le32_to_cpu(temp_request.temperature);
+ return temperature;
}
static int get_voltage(struct gb_battery *gb)
{
- // FIXME!!!
- return 0;
+ struct gb_battery_voltage_request voltage_request;
+ u32 voltage;
+ int retval;
+
+ retval = battery_operation(gb, GB_BATTERY_TYPE_VOLTAGE,
+ &voltage_request, sizeof(voltage_request));
+ if (retval)
+ return retval;
+
+ voltage = le32_to_cpu(voltage_request.voltage);
+ return voltage;
}
static int get_property(struct power_supply *b,
@@ -126,6 +326,13 @@ int gb_battery_device_init(struct gb_connection *connection)
gb->connection = connection; // FIXME refcount!
+ /* Check the version */
+ retval = get_version(gb);
+ if (retval) {
+ kfree(gb);
+ return retval;
+ }
+
b = &gb->bat;
// FIXME - get a better (i.e. unique) name
// FIXME - anything else needs to be set?