Merge tag 'timers-core-2024-03-23' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / power / supply / tps65217_charger.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Battery charger driver for TI's tps65217
3 //
4 // Copyright (C) 2015 Collabora Ltd.
5 // Author: Enric Balletbo i Serra <enric.balletbo@collabora.com>
6
7 /*
8  * Battery charger driver for TI's tps65217
9  */
10 #include <linux/kernel.h>
11 #include <linux/kthread.h>
12 #include <linux/device.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/init.h>
16 #include <linux/interrupt.h>
17 #include <linux/slab.h>
18 #include <linux/err.h>
19 #include <linux/of.h>
20 #include <linux/power_supply.h>
21
22 #include <linux/mfd/core.h>
23 #include <linux/mfd/tps65217.h>
24
25 #define CHARGER_STATUS_PRESENT  (TPS65217_STATUS_ACPWR | TPS65217_STATUS_USBPWR)
26 #define NUM_CHARGER_IRQS        2
27 #define POLL_INTERVAL           (HZ * 2)
28
29 struct tps65217_charger {
30         struct tps65217 *tps;
31         struct device *dev;
32         struct power_supply *psy;
33
34         int     online;
35         int     prev_online;
36
37         struct task_struct      *poll_task;
38 };
39
40 static enum power_supply_property tps65217_charger_props[] = {
41         POWER_SUPPLY_PROP_ONLINE,
42 };
43
44 static int tps65217_config_charger(struct tps65217_charger *charger)
45 {
46         int ret;
47
48         /*
49          * tps65217 rev. G, p. 31 (see p. 32 for NTC schematic)
50          *
51          * The device can be configured to support a 100k NTC (B = 3960) by
52          * setting the NTC_TYPE bit in register CHGCONFIG1 to 1. However it
53          * is not recommended to do so. In sleep mode, the charger continues
54          * charging the battery, but all register values are reset to default
55          * values. Therefore, the charger would get the wrong temperature
56          * information. If 100k NTC setting is required, please contact the
57          * factory.
58          *
59          * ATTENTION, conflicting information, from p. 46
60          *
61          * NTC TYPE (for battery temperature measurement)
62          *   0 – 100k (curve 1, B = 3960)
63          *   1 – 10k  (curve 2, B = 3480) (default on reset)
64          *
65          */
66         ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
67                                   TPS65217_CHGCONFIG1_NTC_TYPE,
68                                   TPS65217_PROTECT_NONE);
69         if (ret) {
70                 dev_err(charger->dev,
71                         "failed to set 100k NTC setting: %d\n", ret);
72                 return ret;
73         }
74
75         return 0;
76 }
77
78 static int tps65217_enable_charging(struct tps65217_charger *charger)
79 {
80         int ret;
81
82         /* charger already enabled */
83         if (charger->online)
84                 return 0;
85
86         dev_dbg(charger->dev, "%s: enable charging\n", __func__);
87         ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
88                                 TPS65217_CHGCONFIG1_CHG_EN,
89                                 TPS65217_CHGCONFIG1_CHG_EN,
90                                 TPS65217_PROTECT_NONE);
91         if (ret) {
92                 dev_err(charger->dev,
93                         "%s: Error in writing CHG_EN in reg 0x%x: %d\n",
94                         __func__, TPS65217_REG_CHGCONFIG1, ret);
95                 return ret;
96         }
97
98         charger->online = 1;
99
100         return 0;
101 }
102
103 static int tps65217_charger_get_property(struct power_supply *psy,
104                                          enum power_supply_property psp,
105                                          union power_supply_propval *val)
106 {
107         struct tps65217_charger *charger = power_supply_get_drvdata(psy);
108
109         if (psp == POWER_SUPPLY_PROP_ONLINE) {
110                 val->intval = charger->online;
111                 return 0;
112         }
113         return -EINVAL;
114 }
115
116 static irqreturn_t tps65217_charger_irq(int irq, void *dev)
117 {
118         int ret, val;
119         struct tps65217_charger *charger = dev;
120
121         charger->prev_online = charger->online;
122
123         ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
124         if (ret < 0) {
125                 dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
126                         __func__, TPS65217_REG_STATUS);
127                 return IRQ_HANDLED;
128         }
129
130         dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
131
132         /* check for charger status bit */
133         if (val & CHARGER_STATUS_PRESENT) {
134                 ret = tps65217_enable_charging(charger);
135                 if (ret) {
136                         dev_err(charger->dev,
137                                 "failed to enable charger: %d\n", ret);
138                         return IRQ_HANDLED;
139                 }
140         } else {
141                 charger->online = 0;
142         }
143
144         if (charger->prev_online != charger->online)
145                 power_supply_changed(charger->psy);
146
147         ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
148         if (ret < 0) {
149                 dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
150                         __func__, TPS65217_REG_CHGCONFIG0);
151                 return IRQ_HANDLED;
152         }
153
154         if (val & TPS65217_CHGCONFIG0_ACTIVE)
155                 dev_dbg(charger->dev, "%s: charger is charging\n", __func__);
156         else
157                 dev_dbg(charger->dev,
158                         "%s: charger is NOT charging\n", __func__);
159
160         return IRQ_HANDLED;
161 }
162
163 static int tps65217_charger_poll_task(void *data)
164 {
165         set_freezable();
166
167         while (!kthread_should_stop()) {
168                 schedule_timeout_interruptible(POLL_INTERVAL);
169                 try_to_freeze();
170                 tps65217_charger_irq(-1, data);
171         }
172         return 0;
173 }
174
175 static const struct power_supply_desc tps65217_charger_desc = {
176         .name                   = "tps65217-charger",
177         .type                   = POWER_SUPPLY_TYPE_MAINS,
178         .get_property           = tps65217_charger_get_property,
179         .properties             = tps65217_charger_props,
180         .num_properties         = ARRAY_SIZE(tps65217_charger_props),
181 };
182
183 static int tps65217_charger_probe(struct platform_device *pdev)
184 {
185         struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
186         struct tps65217_charger *charger;
187         struct power_supply_config cfg = {};
188         struct task_struct *poll_task;
189         int irq[NUM_CHARGER_IRQS];
190         int ret;
191         int i;
192
193         charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
194         if (!charger)
195                 return -ENOMEM;
196
197         platform_set_drvdata(pdev, charger);
198         charger->tps = tps;
199         charger->dev = &pdev->dev;
200
201         cfg.of_node = pdev->dev.of_node;
202         cfg.drv_data = charger;
203
204         charger->psy = devm_power_supply_register(&pdev->dev,
205                                                   &tps65217_charger_desc,
206                                                   &cfg);
207         if (IS_ERR(charger->psy)) {
208                 dev_err(&pdev->dev, "failed: power supply register\n");
209                 return PTR_ERR(charger->psy);
210         }
211
212         irq[0] = platform_get_irq_byname(pdev, "USB");
213         irq[1] = platform_get_irq_byname(pdev, "AC");
214
215         ret = tps65217_config_charger(charger);
216         if (ret < 0) {
217                 dev_err(charger->dev, "charger config failed, err %d\n", ret);
218                 return ret;
219         }
220
221         /* Create a polling thread if an interrupt is invalid */
222         if (irq[0] < 0 || irq[1] < 0) {
223                 poll_task = kthread_run(tps65217_charger_poll_task,
224                                         charger, "ktps65217charger");
225                 if (IS_ERR(poll_task)) {
226                         ret = PTR_ERR(poll_task);
227                         dev_err(charger->dev,
228                                 "Unable to run kthread err %d\n", ret);
229                         return ret;
230                 }
231
232                 charger->poll_task = poll_task;
233                 return 0;
234         }
235
236         /* Create IRQ threads for charger interrupts */
237         for (i = 0; i < NUM_CHARGER_IRQS; i++) {
238                 ret = devm_request_threaded_irq(&pdev->dev, irq[i], NULL,
239                                                 tps65217_charger_irq,
240                                                 IRQF_SHARED, "tps65217-charger",
241                                                 charger);
242                 if (ret) {
243                         dev_err(charger->dev,
244                                 "Unable to register irq %d err %d\n", irq[i],
245                                 ret);
246                         return ret;
247                 }
248
249                 /* Check current state */
250                 tps65217_charger_irq(-1, charger);
251         }
252
253         return 0;
254 }
255
256 static void tps65217_charger_remove(struct platform_device *pdev)
257 {
258         struct tps65217_charger *charger = platform_get_drvdata(pdev);
259
260         if (charger->poll_task)
261                 kthread_stop(charger->poll_task);
262 }
263
264 static const struct of_device_id tps65217_charger_match_table[] = {
265         { .compatible = "ti,tps65217-charger", },
266         { /* sentinel */ }
267 };
268 MODULE_DEVICE_TABLE(of, tps65217_charger_match_table);
269
270 static struct platform_driver tps65217_charger_driver = {
271         .probe  = tps65217_charger_probe,
272         .remove_new = tps65217_charger_remove,
273         .driver = {
274                 .name   = "tps65217-charger",
275                 .of_match_table = of_match_ptr(tps65217_charger_match_table),
276         },
277
278 };
279 module_platform_driver(tps65217_charger_driver);
280
281 MODULE_LICENSE("GPL v2");
282 MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
283 MODULE_DESCRIPTION("TPS65217 battery charger driver");