• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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