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