• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (C) 2017, Topic Embedded Products
3  *  Driver for LTC3651 charger IC.
4  *
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.
9  */
10 
11 #include <linux/device.h>
12 #include <linux/gpio/consumer.h>
13 #include <linux/init.h>
14 #include <linux/interrupt.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/power_supply.h>
19 #include <linux/slab.h>
20 #include <linux/of.h>
21 
22 struct ltc3651_charger {
23 	struct power_supply *charger;
24 	struct power_supply_desc charger_desc;
25 	struct gpio_desc *acpr_gpio;
26 	struct gpio_desc *fault_gpio;
27 	struct gpio_desc *chrg_gpio;
28 };
29 
ltc3651_charger_irq(int irq,void * devid)30 static irqreturn_t ltc3651_charger_irq(int irq, void *devid)
31 {
32 	struct power_supply *charger = devid;
33 
34 	power_supply_changed(charger);
35 
36 	return IRQ_HANDLED;
37 }
38 
psy_to_ltc3651_charger(struct power_supply * psy)39 static inline struct ltc3651_charger *psy_to_ltc3651_charger(
40 	struct power_supply *psy)
41 {
42 	return power_supply_get_drvdata(psy);
43 }
44 
ltc3651_charger_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)45 static int ltc3651_charger_get_property(struct power_supply *psy,
46 		enum power_supply_property psp, union power_supply_propval *val)
47 {
48 	struct ltc3651_charger *ltc3651_charger = psy_to_ltc3651_charger(psy);
49 
50 	switch (psp) {
51 	case POWER_SUPPLY_PROP_STATUS:
52 		if (!ltc3651_charger->chrg_gpio) {
53 			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
54 			break;
55 		}
56 		if (gpiod_get_value(ltc3651_charger->chrg_gpio))
57 			val->intval = POWER_SUPPLY_STATUS_CHARGING;
58 		else
59 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
60 		break;
61 	case POWER_SUPPLY_PROP_ONLINE:
62 		val->intval = gpiod_get_value(ltc3651_charger->acpr_gpio);
63 		break;
64 	case POWER_SUPPLY_PROP_HEALTH:
65 		if (!ltc3651_charger->fault_gpio) {
66 			val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
67 			break;
68 		}
69 		if (!gpiod_get_value(ltc3651_charger->fault_gpio)) {
70 			val->intval = POWER_SUPPLY_HEALTH_GOOD;
71 			break;
72 		}
73 		/*
74 		 * If the fault pin is active, the chrg pin explains the type
75 		 * of failure.
76 		 */
77 		if (!ltc3651_charger->chrg_gpio) {
78 			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
79 			break;
80 		}
81 		val->intval = gpiod_get_value(ltc3651_charger->chrg_gpio) ?
82 				POWER_SUPPLY_HEALTH_OVERHEAT :
83 				POWER_SUPPLY_HEALTH_DEAD;
84 		break;
85 	default:
86 		return -EINVAL;
87 	}
88 
89 	return 0;
90 }
91 
92 static enum power_supply_property ltc3651_charger_properties[] = {
93 	POWER_SUPPLY_PROP_STATUS,
94 	POWER_SUPPLY_PROP_ONLINE,
95 	POWER_SUPPLY_PROP_HEALTH,
96 };
97 
ltc3651_charger_probe(struct platform_device * pdev)98 static int ltc3651_charger_probe(struct platform_device *pdev)
99 {
100 	struct power_supply_config psy_cfg = {};
101 	struct ltc3651_charger *ltc3651_charger;
102 	struct power_supply_desc *charger_desc;
103 	int ret;
104 
105 	ltc3651_charger = devm_kzalloc(&pdev->dev, sizeof(*ltc3651_charger),
106 					GFP_KERNEL);
107 	if (!ltc3651_charger)
108 		return -ENOMEM;
109 
110 	ltc3651_charger->acpr_gpio = devm_gpiod_get(&pdev->dev,
111 					"lltc,acpr", GPIOD_IN);
112 	if (IS_ERR(ltc3651_charger->acpr_gpio)) {
113 		ret = PTR_ERR(ltc3651_charger->acpr_gpio);
114 		dev_err(&pdev->dev, "Failed to acquire acpr GPIO: %d\n", ret);
115 		return ret;
116 	}
117 	ltc3651_charger->fault_gpio = devm_gpiod_get_optional(&pdev->dev,
118 					"lltc,fault", GPIOD_IN);
119 	if (IS_ERR(ltc3651_charger->fault_gpio)) {
120 		ret = PTR_ERR(ltc3651_charger->fault_gpio);
121 		dev_err(&pdev->dev, "Failed to acquire fault GPIO: %d\n", ret);
122 		return ret;
123 	}
124 	ltc3651_charger->chrg_gpio = devm_gpiod_get_optional(&pdev->dev,
125 					"lltc,chrg", GPIOD_IN);
126 	if (IS_ERR(ltc3651_charger->chrg_gpio)) {
127 		ret = PTR_ERR(ltc3651_charger->chrg_gpio);
128 		dev_err(&pdev->dev, "Failed to acquire chrg GPIO: %d\n", ret);
129 		return ret;
130 	}
131 
132 	charger_desc = &ltc3651_charger->charger_desc;
133 	charger_desc->name = pdev->dev.of_node->name;
134 	charger_desc->type = POWER_SUPPLY_TYPE_MAINS;
135 	charger_desc->properties = ltc3651_charger_properties;
136 	charger_desc->num_properties = ARRAY_SIZE(ltc3651_charger_properties);
137 	charger_desc->get_property = ltc3651_charger_get_property;
138 	psy_cfg.of_node = pdev->dev.of_node;
139 	psy_cfg.drv_data = ltc3651_charger;
140 
141 	ltc3651_charger->charger = devm_power_supply_register(&pdev->dev,
142 						      charger_desc, &psy_cfg);
143 	if (IS_ERR(ltc3651_charger->charger)) {
144 		ret = PTR_ERR(ltc3651_charger->charger);
145 		dev_err(&pdev->dev, "Failed to register power supply: %d\n",
146 			ret);
147 		return ret;
148 	}
149 
150 	/*
151 	 * Acquire IRQs for the GPIO pins if possible. If the system does not
152 	 * support IRQs on these pins, userspace will have to poll the sysfs
153 	 * files manually.
154 	 */
155 	if (ltc3651_charger->acpr_gpio) {
156 		ret = gpiod_to_irq(ltc3651_charger->acpr_gpio);
157 		if (ret >= 0)
158 			ret = devm_request_any_context_irq(&pdev->dev, ret,
159 				ltc3651_charger_irq,
160 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
161 				dev_name(&pdev->dev), ltc3651_charger->charger);
162 		if (ret < 0)
163 			dev_warn(&pdev->dev, "Failed to request acpr irq\n");
164 	}
165 	if (ltc3651_charger->fault_gpio) {
166 		ret = gpiod_to_irq(ltc3651_charger->fault_gpio);
167 		if (ret >= 0)
168 			ret = devm_request_any_context_irq(&pdev->dev, ret,
169 				ltc3651_charger_irq,
170 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
171 				dev_name(&pdev->dev), ltc3651_charger->charger);
172 		if (ret < 0)
173 			dev_warn(&pdev->dev, "Failed to request fault irq\n");
174 	}
175 	if (ltc3651_charger->chrg_gpio) {
176 		ret = gpiod_to_irq(ltc3651_charger->chrg_gpio);
177 		if (ret >= 0)
178 			ret = devm_request_any_context_irq(&pdev->dev, ret,
179 				ltc3651_charger_irq,
180 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
181 				dev_name(&pdev->dev), ltc3651_charger->charger);
182 		if (ret < 0)
183 			dev_warn(&pdev->dev, "Failed to request chrg irq\n");
184 	}
185 
186 	platform_set_drvdata(pdev, ltc3651_charger);
187 
188 	return 0;
189 }
190 
191 static const struct of_device_id ltc3651_charger_match[] = {
192 	{ .compatible = "lltc,ltc3651-charger" },
193 	{ }
194 };
195 MODULE_DEVICE_TABLE(of, ltc3651_charger_match);
196 
197 static struct platform_driver ltc3651_charger_driver = {
198 	.probe = ltc3651_charger_probe,
199 	.driver = {
200 		.name = "ltc3651-charger",
201 		.of_match_table = ltc3651_charger_match,
202 	},
203 };
204 
205 module_platform_driver(ltc3651_charger_driver);
206 
207 MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
208 MODULE_DESCRIPTION("Driver for LTC3651 charger");
209 MODULE_LICENSE("GPL");
210 MODULE_ALIAS("platform:ltc3651-charger");
211