2 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
3 * Driver for chargers which report their online status through a GPIO pin
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
10 * You should have received a copy of the GNU General Public License along
11 * with this program; if not, write to the Free Software Foundation, Inc.,
12 * 675 Mass Ave, Cambridge, MA 02139, USA.
16 #include <linux/device.h>
17 #include <linux/gpio.h> /* For legacy platform data */
18 #include <linux/init.h>
19 #include <linux/interrupt.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/platform_device.h>
23 #include <linux/power_supply.h>
24 #include <linux/slab.h>
26 #include <linux/gpio/consumer.h>
28 #include <linux/power/gpio-charger.h>
34 struct power_supply
*charger
;
35 struct power_supply_desc charger_desc
;
36 struct gpio_desc
*gpiod
;
39 static irqreturn_t
gpio_charger_irq(int irq
, void *devid
)
41 struct power_supply
*charger
= devid
;
43 power_supply_changed(charger
);
48 static inline struct gpio_charger
*psy_to_gpio_charger(struct power_supply
*psy
)
50 return power_supply_get_drvdata(psy
);
53 static int gpio_charger_get_property(struct power_supply
*psy
,
54 enum power_supply_property psp
, union power_supply_propval
*val
)
56 struct gpio_charger
*gpio_charger
= psy_to_gpio_charger(psy
);
59 case POWER_SUPPLY_PROP_ONLINE
:
60 val
->intval
= gpiod_get_value_cansleep(gpio_charger
->gpiod
);
69 static enum power_supply_type
gpio_charger_get_type(struct device
*dev
)
71 const char *chargetype
;
73 if (!device_property_read_string(dev
, "charger-type", &chargetype
)) {
74 if (!strcmp("unknown", chargetype
))
75 return POWER_SUPPLY_TYPE_UNKNOWN
;
76 if (!strcmp("battery", chargetype
))
77 return POWER_SUPPLY_TYPE_BATTERY
;
78 if (!strcmp("ups", chargetype
))
79 return POWER_SUPPLY_TYPE_UPS
;
80 if (!strcmp("mains", chargetype
))
81 return POWER_SUPPLY_TYPE_MAINS
;
82 if (!strcmp("usb-sdp", chargetype
))
83 return POWER_SUPPLY_TYPE_USB
;
84 if (!strcmp("usb-dcp", chargetype
))
85 return POWER_SUPPLY_TYPE_USB_DCP
;
86 if (!strcmp("usb-cdp", chargetype
))
87 return POWER_SUPPLY_TYPE_USB_CDP
;
88 if (!strcmp("usb-aca", chargetype
))
89 return POWER_SUPPLY_TYPE_USB_ACA
;
91 dev_warn(dev
, "unknown charger type %s\n", chargetype
);
93 return POWER_SUPPLY_TYPE_UNKNOWN
;
96 static enum power_supply_property gpio_charger_properties
[] = {
97 POWER_SUPPLY_PROP_ONLINE
,
100 static int gpio_charger_probe(struct platform_device
*pdev
)
102 struct device
*dev
= &pdev
->dev
;
103 const struct gpio_charger_platform_data
*pdata
= dev
->platform_data
;
104 struct power_supply_config psy_cfg
= {};
105 struct gpio_charger
*gpio_charger
;
106 struct power_supply_desc
*charger_desc
;
110 if (!pdata
&& !dev
->of_node
) {
111 dev_err(dev
, "No platform data\n");
115 gpio_charger
= devm_kzalloc(dev
, sizeof(*gpio_charger
), GFP_KERNEL
);
120 * This will fetch a GPIO descriptor from device tree, ACPI or
121 * boardfile descriptor tables. It's good to try this first.
123 gpio_charger
->gpiod
= devm_gpiod_get(dev
, NULL
, GPIOD_IN
);
126 * If this fails and we're not using device tree, try the
127 * legacy platform data method.
129 if (IS_ERR(gpio_charger
->gpiod
) && !dev
->of_node
) {
130 /* Non-DT: use legacy GPIO numbers */
131 if (!gpio_is_valid(pdata
->gpio
)) {
132 dev_err(dev
, "Invalid gpio pin in pdata\n");
136 if (pdata
->gpio_active_low
)
137 flags
|= GPIOF_ACTIVE_LOW
;
138 ret
= devm_gpio_request_one(dev
, pdata
->gpio
, flags
,
141 dev_err(dev
, "Failed to request gpio pin: %d\n", ret
);
144 /* Then convert this to gpiod for now */
145 gpio_charger
->gpiod
= gpio_to_desc(pdata
->gpio
);
146 } else if (IS_ERR(gpio_charger
->gpiod
)) {
147 /* Just try again if this happens */
148 if (PTR_ERR(gpio_charger
->gpiod
) == -EPROBE_DEFER
)
149 return -EPROBE_DEFER
;
150 dev_err(dev
, "error getting GPIO descriptor\n");
151 return PTR_ERR(gpio_charger
->gpiod
);
154 charger_desc
= &gpio_charger
->charger_desc
;
155 charger_desc
->properties
= gpio_charger_properties
;
156 charger_desc
->num_properties
= ARRAY_SIZE(gpio_charger_properties
);
157 charger_desc
->get_property
= gpio_charger_get_property
;
159 psy_cfg
.of_node
= dev
->of_node
;
160 psy_cfg
.drv_data
= gpio_charger
;
163 charger_desc
->name
= pdata
->name
;
164 charger_desc
->type
= pdata
->type
;
165 psy_cfg
.supplied_to
= pdata
->supplied_to
;
166 psy_cfg
.num_supplicants
= pdata
->num_supplicants
;
168 charger_desc
->name
= dev
->of_node
->name
;
169 charger_desc
->type
= gpio_charger_get_type(dev
);
172 if (!charger_desc
->name
)
173 charger_desc
->name
= pdev
->name
;
175 gpio_charger
->charger
= devm_power_supply_register(dev
, charger_desc
,
177 if (IS_ERR(gpio_charger
->charger
)) {
178 ret
= PTR_ERR(gpio_charger
->charger
);
179 dev_err(dev
, "Failed to register power supply: %d\n", ret
);
183 irq
= gpiod_to_irq(gpio_charger
->gpiod
);
185 ret
= devm_request_any_context_irq(dev
, irq
, gpio_charger_irq
,
186 IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING
,
187 dev_name(dev
), gpio_charger
->charger
);
189 dev_warn(dev
, "Failed to request irq: %d\n", ret
);
191 gpio_charger
->irq
= irq
;
194 platform_set_drvdata(pdev
, gpio_charger
);
196 device_init_wakeup(dev
, 1);
201 #ifdef CONFIG_PM_SLEEP
202 static int gpio_charger_suspend(struct device
*dev
)
204 struct gpio_charger
*gpio_charger
= dev_get_drvdata(dev
);
206 if (device_may_wakeup(dev
))
207 gpio_charger
->wakeup_enabled
=
208 !enable_irq_wake(gpio_charger
->irq
);
213 static int gpio_charger_resume(struct device
*dev
)
215 struct gpio_charger
*gpio_charger
= dev_get_drvdata(dev
);
217 if (device_may_wakeup(dev
) && gpio_charger
->wakeup_enabled
)
218 disable_irq_wake(gpio_charger
->irq
);
219 power_supply_changed(gpio_charger
->charger
);
225 static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops
,
226 gpio_charger_suspend
, gpio_charger_resume
);
228 static const struct of_device_id gpio_charger_match
[] = {
229 { .compatible
= "gpio-charger" },
232 MODULE_DEVICE_TABLE(of
, gpio_charger_match
);
234 static struct platform_driver gpio_charger_driver
= {
235 .probe
= gpio_charger_probe
,
237 .name
= "gpio-charger",
238 .pm
= &gpio_charger_pm_ops
,
239 .of_match_table
= gpio_charger_match
,
243 module_platform_driver(gpio_charger_driver
);
245 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
246 MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");
247 MODULE_LICENSE("GPL");
248 MODULE_ALIAS("platform:gpio-charger");