ar6000: minimise possibility of race in ar6000_ioctl_siwscan
[kernel.git] / drivers / power / gta01_battery.c
1 /*
2  * Dumb driver for gta01 battery
3  *
4  * Copyright 2009 Openmoko, Inc
5  * Balaji Rao <balajirrao@openmoko.org>
6  */
7
8 #include <linux/module.h>
9 #include <linux/param.h>
10 #include <linux/delay.h>
11 #include <linux/workqueue.h>
12 #include <linux/platform_device.h>
13 #include <linux/power_supply.h>
14 #include <linux/gta01_battery.h>
15
16 struct gta01_battery {
17         struct power_supply psy;
18         struct gta01_bat_platform_data *pdata;
19 };
20
21 static enum power_supply_property gta01_bat_props[] = {
22         POWER_SUPPLY_PROP_PRESENT,
23         POWER_SUPPLY_PROP_STATUS,
24         POWER_SUPPLY_PROP_VOLTAGE_NOW,
25         POWER_SUPPLY_PROP_CURRENT_NOW,
26         POWER_SUPPLY_PROP_CAPACITY,
27         POWER_SUPPLY_PROP_CHARGE_FULL,
28         POWER_SUPPLY_PROP_CHARGE_NOW,
29 };
30
31 /* Capacity of typical BL-5C dumb battery */
32 #define GTA01_BAT_CHARGE_FULL   850000
33
34 static int gta01_bat_voltscale(int volt)
35 {
36         /* This table is suggested by SpeedEvil based on analysis of
37          * experimental data */
38         static const int lut[][2] = {
39                 { 4120, 100 },
40                 { 3900, 60 },
41                 { 3740, 25 },
42                 { 3600, 5 },
43                 { 3000, 0 } };
44         int i, res = 0;
45
46         if (volt > lut[0][0])
47                 res = lut[0][1];
48         else
49                 for (i = 0; lut[i][1]; i++) {
50                         if (volt <= lut[i][0] && volt >= lut[i+1][0]) {
51                                 res = lut[i][1] - (lut[i][0]-volt)*
52                                         (lut[i][1]-lut[i+1][1])/
53                                         (lut[i][0]-lut[i+1][0]);
54                                 break;
55                         }
56                 }
57         return res;
58 }
59
60 static int gta01_bat_get_property(struct power_supply *psy,
61                                        enum power_supply_property psp,
62                                        union power_supply_propval *val)
63 {
64         struct gta01_battery *bat = container_of(psy, struct gta01_battery, psy);
65         
66         switch(psp) {
67         case POWER_SUPPLY_PROP_STATUS:
68                 if (bat->pdata->get_charging_status)
69                         if (bat->pdata->get_charging_status())
70                                 val->intval = POWER_SUPPLY_STATUS_CHARGING;
71                         else
72                                 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
73                 else
74                         val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
75                 break;
76         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
77                 if (bat->pdata->get_voltage)
78                         val->intval = bat->pdata->get_voltage();
79                 else
80                         val->intval = 0;
81                 break;
82         case POWER_SUPPLY_PROP_CURRENT_NOW:
83                 if (bat->pdata->get_current)
84                         val->intval = bat->pdata->get_current();
85                 else
86                         val->intval = 0;
87                 break;
88         case POWER_SUPPLY_PROP_PRESENT:
89                 val->intval = 1; /* You must never run GTA01 without battery. */
90                 break;
91         case POWER_SUPPLY_PROP_CHARGE_NOW:
92                 if (bat->pdata->get_voltage) {
93                         int perc = gta01_bat_voltscale(
94                                         bat->pdata->get_voltage()/1000);
95                         val->intval = perc * GTA01_BAT_CHARGE_FULL / 100;
96                 } else
97                         val->intval = 0;
98                 break;
99         case POWER_SUPPLY_PROP_CAPACITY:
100                 if (bat->pdata->get_voltage)
101                         val->intval = gta01_bat_voltscale(
102                                         bat->pdata->get_voltage()/1000);
103                 else
104                         val->intval = 0;
105                 break;
106         case POWER_SUPPLY_PROP_CHARGE_FULL:
107                 val->intval = GTA01_BAT_CHARGE_FULL;
108                 break;
109
110         default:
111                 return -EINVAL;
112         }
113
114         return 0;
115 }
116
117 static void gta01_bat_ext_changed(struct power_supply *psy)
118 {
119         struct gta01_battery *bat = container_of(psy, struct gta01_battery, psy);
120         power_supply_changed(&bat->psy);
121 }
122
123 static int gta01_battery_probe(struct platform_device *pdev)
124 {
125         struct gta01_battery *gta01_bat;
126
127         gta01_bat = kzalloc(sizeof(*gta01_bat), GFP_KERNEL);
128         if (!gta01_bat)
129                 return -ENOMEM;
130
131         gta01_bat->psy.name = "battery";
132         gta01_bat->psy.type = POWER_SUPPLY_TYPE_BATTERY;
133         gta01_bat->psy.properties = gta01_bat_props;
134         gta01_bat->psy.num_properties = ARRAY_SIZE(gta01_bat_props);
135         gta01_bat->psy.get_property = gta01_bat_get_property;
136         gta01_bat->psy.external_power_changed = gta01_bat_ext_changed;
137
138         gta01_bat->pdata = pdev->dev.platform_data;
139         platform_set_drvdata(pdev, gta01_bat);
140         power_supply_register(&pdev->dev, &gta01_bat->psy);
141
142         return 0;
143 }
144
145 static int gta01_battery_remove(struct platform_device *pdev)
146 {
147         struct gta01_battery *bat = platform_get_drvdata(pdev);
148
149         power_supply_unregister(&bat->psy);
150         kfree(bat);
151
152         return 0;
153 }
154
155 static struct platform_driver gta01_battery_driver = {
156         .driver = {
157                 .name = "gta01_battery",
158         },
159         .probe    = gta01_battery_probe,
160         .remove   = gta01_battery_remove,
161 };
162
163 static int __init gta01_battery_init(void)
164 {
165         return platform_driver_register(&gta01_battery_driver);
166 }
167
168 static void __exit gta01_battery_exit(void)
169 {
170         platform_driver_unregister(&gta01_battery_driver);
171 }
172
173 module_init(gta01_battery_init);
174 module_exit(gta01_battery_exit);
175
176 MODULE_LICENSE("GPL");
177 MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
178 MODULE_DESCRIPTION("gta01 battery driver");