]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/battery-gb.c
greybus: Merge branch 'gregkh_work' into master
[karo-tx-linux.git] / drivers / staging / greybus / battery-gb.c
1 /*
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
15 struct gb_battery {
16         struct power_supply bat;
17         // 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...
21         struct gb_connection *connection;
22         u8 version_major;
23         u8 version_minor;
24
25 };
26 #define to_gb_battery(x) container_of(x, struct gb_battery, bat)
27
28 /* 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
42 struct 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
57 struct 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
69 struct gb_battery_status_request {
70         __u8    status;
71         __le16  battery_status;
72 };
73
74 struct gb_battery_max_voltage_request {
75         __u8    status;
76         __le32  max_voltage;
77 };
78
79 struct gb_battery_capacity_request {
80         __u8    status;
81         __le32  capacity;
82 };
83
84 struct gb_battery_temperature_request {
85         __u8    status;
86         __le32  temperature;
87 };
88
89 struct gb_battery_voltage_request {
90         __u8    status;
91         __le32  voltage;
92 };
93
94
95 static const struct greybus_module_id id_table[] = {
96         { GREYBUS_DEVICE(0x42, 0x42) }, /* make shit up */
97         { },    /* terminating NULL entry */
98 };
99
100 static int battery_operation(struct gb_battery *gb, int type,
101                              void *response, int response_size)
102 {
103         struct gb_connection *connection = gb->connection;
104         struct gb_operation *operation;
105         struct gb_battery_technology_request *fake_request;
106         u8 *local_response;
107         int ret;
108
109         local_response = kmalloc(response_size, GFP_KERNEL);
110         if (!local_response)
111                 return -ENOMEM;
112
113         operation = gb_operation_create(connection, type, 0, response_size);
114         if (!operation) {
115                 kfree(local_response);
116                 return -ENOMEM;
117         }
118
119         /* Synchronous operation--no callback */
120         ret = gb_operation_request_send(operation, NULL);
121         if (ret) {
122                 pr_err("version operation failed (%d)\n", ret);
123                 goto out;
124         }
125
126         /*
127          * We only want to look at the status, and all requests have the same
128          * layout for where the status is, so cast this to a random request so
129          * we can see the status easier.
130          */
131         fake_request = (struct gb_battery_technology_request *)local_response;
132         if (fake_request->status) {
133                 gb_connection_err(connection, "version response %hhu",
134                         fake_request->status);
135                 ret = -EIO;
136         } else {
137                 /* Good request, so copy to the caller's buffer */
138                 memcpy(response, local_response, response_size);
139         }
140 out:
141         gb_operation_destroy(operation);
142
143         return ret;
144 }
145
146 /*
147  * This request only uses the connection field, and if successful,
148  * fills in the major and minor protocol version of the target.
149  */
150 static int get_version(struct gb_battery *gb)
151 {
152         struct gb_battery_proto_version_response version_request;
153         int retval;
154
155         retval = battery_operation(gb, GB_BATTERY_TYPE_PROTOCOL_VERSION,
156                                    &version_request, sizeof(version_request));
157         if (retval)
158                 return retval;
159
160         if (version_request.major > GB_BATTERY_VERSION_MAJOR) {
161                 pr_err("unsupported major version (%hhu > %hhu)\n",
162                         version_request.major, GB_BATTERY_VERSION_MAJOR);
163                 return -ENOTSUPP;
164         }
165
166         gb->version_major = version_request.major;
167         gb->version_minor = version_request.minor;
168         return 0;
169 }
170
171 static int get_tech(struct gb_battery *gb)
172 {
173         struct gb_battery_technology_request tech_request;
174         u32 technology;
175         int retval;
176
177         retval = battery_operation(gb, GB_BATTERY_TYPE_TECHNOLOGY,
178                                    &tech_request, sizeof(tech_request));
179         if (retval)
180                 return retval;
181
182         /*
183          * We have a one-to-one mapping of tech types to power_supply
184          * status, so just return that value.
185          */
186         technology = le32_to_cpu(tech_request.technology);
187         return technology;
188 }
189
190 static int get_status(struct gb_battery *gb)
191 {
192         struct gb_battery_status_request status_request;
193         u16 battery_status;
194         int retval;
195
196         retval = battery_operation(gb, GB_BATTERY_TYPE_STATUS,
197                                    &status_request, sizeof(status_request));
198         if (retval)
199                 return retval;
200
201         /*
202          * We have a one-to-one mapping of battery status to power_supply
203          * status, so just return that value.
204          */
205         battery_status = le16_to_cpu(status_request.battery_status);
206         return battery_status;
207 }
208
209 static int get_max_voltage(struct gb_battery *gb)
210 {
211         struct gb_battery_max_voltage_request volt_request;
212         u32 max_voltage;
213         int retval;
214
215         retval = battery_operation(gb, GB_BATTERY_TYPE_MAX_VOLTAGE,
216                                    &volt_request, sizeof(volt_request));
217         if (retval)
218                 return retval;
219
220         max_voltage = le32_to_cpu(volt_request.max_voltage);
221         return max_voltage;
222 }
223
224 static int get_capacity(struct gb_battery *gb)
225 {
226         struct gb_battery_capacity_request capacity_request;
227         u32 capacity;
228         int retval;
229
230         retval = battery_operation(gb, GB_BATTERY_TYPE_CAPACITY,
231                                    &capacity_request, sizeof(capacity_request));
232         if (retval)
233                 return retval;
234
235         capacity = le32_to_cpu(capacity_request.capacity);
236         return capacity;
237 }
238
239 static int get_temp(struct gb_battery *gb)
240 {
241         struct gb_battery_temperature_request temp_request;
242         u32 temperature;
243         int retval;
244
245         retval = battery_operation(gb, GB_BATTERY_TYPE_TEMPERATURE,
246                                    &temp_request, sizeof(temp_request));
247         if (retval)
248                 return retval;
249
250         temperature = le32_to_cpu(temp_request.temperature);
251         return temperature;
252 }
253
254 static int get_voltage(struct gb_battery *gb)
255 {
256         struct gb_battery_voltage_request voltage_request;
257         u32 voltage;
258         int retval;
259
260         retval = battery_operation(gb, GB_BATTERY_TYPE_VOLTAGE,
261                                    &voltage_request, sizeof(voltage_request));
262         if (retval)
263                 return retval;
264
265         voltage = le32_to_cpu(voltage_request.voltage);
266         return voltage;
267 }
268
269 static int get_property(struct power_supply *b,
270                         enum power_supply_property psp,
271                         union power_supply_propval *val)
272 {
273         struct gb_battery *gb = to_gb_battery(b);
274
275         switch (psp) {
276         case POWER_SUPPLY_PROP_TECHNOLOGY:
277                 val->intval = get_tech(gb);
278                 break;
279
280         case POWER_SUPPLY_PROP_STATUS:
281                 val->intval = get_status(gb);
282                 break;
283
284         case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
285                 val->intval = get_max_voltage(gb);
286                 break;
287
288         case POWER_SUPPLY_PROP_CAPACITY:
289                 val->intval = get_capacity(gb);
290                 break;
291
292         case POWER_SUPPLY_PROP_TEMP:
293                 val->intval = get_temp(gb);
294                 break;
295
296         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
297                 val->intval = get_voltage(gb);
298                 break;
299
300         default:
301                 return -EINVAL;
302         }
303
304         return 0;
305 }
306
307 // FIXME - verify this list, odds are some can be removed and others added.
308 static enum power_supply_property battery_props[] = {
309         POWER_SUPPLY_PROP_TECHNOLOGY,
310         POWER_SUPPLY_PROP_STATUS,
311         POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
312         POWER_SUPPLY_PROP_CAPACITY,
313         POWER_SUPPLY_PROP_TEMP,
314         POWER_SUPPLY_PROP_VOLTAGE_NOW,
315 };
316
317 int gb_battery_device_init(struct gb_connection *connection)
318 {
319         struct gb_battery *gb;
320         struct power_supply *b;
321         int retval;
322
323         gb = kzalloc(sizeof(*gb), GFP_KERNEL);
324         if (!gb)
325                 return -ENOMEM;
326
327         gb->connection = connection;    // FIXME refcount!
328         connection->private = gb;
329
330         /* Check the version */
331         retval = get_version(gb);
332         if (retval) {
333                 kfree(gb);
334                 return retval;
335         }
336
337         b = &gb->bat;
338         // FIXME - get a better (i.e. unique) name
339         // FIXME - anything else needs to be set?
340         b->name                 = "gb_battery";
341         b->type                 = POWER_SUPPLY_TYPE_BATTERY,
342         b->properties           = battery_props,
343         b->num_properties       = ARRAY_SIZE(battery_props),
344         b->get_property         = get_property,
345
346         retval = power_supply_register(&connection->interface->gmod->dev, b);
347         if (retval) {
348                 kfree(gb);
349                 return retval;
350         }
351
352         return 0;
353 }
354
355 void gb_battery_device_exit(struct gb_connection *connection)
356 {
357         struct gb_battery *gb = connection->private;
358
359         power_supply_unregister(&gb->bat);
360         kfree(gb);
361 }
362
363 void gb_battery_disconnect(struct gb_module *gmod)
364 {
365 #if 0
366         struct gb_battery *gb;
367
368         gb = gmod->gb_battery;
369         if (!gb)
370                 return;
371
372         power_supply_unregister(&gb->bat);
373
374         kfree(gb);
375 #endif
376 }
377
378 #if 0
379 static struct greybus_driver battery_gb_driver = {
380         .probe =        gb_battery_probe,
381         .disconnect =   gb_battery_disconnect,
382         .id_table =     id_table,
383 };
384
385 module_greybus_driver(battery_gb_driver);
386 MODULE_LICENSE("GPL");
387 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
388 #endif