]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/power/supply/goldfish_battery.c
f65ceb50d5880fddd822894b1b20a679b1b6968c
[linux.git] / drivers / power / supply / goldfish_battery.c
1 // SPDX-License-Identifier: GPL
2 /*
3  * Power supply driver for the goldfish emulator
4  *
5  * Copyright (C) 2008 Google, Inc.
6  * Copyright (C) 2012 Intel, Inc.
7  * Copyright (C) 2013 Intel, Inc.
8  * Author: Mike Lockwood <lockwood@android.com>
9  */
10
11 #include <linux/module.h>
12 #include <linux/err.h>
13 #include <linux/platform_device.h>
14 #include <linux/power_supply.h>
15 #include <linux/types.h>
16 #include <linux/pci.h>
17 #include <linux/interrupt.h>
18 #include <linux/io.h>
19 #include <linux/acpi.h>
20
21 struct goldfish_battery_data {
22         void __iomem *reg_base;
23         int irq;
24         spinlock_t lock;
25
26         struct power_supply *battery;
27         struct power_supply *ac;
28 };
29
30 #define GOLDFISH_BATTERY_READ(data, addr) \
31         (readl(data->reg_base + addr))
32 #define GOLDFISH_BATTERY_WRITE(data, addr, x) \
33         (writel(x, data->reg_base + addr))
34
35 enum {
36         /* status register */
37         BATTERY_INT_STATUS          = 0x00,
38         /* set this to enable IRQ */
39         BATTERY_INT_ENABLE          = 0x04,
40
41         BATTERY_AC_ONLINE       = 0x08,
42         BATTERY_STATUS          = 0x0C,
43         BATTERY_HEALTH          = 0x10,
44         BATTERY_PRESENT         = 0x14,
45         BATTERY_CAPACITY        = 0x18,
46
47         BATTERY_STATUS_CHANGED  = 1U << 0,
48         AC_STATUS_CHANGED       = 1U << 1,
49         BATTERY_INT_MASK        = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
50 };
51
52
53 static int goldfish_ac_get_property(struct power_supply *psy,
54                         enum power_supply_property psp,
55                         union power_supply_propval *val)
56 {
57         struct goldfish_battery_data *data = power_supply_get_drvdata(psy);
58         int ret = 0;
59
60         switch (psp) {
61         case POWER_SUPPLY_PROP_ONLINE:
62                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
63                 break;
64         default:
65                 ret = -EINVAL;
66                 break;
67         }
68         return ret;
69 }
70
71 static int goldfish_battery_get_property(struct power_supply *psy,
72                                  enum power_supply_property psp,
73                                  union power_supply_propval *val)
74 {
75         struct goldfish_battery_data *data = power_supply_get_drvdata(psy);
76         int ret = 0;
77
78         switch (psp) {
79         case POWER_SUPPLY_PROP_STATUS:
80                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
81                 break;
82         case POWER_SUPPLY_PROP_HEALTH:
83                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
84                 break;
85         case POWER_SUPPLY_PROP_PRESENT:
86                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
87                 break;
88         case POWER_SUPPLY_PROP_TECHNOLOGY:
89                 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
90                 break;
91         case POWER_SUPPLY_PROP_CAPACITY:
92                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
93                 break;
94         default:
95                 ret = -EINVAL;
96                 break;
97         }
98
99         return ret;
100 }
101
102 static enum power_supply_property goldfish_battery_props[] = {
103         POWER_SUPPLY_PROP_STATUS,
104         POWER_SUPPLY_PROP_HEALTH,
105         POWER_SUPPLY_PROP_PRESENT,
106         POWER_SUPPLY_PROP_TECHNOLOGY,
107         POWER_SUPPLY_PROP_CAPACITY,
108 };
109
110 static enum power_supply_property goldfish_ac_props[] = {
111         POWER_SUPPLY_PROP_ONLINE,
112 };
113
114 static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
115 {
116         unsigned long irq_flags;
117         struct goldfish_battery_data *data = dev_id;
118         uint32_t status;
119
120         spin_lock_irqsave(&data->lock, irq_flags);
121
122         /* read status flags, which will clear the interrupt */
123         status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
124         status &= BATTERY_INT_MASK;
125
126         if (status & BATTERY_STATUS_CHANGED)
127                 power_supply_changed(data->battery);
128         if (status & AC_STATUS_CHANGED)
129                 power_supply_changed(data->ac);
130
131         spin_unlock_irqrestore(&data->lock, irq_flags);
132         return status ? IRQ_HANDLED : IRQ_NONE;
133 }
134
135 static const struct power_supply_desc battery_desc = {
136         .properties     = goldfish_battery_props,
137         .num_properties = ARRAY_SIZE(goldfish_battery_props),
138         .get_property   = goldfish_battery_get_property,
139         .name           = "battery",
140         .type           = POWER_SUPPLY_TYPE_BATTERY,
141 };
142
143 static const struct power_supply_desc ac_desc = {
144         .properties     = goldfish_ac_props,
145         .num_properties = ARRAY_SIZE(goldfish_ac_props),
146         .get_property   = goldfish_ac_get_property,
147         .name           = "ac",
148         .type           = POWER_SUPPLY_TYPE_MAINS,
149 };
150
151 static int goldfish_battery_probe(struct platform_device *pdev)
152 {
153         int ret;
154         struct resource *r;
155         struct goldfish_battery_data *data;
156         struct power_supply_config psy_cfg = {};
157
158         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
159         if (data == NULL)
160                 return -ENOMEM;
161
162         spin_lock_init(&data->lock);
163
164         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
165         if (r == NULL) {
166                 dev_err(&pdev->dev, "platform_get_resource failed\n");
167                 return -ENODEV;
168         }
169
170         data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
171         if (data->reg_base == NULL) {
172                 dev_err(&pdev->dev, "unable to remap MMIO\n");
173                 return -ENOMEM;
174         }
175
176         data->irq = platform_get_irq(pdev, 0);
177         if (data->irq < 0) {
178                 dev_err(&pdev->dev, "platform_get_irq failed\n");
179                 return -ENODEV;
180         }
181
182         ret = devm_request_irq(&pdev->dev, data->irq,
183                                goldfish_battery_interrupt,
184                                IRQF_SHARED, pdev->name, data);
185         if (ret)
186                 return ret;
187
188         psy_cfg.drv_data = data;
189
190         data->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
191         if (IS_ERR(data->ac))
192                 return PTR_ERR(data->ac);
193
194         data->battery = power_supply_register(&pdev->dev, &battery_desc,
195                                                 &psy_cfg);
196         if (IS_ERR(data->battery)) {
197                 power_supply_unregister(data->ac);
198                 return PTR_ERR(data->battery);
199         }
200
201         platform_set_drvdata(pdev, data);
202
203         GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
204         return 0;
205 }
206
207 static int goldfish_battery_remove(struct platform_device *pdev)
208 {
209         struct goldfish_battery_data *data = platform_get_drvdata(pdev);
210
211         power_supply_unregister(data->battery);
212         power_supply_unregister(data->ac);
213         return 0;
214 }
215
216 static const struct of_device_id goldfish_battery_of_match[] = {
217         { .compatible = "google,goldfish-battery", },
218         {},
219 };
220 MODULE_DEVICE_TABLE(of, goldfish_battery_of_match);
221
222 static const struct acpi_device_id goldfish_battery_acpi_match[] = {
223         { "GFSH0001", 0 },
224         { },
225 };
226 MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match);
227
228 static struct platform_driver goldfish_battery_device = {
229         .probe          = goldfish_battery_probe,
230         .remove         = goldfish_battery_remove,
231         .driver = {
232                 .name = "goldfish-battery",
233                 .of_match_table = goldfish_battery_of_match,
234                 .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match),
235         }
236 };
237 module_platform_driver(goldfish_battery_device);
238
239 MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
240 MODULE_LICENSE("GPL");
241 MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");