blob: 7ce37377bca5a6a1331c25a4c31106639fdf9383 [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 {
17 struct power_supply bat;
Greg Kroah-Hartmand47aa762014-09-07 15:54:24 -070018 // FIXME
19 // we will want to keep the battery stats in here as we will be getting
20 // updates from the SVC "on the fly" so we don't have to always go ask
21 // the battery for some information. Hopefully...
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +080022 struct gb_connection *connection;
23 u8 version_major;
24 u8 version_minor;
25
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070026};
27#define to_gb_battery(x) container_of(x, struct gb_battery, bat)
28
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080029/* Version of the Greybus battery protocol we support */
30#define GB_BATTERY_VERSION_MAJOR 0x00
31#define GB_BATTERY_VERSION_MINOR 0x01
32
33/* Greybus battery request types */
34#define GB_BATTERY_TYPE_INVALID 0x00
35#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01
36#define GB_BATTERY_TYPE_TECHNOLOGY 0x02
37#define GB_BATTERY_TYPE_STATUS 0x03
38#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -050039#define GB_BATTERY_TYPE_PERCENT_CAPACITY 0x05
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080040#define GB_BATTERY_TYPE_TEMPERATURE 0x06
41#define GB_BATTERY_TYPE_VOLTAGE 0x07
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -050042#define GB_BATTERY_TYPE_CURRENT 0x08
43#define GB_BATTERY_TYPE_CAPACITY 0x09 // TODO - POWER_SUPPLY_PROP_CURRENT_MAX
44#define GB_BATTERY_TYPE_SHUTDOWN_TEMP 0x0a // TODO - POWER_SUPPLY_PROP_TEMP_ALERT_MAX
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080045
46struct gb_battery_proto_version_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080047 __u8 major;
48 __u8 minor;
49};
50
51/* Should match up with battery types in linux/power_supply.h */
52#define GB_BATTERY_TECH_UNKNOWN 0x0000
53#define GB_BATTERY_TECH_NiMH 0x0001
54#define GB_BATTERY_TECH_LION 0x0002
55#define GB_BATTERY_TECH_LIPO 0x0003
56#define GB_BATTERY_TECH_LiFe 0x0004
57#define GB_BATTERY_TECH_NiCd 0x0005
58#define GB_BATTERY_TECH_LiMn 0x0006
59
Alex Elder0bbfe042014-11-19 16:29:14 -060060struct gb_battery_technology_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080061 __le32 technology;
62};
63
64/* Should match up with battery status in linux/power_supply.h */
65#define GB_BATTERY_STATUS_UNKNOWN 0x0000
66#define GB_BATTERY_STATUS_CHARGING 0x0001
67#define GB_BATTERY_STATUS_DISCHARGING 0x0002
68#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003
69#define GB_BATTERY_STATUS_FULL 0x0004
70
Alex Elder0bbfe042014-11-19 16:29:14 -060071struct gb_battery_status_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080072 __le16 battery_status;
73};
74
Alex Elder0bbfe042014-11-19 16:29:14 -060075struct gb_battery_max_voltage_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080076 __le32 max_voltage;
77};
78
Alex Elder0bbfe042014-11-19 16:29:14 -060079struct gb_battery_capacity_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080080 __le32 capacity;
81};
82
Alex Elder0bbfe042014-11-19 16:29:14 -060083struct gb_battery_temperature_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080084 __le32 temperature;
85};
86
Alex Elder0bbfe042014-11-19 16:29:14 -060087struct gb_battery_voltage_response {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080088 __le32 voltage;
89};
90
Alex Elder0bbfe042014-11-19 16:29:14 -060091/*
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080092 * This request only uses the connection field, and if successful,
93 * fills in the major and minor protocol version of the target.
94 */
95static int get_version(struct gb_battery *gb)
96{
Alex Elder0bbfe042014-11-19 16:29:14 -060097 struct gb_battery_proto_version_response version_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080098 int retval;
99
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800100 retval = gb_operation_sync(gb->connection,
101 GB_BATTERY_TYPE_PROTOCOL_VERSION,
102 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600103 &version_response, sizeof(version_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800104 if (retval)
105 return retval;
106
Alex Elder0bbfe042014-11-19 16:29:14 -0600107 if (version_response.major > GB_BATTERY_VERSION_MAJOR) {
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800108 pr_err("unsupported major version (%hhu > %hhu)\n",
Alex Elder0bbfe042014-11-19 16:29:14 -0600109 version_response.major, GB_BATTERY_VERSION_MAJOR);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800110 return -ENOTSUPP;
111 }
112
Alex Elder0bbfe042014-11-19 16:29:14 -0600113 gb->version_major = version_response.major;
114 gb->version_minor = version_response.minor;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800115 return 0;
116}
117
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800118static int get_tech(struct gb_battery *gb)
119{
Alex Elder0bbfe042014-11-19 16:29:14 -0600120 struct gb_battery_technology_response tech_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800121 u32 technology;
122 int retval;
123
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800124 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TECHNOLOGY,
125 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600126 &tech_response, sizeof(tech_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800127 if (retval)
128 return retval;
129
130 /*
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800131 * Map greybus values to power_supply values. Hopefully these are
Viresh Kumar696e0cc2014-11-21 11:26:30 +0530132 * "identical" which should allow gcc to optimize the code away to
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800133 * nothing.
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800134 */
Alex Elder0bbfe042014-11-19 16:29:14 -0600135 technology = le32_to_cpu(tech_response.technology);
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800136 switch (technology) {
137 case GB_BATTERY_TECH_NiMH:
138 technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
139 break;
140 case GB_BATTERY_TECH_LION:
141 technology = POWER_SUPPLY_TECHNOLOGY_LION;
142 break;
143 case GB_BATTERY_TECH_LIPO:
144 technology = POWER_SUPPLY_TECHNOLOGY_LIPO;
145 break;
146 case GB_BATTERY_TECH_LiFe:
147 technology = POWER_SUPPLY_TECHNOLOGY_LiFe;
148 break;
149 case GB_BATTERY_TECH_NiCd:
150 technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
151 break;
152 case GB_BATTERY_TECH_LiMn:
153 technology = POWER_SUPPLY_TECHNOLOGY_LiMn;
154 break;
155 case GB_BATTERY_TECH_UNKNOWN:
156 default:
157 technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
158 break;
159 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800160 return technology;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800161}
162
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700163static int get_status(struct gb_battery *gb)
164{
Alex Elder0bbfe042014-11-19 16:29:14 -0600165 struct gb_battery_status_response status_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800166 u16 battery_status;
167 int retval;
168
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800169 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_STATUS,
170 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600171 &status_response, sizeof(status_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800172 if (retval)
173 return retval;
174
175 /*
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800176 * Map greybus values to power_supply values. Hopefully these are
Viresh Kumar696e0cc2014-11-21 11:26:30 +0530177 * "identical" which should allow gcc to optimize the code away to
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800178 * nothing.
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800179 */
Alex Elder0bbfe042014-11-19 16:29:14 -0600180 battery_status = le16_to_cpu(status_response.battery_status);
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800181 switch (battery_status) {
182 case GB_BATTERY_STATUS_CHARGING:
183 battery_status = POWER_SUPPLY_STATUS_CHARGING;
184 break;
185 case GB_BATTERY_STATUS_DISCHARGING:
186 battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
187 break;
188 case GB_BATTERY_STATUS_NOT_CHARGING:
189 battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
190 break;
191 case GB_BATTERY_STATUS_FULL:
192 battery_status = POWER_SUPPLY_STATUS_FULL;
193 break;
194 case GB_BATTERY_STATUS_UNKNOWN:
195 default:
196 battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
197 break;
198 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800199 return battery_status;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700200}
201
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800202static int get_max_voltage(struct gb_battery *gb)
203{
Alex Elder0bbfe042014-11-19 16:29:14 -0600204 struct gb_battery_max_voltage_response volt_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800205 u32 max_voltage;
206 int retval;
207
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800208 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_MAX_VOLTAGE,
209 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600210 &volt_response, sizeof(volt_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800211 if (retval)
212 return retval;
213
Alex Elder0bbfe042014-11-19 16:29:14 -0600214 max_voltage = le32_to_cpu(volt_response.max_voltage);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800215 return max_voltage;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800216}
217
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -0500218static int get_percent_capacity(struct gb_battery *gb)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700219{
Alex Elder0bbfe042014-11-19 16:29:14 -0600220 struct gb_battery_capacity_response capacity_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800221 u32 capacity;
222 int retval;
223
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -0500224 retval = gb_operation_sync(gb->connection,
225 GB_BATTERY_TYPE_PERCENT_CAPACITY,
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800226 NULL, 0, &capacity_response,
227 sizeof(capacity_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800228 if (retval)
229 return retval;
230
Alex Elder0bbfe042014-11-19 16:29:14 -0600231 capacity = le32_to_cpu(capacity_response.capacity);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800232 return capacity;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700233}
234
235static int get_temp(struct gb_battery *gb)
236{
Alex Elder0bbfe042014-11-19 16:29:14 -0600237 struct gb_battery_temperature_response temp_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800238 u32 temperature;
239 int retval;
240
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800241 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_TEMPERATURE,
242 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600243 &temp_response, sizeof(temp_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800244 if (retval)
245 return retval;
246
Alex Elder0bbfe042014-11-19 16:29:14 -0600247 temperature = le32_to_cpu(temp_response.temperature);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800248 return temperature;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700249}
250
251static int get_voltage(struct gb_battery *gb)
252{
Alex Elder0bbfe042014-11-19 16:29:14 -0600253 struct gb_battery_voltage_response voltage_response;
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800254 u32 voltage;
255 int retval;
256
Greg Kroah-Hartmand5671a62014-11-23 17:45:19 -0800257 retval = gb_operation_sync(gb->connection, GB_BATTERY_TYPE_VOLTAGE,
258 NULL, 0,
Alex Elder0bbfe042014-11-19 16:29:14 -0600259 &voltage_response, sizeof(voltage_response));
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800260 if (retval)
261 return retval;
262
Alex Elder0bbfe042014-11-19 16:29:14 -0600263 voltage = le32_to_cpu(voltage_response.voltage);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800264 return voltage;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700265}
266
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700267static int get_property(struct power_supply *b,
268 enum power_supply_property psp,
269 union power_supply_propval *val)
270{
271 struct gb_battery *gb = to_gb_battery(b);
272
273 switch (psp) {
274 case POWER_SUPPLY_PROP_TECHNOLOGY:
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800275 val->intval = get_tech(gb);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700276 break;
277
278 case POWER_SUPPLY_PROP_STATUS:
279 val->intval = get_status(gb);
280 break;
281
282 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800283 val->intval = get_max_voltage(gb);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700284 break;
285
286 case POWER_SUPPLY_PROP_CAPACITY:
Greg Kroah-Hartman6b7dff82014-12-08 17:45:10 -0500287 val->intval = get_percent_capacity(gb);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700288 break;
289
290 case POWER_SUPPLY_PROP_TEMP:
291 val->intval = get_temp(gb);
292 break;
293
294 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
295 val->intval = get_voltage(gb);
296 break;
297
298 default:
299 return -EINVAL;
300 }
301
302 return 0;
303}
304
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700305// FIXME - verify this list, odds are some can be removed and others added.
306static enum power_supply_property battery_props[] = {
307 POWER_SUPPLY_PROP_TECHNOLOGY,
308 POWER_SUPPLY_PROP_STATUS,
309 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
310 POWER_SUPPLY_PROP_CAPACITY,
311 POWER_SUPPLY_PROP_TEMP,
312 POWER_SUPPLY_PROP_VOLTAGE_NOW,
313};
314
Alex Elder3689f972014-10-27 06:04:30 -0500315static int gb_battery_connection_init(struct gb_connection *connection)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700316{
317 struct gb_battery *gb;
318 struct power_supply *b;
319 int retval;
320
321 gb = kzalloc(sizeof(*gb), GFP_KERNEL);
322 if (!gb)
323 return -ENOMEM;
324
Greg Kroah-Hartman6507cce2014-10-27 17:58:54 +0800325 gb->connection = connection;
Alex Elderfb305c32014-10-20 23:01:03 -0500326 connection->private = gb;
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +0800327
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800328 /* Check the version */
329 retval = get_version(gb);
330 if (retval) {
331 kfree(gb);
332 return retval;
333 }
334
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700335 b = &gb->bat;
336 // FIXME - get a better (i.e. unique) name
337 // FIXME - anything else needs to be set?
338 b->name = "gb_battery";
339 b->type = POWER_SUPPLY_TYPE_BATTERY,
340 b->properties = battery_props,
341 b->num_properties = ARRAY_SIZE(battery_props),
342 b->get_property = get_property,
343
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800344 retval = power_supply_register(&connection->bundle->intf->dev, b);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700345 if (retval) {
346 kfree(gb);
347 return retval;
348 }
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700349
350 return 0;
351}
352
Alex Elder3689f972014-10-27 06:04:30 -0500353static void gb_battery_connection_exit(struct gb_connection *connection)
Alex Elder697e55d2014-10-20 23:01:04 -0500354{
355 struct gb_battery *gb = connection->private;
356
357 power_supply_unregister(&gb->bat);
358 kfree(gb);
359}
360
Alex Elder19d03de2014-11-05 16:12:53 -0600361static struct gb_protocol battery_protocol = {
362 .id = GREYBUS_PROTOCOL_BATTERY,
363 .major = 0,
364 .minor = 1,
Alex Elder5d9fd7e2014-11-05 16:12:54 -0600365 .connection_init = gb_battery_connection_init,
366 .connection_exit = gb_battery_connection_exit,
Alex Elderf8fb05e2014-11-05 16:12:55 -0600367 .request_recv = NULL, /* no incoming requests */
Alex Elder19d03de2014-11-05 16:12:53 -0600368};
369
Greg Kroah-Hartman7dd26262014-12-24 13:01:42 -0800370gb_protocol_driver(&battery_protocol);
Alex Elder19d03de2014-11-05 16:12:53 -0600371
Greg Kroah-Hartman7dd26262014-12-24 13:01:42 -0800372MODULE_LICENSE("GPL v2");