Merge tag 'xtensa-next-20130912' of git://github.com/czankel/xtensa-linux
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / power / goldfish_battery.c
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
28 struct goldfish_battery_data {
29         void __iomem *reg_base;
30         int irq;
31         spinlock_t lock;
32
33         struct power_supply battery;
34         struct power_supply ac;
35 };
36
37 #define GOLDFISH_BATTERY_READ(data, addr) \
38         (readl(data->reg_base + addr))
39 #define GOLDFISH_BATTERY_WRITE(data, addr, x) \
40         (writel(x, data->reg_base + addr))
41
42 /*
43  * Temporary variable used between goldfish_battery_probe() and
44  * goldfish_battery_open().
45  */
46 static struct goldfish_battery_data *battery_data;
47
48 enum {
49         /* status register */
50         BATTERY_INT_STATUS          = 0x00,
51         /* set this to enable IRQ */
52         BATTERY_INT_ENABLE          = 0x04,
53
54         BATTERY_AC_ONLINE       = 0x08,
55         BATTERY_STATUS          = 0x0C,
56         BATTERY_HEALTH          = 0x10,
57         BATTERY_PRESENT         = 0x14,
58         BATTERY_CAPACITY        = 0x18,
59
60         BATTERY_STATUS_CHANGED  = 1U << 0,
61         AC_STATUS_CHANGED       = 1U << 1,
62         BATTERY_INT_MASK        = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
63 };
64
65
66 static int goldfish_ac_get_property(struct power_supply *psy,
67                         enum power_supply_property psp,
68                         union power_supply_propval *val)
69 {
70         struct goldfish_battery_data *data = container_of(psy,
71                 struct goldfish_battery_data, ac);
72         int ret = 0;
73
74         switch (psp) {
75         case POWER_SUPPLY_PROP_ONLINE:
76                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
77                 break;
78         default:
79                 ret = -EINVAL;
80                 break;
81         }
82         return ret;
83 }
84
85 static int goldfish_battery_get_property(struct power_supply *psy,
86                                  enum power_supply_property psp,
87                                  union power_supply_propval *val)
88 {
89         struct goldfish_battery_data *data = container_of(psy,
90                 struct goldfish_battery_data, battery);
91         int ret = 0;
92
93         switch (psp) {
94         case POWER_SUPPLY_PROP_STATUS:
95                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
96                 break;
97         case POWER_SUPPLY_PROP_HEALTH:
98                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
99                 break;
100         case POWER_SUPPLY_PROP_PRESENT:
101                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
102                 break;
103         case POWER_SUPPLY_PROP_TECHNOLOGY:
104                 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
105                 break;
106         case POWER_SUPPLY_PROP_CAPACITY:
107                 val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
108                 break;
109         default:
110                 ret = -EINVAL;
111                 break;
112         }
113
114         return ret;
115 }
116
117 static enum power_supply_property goldfish_battery_props[] = {
118         POWER_SUPPLY_PROP_STATUS,
119         POWER_SUPPLY_PROP_HEALTH,
120         POWER_SUPPLY_PROP_PRESENT,
121         POWER_SUPPLY_PROP_TECHNOLOGY,
122         POWER_SUPPLY_PROP_CAPACITY,
123 };
124
125 static enum power_supply_property goldfish_ac_props[] = {
126         POWER_SUPPLY_PROP_ONLINE,
127 };
128
129 static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
130 {
131         unsigned long irq_flags;
132         struct goldfish_battery_data *data = dev_id;
133         uint32_t status;
134
135         spin_lock_irqsave(&data->lock, irq_flags);
136
137         /* read status flags, which will clear the interrupt */
138         status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
139         status &= BATTERY_INT_MASK;
140
141         if (status & BATTERY_STATUS_CHANGED)
142                 power_supply_changed(&data->battery);
143         if (status & AC_STATUS_CHANGED)
144                 power_supply_changed(&data->ac);
145
146         spin_unlock_irqrestore(&data->lock, irq_flags);
147         return status ? IRQ_HANDLED : IRQ_NONE;
148 }
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
157         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
158         if (data == NULL)
159                 return -ENOMEM;
160
161         spin_lock_init(&data->lock);
162
163         data->battery.properties = goldfish_battery_props;
164         data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props);
165         data->battery.get_property = goldfish_battery_get_property;
166         data->battery.name = "battery";
167         data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
168
169         data->ac.properties = goldfish_ac_props;
170         data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props);
171         data->ac.get_property = goldfish_ac_get_property;
172         data->ac.name = "ac";
173         data->ac.type = POWER_SUPPLY_TYPE_MAINS;
174
175         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
176         if (r == NULL) {
177                 dev_err(&pdev->dev, "platform_get_resource failed\n");
178                 return -ENODEV;
179         }
180
181         data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
182         if (data->reg_base == NULL) {
183                 dev_err(&pdev->dev, "unable to remap MMIO\n");
184                 return -ENOMEM;
185         }
186
187         data->irq = platform_get_irq(pdev, 0);
188         if (data->irq < 0) {
189                 dev_err(&pdev->dev, "platform_get_irq failed\n");
190                 return -ENODEV;
191         }
192
193         ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt,
194                                                 IRQF_SHARED, pdev->name, data);
195         if (ret)
196                 return ret;
197
198         ret = power_supply_register(&pdev->dev, &data->ac);
199         if (ret)
200                 return ret;
201
202         ret = power_supply_register(&pdev->dev, &data->battery);
203         if (ret) {
204                 power_supply_unregister(&data->ac);
205                 return ret;
206         }
207
208         platform_set_drvdata(pdev, data);
209         battery_data = data;
210
211         GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
212         return 0;
213 }
214
215 static int goldfish_battery_remove(struct platform_device *pdev)
216 {
217         struct goldfish_battery_data *data = platform_get_drvdata(pdev);
218
219         power_supply_unregister(&data->battery);
220         power_supply_unregister(&data->ac);
221         battery_data = NULL;
222         return 0;
223 }
224
225 static struct platform_driver goldfish_battery_device = {
226         .probe          = goldfish_battery_probe,
227         .remove         = goldfish_battery_remove,
228         .driver = {
229                 .name = "goldfish-battery"
230         }
231 };
232 module_platform_driver(goldfish_battery_device);
233
234 MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
235 MODULE_LICENSE("GPL");
236 MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");