blob: 4bd7aed0a27b581c56ef4b3889b22da0d36ce3e9 [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.
5 *
6 * Released under the GPLv2 only.
7 */
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/slab.h>
12#include <linux/power_supply.h>
13#include "greybus.h"
14
15struct gb_battery {
16 struct power_supply bat;
Greg Kroah-Hartmand47aa762014-09-07 15:54:24 -070017 // FIXME
18 // we will want to keep the battery stats in here as we will be getting
19 // updates from the SVC "on the fly" so we don't have to always go ask
20 // the battery for some information. Hopefully...
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +080021 struct gb_connection *connection;
22 u8 version_major;
23 u8 version_minor;
24
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -070025};
26#define to_gb_battery(x) container_of(x, struct gb_battery, bat)
27
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080028/* Version of the Greybus battery protocol we support */
29#define GB_BATTERY_VERSION_MAJOR 0x00
30#define GB_BATTERY_VERSION_MINOR 0x01
31
32/* Greybus battery request types */
33#define GB_BATTERY_TYPE_INVALID 0x00
34#define GB_BATTERY_TYPE_PROTOCOL_VERSION 0x01
35#define GB_BATTERY_TYPE_TECHNOLOGY 0x02
36#define GB_BATTERY_TYPE_STATUS 0x03
37#define GB_BATTERY_TYPE_MAX_VOLTAGE 0x04
38#define GB_BATTERY_TYPE_CAPACITY 0x05
39#define GB_BATTERY_TYPE_TEMPERATURE 0x06
40#define GB_BATTERY_TYPE_VOLTAGE 0x07
41
42struct gb_battery_proto_version_response {
43 __u8 status;
44 __u8 major;
45 __u8 minor;
46};
47
48/* Should match up with battery types in linux/power_supply.h */
49#define GB_BATTERY_TECH_UNKNOWN 0x0000
50#define GB_BATTERY_TECH_NiMH 0x0001
51#define GB_BATTERY_TECH_LION 0x0002
52#define GB_BATTERY_TECH_LIPO 0x0003
53#define GB_BATTERY_TECH_LiFe 0x0004
54#define GB_BATTERY_TECH_NiCd 0x0005
55#define GB_BATTERY_TECH_LiMn 0x0006
56
57struct gb_battery_technology_request {
58 __u8 status;
59 __le32 technology;
60};
61
62/* Should match up with battery status in linux/power_supply.h */
63#define GB_BATTERY_STATUS_UNKNOWN 0x0000
64#define GB_BATTERY_STATUS_CHARGING 0x0001
65#define GB_BATTERY_STATUS_DISCHARGING 0x0002
66#define GB_BATTERY_STATUS_NOT_CHARGING 0x0003
67#define GB_BATTERY_STATUS_FULL 0x0004
68
69struct gb_battery_status_request {
70 __u8 status;
71 __le16 battery_status;
72};
73
74struct gb_battery_max_voltage_request {
75 __u8 status;
76 __le32 max_voltage;
77};
78
79struct gb_battery_capacity_request {
80 __u8 status;
81 __le32 capacity;
82};
83
84struct gb_battery_temperature_request {
85 __u8 status;
86 __le32 temperature;
87};
88
89struct gb_battery_voltage_request {
90 __u8 status;
91 __le32 voltage;
92};
93
94
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +080095static int battery_operation(struct gb_battery *gb, int type,
96 void *response, int response_size)
97{
98 struct gb_connection *connection = gb->connection;
99 struct gb_operation *operation;
100 struct gb_battery_technology_request *fake_request;
101 u8 *local_response;
102 int ret;
103
104 local_response = kmalloc(response_size, GFP_KERNEL);
105 if (!local_response)
106 return -ENOMEM;
107
108 operation = gb_operation_create(connection, type, 0, response_size);
109 if (!operation) {
110 kfree(local_response);
111 return -ENOMEM;
112 }
113
114 /* Synchronous operation--no callback */
115 ret = gb_operation_request_send(operation, NULL);
116 if (ret) {
117 pr_err("version operation failed (%d)\n", ret);
118 goto out;
119 }
120
121 /*
122 * We only want to look at the status, and all requests have the same
123 * layout for where the status is, so cast this to a random request so
124 * we can see the status easier.
125 */
126 fake_request = (struct gb_battery_technology_request *)local_response;
127 if (fake_request->status) {
128 gb_connection_err(connection, "version response %hhu",
129 fake_request->status);
130 ret = -EIO;
131 } else {
132 /* Good request, so copy to the caller's buffer */
133 memcpy(response, local_response, response_size);
134 }
135out:
136 gb_operation_destroy(operation);
Greg Kroah-Hartmanc9346e12014-10-21 15:51:53 +0800137 kfree(local_response);
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800138
139 return ret;
140}
141
142/*
143 * This request only uses the connection field, and if successful,
144 * fills in the major and minor protocol version of the target.
145 */
146static int get_version(struct gb_battery *gb)
147{
148 struct gb_battery_proto_version_response version_request;
149 int retval;
150
151 retval = battery_operation(gb, GB_BATTERY_TYPE_PROTOCOL_VERSION,
152 &version_request, sizeof(version_request));
153 if (retval)
154 return retval;
155
156 if (version_request.major > GB_BATTERY_VERSION_MAJOR) {
157 pr_err("unsupported major version (%hhu > %hhu)\n",
158 version_request.major, GB_BATTERY_VERSION_MAJOR);
159 return -ENOTSUPP;
160 }
161
162 gb->version_major = version_request.major;
163 gb->version_minor = version_request.minor;
164 return 0;
165}
166
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800167static int get_tech(struct gb_battery *gb)
168{
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800169 struct gb_battery_technology_request tech_request;
170 u32 technology;
171 int retval;
172
173 retval = battery_operation(gb, GB_BATTERY_TYPE_TECHNOLOGY,
174 &tech_request, sizeof(tech_request));
175 if (retval)
176 return retval;
177
178 /*
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800179 * Map greybus values to power_supply values. Hopefully these are
180 * "identical" which should allow gcc to optomize the code away to
181 * nothing.
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800182 */
183 technology = le32_to_cpu(tech_request.technology);
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800184 switch (technology) {
185 case GB_BATTERY_TECH_NiMH:
186 technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
187 break;
188 case GB_BATTERY_TECH_LION:
189 technology = POWER_SUPPLY_TECHNOLOGY_LION;
190 break;
191 case GB_BATTERY_TECH_LIPO:
192 technology = POWER_SUPPLY_TECHNOLOGY_LIPO;
193 break;
194 case GB_BATTERY_TECH_LiFe:
195 technology = POWER_SUPPLY_TECHNOLOGY_LiFe;
196 break;
197 case GB_BATTERY_TECH_NiCd:
198 technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
199 break;
200 case GB_BATTERY_TECH_LiMn:
201 technology = POWER_SUPPLY_TECHNOLOGY_LiMn;
202 break;
203 case GB_BATTERY_TECH_UNKNOWN:
204 default:
205 technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
206 break;
207 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800208 return technology;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800209}
210
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700211static int get_status(struct gb_battery *gb)
212{
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800213 struct gb_battery_status_request status_request;
214 u16 battery_status;
215 int retval;
216
217 retval = battery_operation(gb, GB_BATTERY_TYPE_STATUS,
218 &status_request, sizeof(status_request));
219 if (retval)
220 return retval;
221
222 /*
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800223 * Map greybus values to power_supply values. Hopefully these are
224 * "identical" which should allow gcc to optomize the code away to
225 * nothing.
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800226 */
227 battery_status = le16_to_cpu(status_request.battery_status);
Greg Kroah-Hartman0369a452014-10-21 16:25:13 +0800228 switch (battery_status) {
229 case GB_BATTERY_STATUS_CHARGING:
230 battery_status = POWER_SUPPLY_STATUS_CHARGING;
231 break;
232 case GB_BATTERY_STATUS_DISCHARGING:
233 battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
234 break;
235 case GB_BATTERY_STATUS_NOT_CHARGING:
236 battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
237 break;
238 case GB_BATTERY_STATUS_FULL:
239 battery_status = POWER_SUPPLY_STATUS_FULL;
240 break;
241 case GB_BATTERY_STATUS_UNKNOWN:
242 default:
243 battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
244 break;
245 }
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800246 return battery_status;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700247}
248
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800249static int get_max_voltage(struct gb_battery *gb)
250{
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800251 struct gb_battery_max_voltage_request volt_request;
252 u32 max_voltage;
253 int retval;
254
255 retval = battery_operation(gb, GB_BATTERY_TYPE_MAX_VOLTAGE,
256 &volt_request, sizeof(volt_request));
257 if (retval)
258 return retval;
259
260 max_voltage = le32_to_cpu(volt_request.max_voltage);
261 return max_voltage;
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800262}
263
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700264static int get_capacity(struct gb_battery *gb)
265{
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800266 struct gb_battery_capacity_request capacity_request;
267 u32 capacity;
268 int retval;
269
270 retval = battery_operation(gb, GB_BATTERY_TYPE_CAPACITY,
271 &capacity_request, sizeof(capacity_request));
272 if (retval)
273 return retval;
274
275 capacity = le32_to_cpu(capacity_request.capacity);
276 return capacity;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700277}
278
279static int get_temp(struct gb_battery *gb)
280{
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800281 struct gb_battery_temperature_request temp_request;
282 u32 temperature;
283 int retval;
284
285 retval = battery_operation(gb, GB_BATTERY_TYPE_TEMPERATURE,
286 &temp_request, sizeof(temp_request));
287 if (retval)
288 return retval;
289
290 temperature = le32_to_cpu(temp_request.temperature);
291 return temperature;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700292}
293
294static int get_voltage(struct gb_battery *gb)
295{
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800296 struct gb_battery_voltage_request voltage_request;
297 u32 voltage;
298 int retval;
299
300 retval = battery_operation(gb, GB_BATTERY_TYPE_VOLTAGE,
301 &voltage_request, sizeof(voltage_request));
302 if (retval)
303 return retval;
304
305 voltage = le32_to_cpu(voltage_request.voltage);
306 return voltage;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700307}
308
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700309static int get_property(struct power_supply *b,
310 enum power_supply_property psp,
311 union power_supply_propval *val)
312{
313 struct gb_battery *gb = to_gb_battery(b);
314
315 switch (psp) {
316 case POWER_SUPPLY_PROP_TECHNOLOGY:
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800317 val->intval = get_tech(gb);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700318 break;
319
320 case POWER_SUPPLY_PROP_STATUS:
321 val->intval = get_status(gb);
322 break;
323
324 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
Greg Kroah-Hartman43789c32014-10-20 15:09:49 +0800325 val->intval = get_max_voltage(gb);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700326 break;
327
328 case POWER_SUPPLY_PROP_CAPACITY:
329 val->intval = get_capacity(gb);
330 break;
331
332 case POWER_SUPPLY_PROP_TEMP:
333 val->intval = get_temp(gb);
334 break;
335
336 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
337 val->intval = get_voltage(gb);
338 break;
339
340 default:
341 return -EINVAL;
342 }
343
344 return 0;
345}
346
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700347// FIXME - verify this list, odds are some can be removed and others added.
348static enum power_supply_property battery_props[] = {
349 POWER_SUPPLY_PROP_TECHNOLOGY,
350 POWER_SUPPLY_PROP_STATUS,
351 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
352 POWER_SUPPLY_PROP_CAPACITY,
353 POWER_SUPPLY_PROP_TEMP,
354 POWER_SUPPLY_PROP_VOLTAGE_NOW,
355};
356
Alex Elder3689f972014-10-27 06:04:30 -0500357static int gb_battery_connection_init(struct gb_connection *connection)
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700358{
359 struct gb_battery *gb;
360 struct power_supply *b;
361 int retval;
362
363 gb = kzalloc(sizeof(*gb), GFP_KERNEL);
364 if (!gb)
365 return -ENOMEM;
366
Greg Kroah-Hartman6507cce2014-10-27 17:58:54 +0800367 gb->connection = connection;
Alex Elderfb305c32014-10-20 23:01:03 -0500368 connection->private = gb;
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +0800369
Greg Kroah-Hartmanc0855bf2014-10-21 14:31:24 +0800370 /* Check the version */
371 retval = get_version(gb);
372 if (retval) {
373 kfree(gb);
374 return retval;
375 }
376
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700377 b = &gb->bat;
378 // FIXME - get a better (i.e. unique) name
379 // FIXME - anything else needs to be set?
380 b->name = "gb_battery";
381 b->type = POWER_SUPPLY_TYPE_BATTERY,
382 b->properties = battery_props,
383 b->num_properties = ARRAY_SIZE(battery_props),
384 b->get_property = get_property,
385
Greg Kroah-Hartman2bb7eae2014-10-20 15:24:57 +0800386 retval = power_supply_register(&connection->interface->gmod->dev, b);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700387 if (retval) {
388 kfree(gb);
389 return retval;
390 }
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700391
392 return 0;
393}
394
Alex Elder3689f972014-10-27 06:04:30 -0500395static void gb_battery_connection_exit(struct gb_connection *connection)
Alex Elder697e55d2014-10-20 23:01:04 -0500396{
397 struct gb_battery *gb = connection->private;
398
399 power_supply_unregister(&gb->bat);
400 kfree(gb);
401}
402
Alex Elder19d03de2014-11-05 16:12:53 -0600403static struct gb_protocol battery_protocol = {
404 .id = GREYBUS_PROTOCOL_BATTERY,
405 .major = 0,
406 .minor = 1,
Alex Elder5d9fd7e2014-11-05 16:12:54 -0600407 .connection_init = gb_battery_connection_init,
408 .connection_exit = gb_battery_connection_exit,
Alex Elderf8fb05e2014-11-05 16:12:55 -0600409 .request_recv = NULL, /* no incoming requests */
Alex Elder19d03de2014-11-05 16:12:53 -0600410};
411
412bool gb_battery_protocol_init(void)
413{
414 return gb_protocol_register(&battery_protocol);
415}
416
417void gb_battery_protocol_exit(void)
418{
419 gb_protocol_deregister(&battery_protocol);
420}