1 /*
2 * Power supply driver for the goldfish emulator
3 *
4 * Copyright (C) 2008 Google, Inc.
5 * Copyright (C) 2012 Intel, Inc.
6 * Copyright (C) 2013 Intel, Inc.
7 * Author: Mike Lockwood <lockwood@android.com>
8 *
9 * This software is licensed under the terms of the GNU General Public
10 * License version 2, as published by the Free Software Foundation, and
11 * may be copied, distributed, and modified under those terms.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19 #include <linux/module.h>
20 #include <linux/err.h>
21 #include <linux/platform_device.h>
22 #include <linux/power_supply.h>
23 #include <linux/types.h>
24 #include <linux/pci.h>
25 #include <linux/interrupt.h>
26 #include <linux/io.h>
27 #include <linux/acpi.h>
28
29 struct goldfish_battery_data {
30 void __iomem *reg_base;
31 int irq;
32 spinlock_t lock;
33
34 struct power_supply battery;
35 struct power_supply ac;
36 };
37
38 #define GOLDFISH_BATTERY_READ(data, addr) \
39 (readl(data->reg_base + addr))
40 #define GOLDFISH_BATTERY_WRITE(data, addr, x) \
41 (writel(x, data->reg_base + addr))
42
43 /*
44 * Temporary variable used between goldfish_battery_probe() and
45 * goldfish_battery_open().
46 */
47 static struct goldfish_battery_data *battery_data;
48
49 enum {
50 /* status register */
51 BATTERY_INT_STATUS = 0x00,
52 /* set this to enable IRQ */
53 BATTERY_INT_ENABLE = 0x04,
54 BATTERY_AC_ONLINE = 0x08,
55 BATTERY_STATUS = 0x0C,
56 BATTERY_HEALTH = 0x10,
57 BATTERY_PRESENT = 0x14,
58 BATTERY_CAPACITY = 0x18,
59 BATTERY_VOLTAGE = 0x1C,
60 BATTERY_TEMP = 0x20,
61 BATTERY_CHARGE_COUNTER = 0x24,
62 BATTERY_VOLTAGE_MAX = 0x28,
63 BATTERY_CURRENT_MAX = 0x2c,
64 BATTERY_STATUS_CHANGED = 1U << 0,
65 AC_STATUS_CHANGED = 1U << 1,
66 BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
67 };
68
69
goldfish_ac_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)70 static int goldfish_ac_get_property(struct power_supply *psy,
71 enum power_supply_property psp,
72 union power_supply_propval *val)
73 {
74 struct goldfish_battery_data *data = container_of(psy,
75 struct goldfish_battery_data, ac);
76 int ret = 0;
77
78 switch (psp) {
79 case POWER_SUPPLY_PROP_ONLINE:
80 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
81 break;
82
83 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
84 val->intval = 5000000; //5 volt
85 break;
86 case POWER_SUPPLY_PROP_CURRENT_MAX:
87 val->intval = 5000000; // 5 amp
88 break;
89 default:
90 ret = -EINVAL;
91 break;
92 }
93 return ret;
94 }
95
goldfish_battery_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)96 static int goldfish_battery_get_property(struct power_supply *psy,
97 enum power_supply_property psp,
98 union power_supply_propval *val)
99 {
100 struct goldfish_battery_data *data = container_of(psy,
101 struct goldfish_battery_data, battery);
102 int ret = 0;
103
104 switch (psp) {
105 case POWER_SUPPLY_PROP_STATUS:
106 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
107 break;
108 case POWER_SUPPLY_PROP_HEALTH:
109 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
110 break;
111 case POWER_SUPPLY_PROP_PRESENT:
112 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
113 break;
114 case POWER_SUPPLY_PROP_TECHNOLOGY:
115 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
116 break;
117 case POWER_SUPPLY_PROP_CAPACITY:
118 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
119 break;
120 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
121 val->intval = 5000000; // 5 volt
122 break;
123 case POWER_SUPPLY_PROP_TEMP:
124 val->intval = 250; // 25 celsuis
125 break;
126 case POWER_SUPPLY_PROP_CHARGE_COUNTER:
127 val->intval = 10;
128 break;
129 default:
130 ret = -EINVAL;
131 break;
132 }
133
134 return ret;
135 }
136
137 static enum power_supply_property goldfish_battery_props[] = {
138 POWER_SUPPLY_PROP_STATUS,
139 POWER_SUPPLY_PROP_HEALTH,
140 POWER_SUPPLY_PROP_PRESENT,
141 POWER_SUPPLY_PROP_TECHNOLOGY,
142 POWER_SUPPLY_PROP_CAPACITY,
143 POWER_SUPPLY_PROP_VOLTAGE_NOW,
144 POWER_SUPPLY_PROP_TEMP,
145 POWER_SUPPLY_PROP_CHARGE_COUNTER,
146 };
147
148 static enum power_supply_property goldfish_ac_props[] = {
149 POWER_SUPPLY_PROP_ONLINE,
150 POWER_SUPPLY_PROP_VOLTAGE_MAX,
151 POWER_SUPPLY_PROP_CURRENT_MAX,
152 };
153
goldfish_battery_interrupt(int irq,void * dev_id)154 static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
155 {
156 unsigned long irq_flags;
157 struct goldfish_battery_data *data = dev_id;
158 uint32_t status;
159
160 spin_lock_irqsave(&data->lock, irq_flags);
161
162 /* read status flags, which will clear the interrupt */
163 status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
164 status &= BATTERY_INT_MASK;
165
166 if (status & BATTERY_STATUS_CHANGED)
167 power_supply_changed(&data->battery);
168 if (status & AC_STATUS_CHANGED)
169 power_supply_changed(&data->ac);
170
171 spin_unlock_irqrestore(&data->lock, irq_flags);
172 return status ? IRQ_HANDLED : IRQ_NONE;
173 }
174
175
goldfish_battery_probe(struct platform_device * pdev)176 static int goldfish_battery_probe(struct platform_device *pdev)
177 {
178 int ret;
179 struct resource *r;
180 struct goldfish_battery_data *data;
181
182 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
183 if (data == NULL)
184 return -ENOMEM;
185
186 spin_lock_init(&data->lock);
187
188 data->battery.properties = goldfish_battery_props;
189 data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props);
190 data->battery.get_property = goldfish_battery_get_property;
191 data->battery.name = "battery";
192 data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
193
194 data->ac.properties = goldfish_ac_props;
195 data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props);
196 data->ac.get_property = goldfish_ac_get_property;
197 data->ac.name = "ac";
198 data->ac.type = POWER_SUPPLY_TYPE_MAINS;
199
200 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
201 if (r == NULL) {
202 dev_err(&pdev->dev, "platform_get_resource failed\n");
203 return -ENODEV;
204 }
205
206 data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
207 if (data->reg_base == NULL) {
208 dev_err(&pdev->dev, "unable to remap MMIO\n");
209 return -ENOMEM;
210 }
211
212 data->irq = platform_get_irq(pdev, 0);
213 if (data->irq < 0) {
214 dev_err(&pdev->dev, "platform_get_irq failed\n");
215 return -ENODEV;
216 }
217
218 ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt,
219 IRQF_SHARED, pdev->name, data);
220 if (ret)
221 return ret;
222
223 ret = power_supply_register(&pdev->dev, &data->ac);
224 if (ret)
225 return ret;
226
227 ret = power_supply_register(&pdev->dev, &data->battery);
228 if (ret) {
229 power_supply_unregister(&data->ac);
230 return ret;
231 }
232
233 platform_set_drvdata(pdev, data);
234 battery_data = data;
235
236 GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
237 return 0;
238 }
239
goldfish_battery_remove(struct platform_device * pdev)240 static int goldfish_battery_remove(struct platform_device *pdev)
241 {
242 struct goldfish_battery_data *data = platform_get_drvdata(pdev);
243
244 power_supply_unregister(&data->battery);
245 power_supply_unregister(&data->ac);
246 battery_data = NULL;
247 return 0;
248 }
249
250 static const struct of_device_id goldfish_battery_of_match[] = {
251 { .compatible = "generic,goldfish-battery", },
252 {},
253 };
254 MODULE_DEVICE_TABLE(of, goldfish_battery_of_match);
255
256 static const struct acpi_device_id goldfish_battery_acpi_match[] = {
257 { "GFSH0001", 0 },
258 { },
259 };
260 MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match);
261
262 static struct platform_driver goldfish_battery_device = {
263 .probe = goldfish_battery_probe,
264 .remove = goldfish_battery_remove,
265 .driver = {
266 .name = "goldfish-battery",
267 .of_match_table = goldfish_battery_of_match,
268 .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match),
269 }
270 };
271 module_platform_driver(goldfish_battery_device);
272
273 MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
274 MODULE_LICENSE("GPL");
275 MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");
276