blob: aad1749752586aea016110739fb0afa86bee8299 [file] [log] [blame]
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -07001/*
2 * Battery driver for a Greybus module.
3 *
4 * Copyright 2014 Google Inc.
Alex Eldera46e9672014-12-12 12:08:42 -06005 * Copyright 2014 Linaro Ltd.
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -07006 *
7 * Released under the GPLv2 only.
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/slab.h>
13#include <linux/power_supply.h>
14#include "greybus.h"
15
16struct gb_battery {
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +020017 /*
18 * The power supply api changed in 4.1, so handle both the old
19 * and new apis in the same driver for now, until this is merged
20 * upstream, when all of these version checks can be removed.
21 */
Alex Elder5c586402015-05-07 13:00:21 -050022#ifdef DRIVER_OWNS_PSY_STRUCT
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070023 struct power_supply bat;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +020024#define to_gb_battery(x) container_of(x, struct gb_battery, bat)
25#else
26 struct power_supply *bat;
27 struct power_supply_desc desc;
28#define to_gb_battery(x) power_supply_get_drvdata(x)
29#endif
Greg Kroah-Hartmand47aa762014-09-07 15:54:24 -070030 // FIXME
31 // we will want to keep the battery stats in here as we will be getting
32 // updates from the SVC "on the fly" so we don't have to always go ask
33 // the battery for some information. Hopefully...
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +080034 struct gb_connection *connection;
35 u8 version_major;
36 u8 version_minor;
37
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070038};
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070039
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080040/* Version of the Greybus battery protocol we support */
41#define GB_BATTERY_VERSION_MAJOR 0x00
42#define GB_BATTERY_VERSION_MINOR 0x01
43
44/* Greybus battery request types */
45#define GB_BATTERY_TYPE_INVALID 0x00
46#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01
47#define GB_BATTERY_TYPE_TECHNOLOGY 0x02
48#define GB_BATTERY_TYPE_STATUS 0x03
49#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -050050#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080051#define GB_BATTERY_TYPE_TEMPERATURE 0x06
52#define GB_BATTERY_TYPE_VOLTAGE 0x07
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -050053#define GB_BATTERY_TYPE_CURRENT 0x08
54#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX
55#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080056
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080057/* Should match up with battery types in linux/power_supply.h */
58#define GB_BATTERY_TECH_UNKNOWN 0x0000
59#define GB_BATTERY_TECH_NiMH 0x0001
60#define GB_BATTERY_TECH_LION 0x0002
61#define GB_BATTERY_TECH_LIPO 0x0003
62#define GB_BATTERY_TECH_LiFe 0x0004
63#define GB_BATTERY_TECH_NiCd 0x0005
64#define GB_BATTERY_TECH_LiMn 0x0006
65
Alex Elder0bbfe042014-11-19 16:29:14 -060066struct gb_battery_technology_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080067 __le32 technology;
68};
69
70/* Should match up with battery status in linux/power_supply.h */
71#define GB_BATTERY_STATUS_UNKNOWN 0x0000
72#define GB_BATTERY_STATUS_CHARGING 0x0001
73#define GB_BATTERY_STATUS_DISCHARGING 0x0002
74#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003
75#define GB_BATTERY_STATUS_FULL 0x0004
76
Alex Elder0bbfe042014-11-19 16:29:14 -060077struct gb_battery_status_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080078 __le16 battery_status;
79};
80
Alex Elder0bbfe042014-11-19 16:29:14 -060081struct gb_battery_max_voltage_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080082 __le32 max_voltage;
83};
84
Alex Elder0bbfe042014-11-19 16:29:14 -060085struct gb_battery_capacity_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080086 __le32 capacity;
87};
88
Alex Elder0bbfe042014-11-19 16:29:14 -060089struct gb_battery_temperature_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080090 __le32 temperature;
91};
92
Alex Elder0bbfe042014-11-19 16:29:14 -060093struct gb_battery_voltage_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080094 __le32 voltage;
95};
96
Viresh Kumar36e79de2015-01-21 18:12:36 +053097/* Define get_version() routine */
98define_get_version(gb_battery, BATTERY);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080099
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800100static int get_tech(struct gb_battery *gb)
101{
Alex Elder0bbfe042014-11-19 16:29:14 -0600102 struct gb_battery_technology_response tech_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800103 u32 technology;
104 int retval;
105
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800106 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY,
107 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600108 &tech_response, sizeof(tech_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800109 if (retval)
110 return retval;
111
112 /*
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800113 * Map greybus values to power_supply values. Hopefully these are
Viresh Kumar696e0cc2014-11-21 11:26:30 +0530114 * "identical" which should allow gcc to optimize the code away to
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800115 * nothing.
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800116 */
Alex Elder0bbfe042014-11-19 16:29:14 -0600117 technology = le32_to_cpu(tech_response.technology);
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800118 switch (technology) {
119 case GB_BATTERY_TECH_NiMH:
120 technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
121 break;
122 case GB_BATTERY_TECH_LION:
123 technology = POWER_SUPPLY_TECHNOLOGY_LION;
124 break;
125 case GB_BATTERY_TECH_LIPO:
126 technology = POWER_SUPPLY_TECHNOLOGY_LIPO;
127 break;
128 case GB_BATTERY_TECH_LiFe:
129 technology = POWER_SUPPLY_TECHNOLOGY_LiFe;
130 break;
131 case GB_BATTERY_TECH_NiCd:
132 technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
133 break;
134 case GB_BATTERY_TECH_LiMn:
135 technology = POWER_SUPPLY_TECHNOLOGY_LiMn;
136 break;
137 case GB_BATTERY_TECH_UNKNOWN:
138 default:
139 technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
140 break;
141 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800142 return technology;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800143}
144
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700145static int get_status(struct gb_battery *gb)
146{
Alex Elder0bbfe042014-11-19 16:29:14 -0600147 struct gb_battery_status_response status_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800148 u16 battery_status;
149 int retval;
150
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800151 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS,
152 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600153 &status_response, sizeof(status_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800154 if (retval)
155 return retval;
156
157 /*
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800158 * Map greybus values to power_supply values. Hopefully these are
Viresh Kumar696e0cc2014-11-21 11:26:30 +0530159 * "identical" which should allow gcc to optimize the code away to
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800160 * nothing.
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800161 */
Alex Elder0bbfe042014-11-19 16:29:14 -0600162 battery_status = le16_to_cpu(status_response.battery_status);
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800163 switch (battery_status) {
164 case GB_BATTERY_STATUS_CHARGING:
165 battery_status = POWER_SUPPLY_STATUS_CHARGING;
166 break;
167 case GB_BATTERY_STATUS_DISCHARGING:
168 battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
169 break;
170 case GB_BATTERY_STATUS_NOT_CHARGING:
171 battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
172 break;
173 case GB_BATTERY_STATUS_FULL:
174 battery_status = POWER_SUPPLY_STATUS_FULL;
175 break;
176 case GB_BATTERY_STATUS_UNKNOWN:
177 default:
178 battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
179 break;
180 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800181 return battery_status;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700182}
183
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800184static int get_max_voltage(struct gb_battery *gb)
185{
Alex Elder0bbfe042014-11-19 16:29:14 -0600186 struct gb_battery_max_voltage_response volt_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800187 u32 max_voltage;
188 int retval;
189
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800190 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE,
191 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600192 &volt_response, sizeof(volt_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800193 if (retval)
194 return retval;
195
Alex Elder0bbfe042014-11-19 16:29:14 -0600196 max_voltage = le32_to_cpu(volt_response.max_voltage);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800197 return max_voltage;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800198}
199
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -0500200static int get_percent_capacity(struct gb_battery *gb)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700201{
Alex Elder0bbfe042014-11-19 16:29:14 -0600202 struct gb_battery_capacity_response capacity_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800203 u32 capacity;
204 int retval;
205
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -0500206 retval = gb_operation_sync(gb->connection,
207 GB_BATTERY_TYPE_PERCENT_CAPACITY,
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800208 NULL, 0, &capacity_response,
209 sizeof(capacity_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800210 if (retval)
211 return retval;
212
Alex Elder0bbfe042014-11-19 16:29:14 -0600213 capacity = le32_to_cpu(capacity_response.capacity);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800214 return capacity;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700215}
216
217static int get_temp(struct gb_battery *gb)
218{
Alex Elder0bbfe042014-11-19 16:29:14 -0600219 struct gb_battery_temperature_response temp_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800220 u32 temperature;
221 int retval;
222
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800223 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE,
224 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600225 &temp_response, sizeof(temp_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800226 if (retval)
227 return retval;
228
Alex Elder0bbfe042014-11-19 16:29:14 -0600229 temperature = le32_to_cpu(temp_response.temperature);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800230 return temperature;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700231}
232
233static int get_voltage(struct gb_battery *gb)
234{
Alex Elder0bbfe042014-11-19 16:29:14 -0600235 struct gb_battery_voltage_response voltage_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800236 u32 voltage;
237 int retval;
238
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800239 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE,
240 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600241 &voltage_response, sizeof(voltage_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800242 if (retval)
243 return retval;
244
Alex Elder0bbfe042014-11-19 16:29:14 -0600245 voltage = le32_to_cpu(voltage_response.voltage);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800246 return voltage;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700247}
248
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700249static int get_property(struct power_supply *b,
250 enum power_supply_property psp,
251 union power_supply_propval *val)
252{
253 struct gb_battery *gb = to_gb_battery(b);
254
255 switch (psp) {
256 case POWER_SUPPLY_PROP_TECHNOLOGY:
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800257 val->intval = get_tech(gb);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700258 break;
259
260 case POWER_SUPPLY_PROP_STATUS:
261 val->intval = get_status(gb);
262 break;
263
264 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800265 val->intval = get_max_voltage(gb);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700266 break;
267
268 case POWER_SUPPLY_PROP_CAPACITY:
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -0500269 val->intval = get_percent_capacity(gb);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700270 break;
271
272 case POWER_SUPPLY_PROP_TEMP:
273 val->intval = get_temp(gb);
274 break;
275
276 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
277 val->intval = get_voltage(gb);
278 break;
279
280 default:
281 return -EINVAL;
282 }
283
284 return 0;
285}
286
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700287// FIXME - verify this list, odds are some can be removed and others added.
288static enum power_supply_property battery_props[] = {
289 POWER_SUPPLY_PROP_TECHNOLOGY,
290 POWER_SUPPLY_PROP_STATUS,
291 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
292 POWER_SUPPLY_PROP_CAPACITY,
293 POWER_SUPPLY_PROP_TEMP,
294 POWER_SUPPLY_PROP_VOLTAGE_NOW,
295};
296
Alex Elder5c586402015-05-07 13:00:21 -0500297#ifdef DRIVER_OWNS_PSY_STRUCT
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200298static int init_and_register(struct gb_connection *connection,
299 struct gb_battery *gb)
300{
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200301 // FIXME - get a better (i.e. unique) name
302 // FIXME - anything else needs to be set?
303 gb->bat.name = "gb_battery";
304 gb->bat.type = POWER_SUPPLY_TYPE_BATTERY;
305 gb->bat.properties = battery_props;
306 gb->bat.num_properties = ARRAY_SIZE(battery_props);
307 gb->bat.get_property = get_property;
308
Alex Elder9ade6d32015-05-07 13:00:20 -0500309 return power_supply_register(&connection->bundle->intf->dev, &gb->bat);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200310}
311#else
312static int init_and_register(struct gb_connection *connection,
313 struct gb_battery *gb)
314{
315 struct power_supply_config cfg = {};
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200316
317 cfg.drv_data = gb;
318
319 // FIXME - get a better (i.e. unique) name
320 // FIXME - anything else needs to be set?
321 gb->desc.name = "gb_battery";
322 gb->desc.type = POWER_SUPPLY_TYPE_BATTERY;
323 gb->desc.properties = battery_props;
324 gb->desc.num_properties = ARRAY_SIZE(battery_props);
325 gb->desc.get_property = get_property;
326
327 gb->bat = power_supply_register(&connection->bundle->intf->dev,
328 &gb->desc, &cfg);
Alex Elder9ade6d32015-05-07 13:00:20 -0500329 if (IS_ERR(gb->bat))
330 return PTR_ERR(gb->bat);
331
332 return 0;
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200333}
334#endif
335
Alex Elder3689f972014-10-27 06:04:30 -0500336static int gb_battery_connection_init(struct gb_connection *connection)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700337{
338 struct gb_battery *gb;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700339 int retval;
340
341 gb = kzalloc(sizeof(*gb), GFP_KERNEL);
342 if (!gb)
343 return -ENOMEM;
344
Greg Kroah-Hartman6507cce2014-10-27 17:58:54 +0800345 gb->connection = connection;
Alex Elderfb305c32014-10-20 23:01:03 -0500346 connection->private = gb;
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +0800347
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800348 /* Check the version */
349 retval = get_version(gb);
Alex Elder9ade6d32015-05-07 13:00:20 -0500350 if (retval)
351 goto out;
352 retval = init_and_register(connection, gb);
353out:
354 if (retval)
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800355 kfree(gb);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800356
Alex Elder9ade6d32015-05-07 13:00:20 -0500357 return retval;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700358}
359
Alex Elder3689f972014-10-27 06:04:30 -0500360static void gb_battery_connection_exit(struct gb_connection *connection)
Alex Elder697e55d2014-10-20 23:01:04 -0500361{
362 struct gb_battery *gb = connection->private;
363
Alex Elder5c586402015-05-07 13:00:21 -0500364#ifdef DRIVER_OWNS_PSY_STRUCT
Alex Elder697e55d2014-10-20 23:01:04 -0500365 power_supply_unregister(&gb->bat);
Greg Kroah-Hartmana549be512015-05-01 20:41:00 +0200366#else
367 power_supply_unregister(gb->bat);
368#endif
Alex Elder697e55d2014-10-20 23:01:04 -0500369 kfree(gb);
370}
371
Alex Elder19d03de2014-11-05 16:12:53 -0600372static struct gb_protocol battery_protocol = {
Greg Kroah-Hartman7422a1e2014-12-24 13:01:45 -0800373 .name = "battery",
Alex Elder19d03de2014-11-05 16:12:53 -0600374 .id = GREYBUS_PROTOCOL_BATTERY,
375 .major = 0,
376 .minor = 1,
Alex Elder5d9fd7e2014-11-05 16:12:54 -0600377 .connection_init = gb_battery_connection_init,
378 .connection_exit = gb_battery_connection_exit,
Alex Elderf8fb05e2014-11-05 16:12:55 -0600379 .request_recv = NULL, /* no incoming requests */
Alex Elder19d03de2014-11-05 16:12:53 -0600380};
381
Greg Kroah-Hartman7dd26262014-12-24 13:01:42 -0800382gb_protocol_driver(&battery_protocol);
Alex Elder19d03de2014-11-05 16:12:53 -0600383
Greg Kroah-Hartman7dd26262014-12-24 13:01:42 -0800384MODULE_LICENSE("GPL v2");