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 / collie_battery.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Battery and Power Management code for the Sharp SL-5x00
4  *
5  * Copyright (C) 2009 Thomas Kunze
6  *
7  * based on tosa_battery.c
8  */
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/power_supply.h>
12 #include <linux/delay.h>
13 #include <linux/spinlock.h>
14 #include <linux/interrupt.h>
15 #include <linux/gpio/driver.h>
16 #include <linux/gpio/machine.h>
17 #include <linux/gpio/consumer.h>
18 #include <linux/mfd/ucb1x00.h>
19
20 #include <asm/mach/sharpsl_param.h>
21 #include <asm/mach-types.h>
22 #include <mach/collie.h>
23
24 static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
25 static struct work_struct bat_work;
26 static struct ucb1x00 *ucb;
27
28 struct collie_bat {
29         int status;
30         struct power_supply *psy;
31         int full_chrg;
32
33         struct mutex work_lock; /* protects data */
34
35         bool (*is_present)(struct collie_bat *bat);
36         struct gpio_desc *gpio_full;
37         struct gpio_desc *gpio_charge_on;
38
39         int technology;
40
41         struct gpio_desc *gpio_bat;
42         int adc_bat;
43         int adc_bat_divider;
44         int bat_max;
45         int bat_min;
46
47         struct gpio_desc *gpio_temp;
48         int adc_temp;
49         int adc_temp_divider;
50 };
51
52 static struct collie_bat collie_bat_main;
53
54 static unsigned long collie_read_bat(struct collie_bat *bat)
55 {
56         unsigned long value = 0;
57
58         if (!bat->gpio_bat || bat->adc_bat < 0)
59                 return 0;
60         mutex_lock(&bat_lock);
61         gpiod_set_value(bat->gpio_bat, 1);
62         msleep(5);
63         ucb1x00_adc_enable(ucb);
64         value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
65         ucb1x00_adc_disable(ucb);
66         gpiod_set_value(bat->gpio_bat, 0);
67         mutex_unlock(&bat_lock);
68         value = value * 1000000 / bat->adc_bat_divider;
69
70         return value;
71 }
72
73 static unsigned long collie_read_temp(struct collie_bat *bat)
74 {
75         unsigned long value = 0;
76         if (!bat->gpio_temp || bat->adc_temp < 0)
77                 return 0;
78
79         mutex_lock(&bat_lock);
80         gpiod_set_value(bat->gpio_temp, 1);
81         msleep(5);
82         ucb1x00_adc_enable(ucb);
83         value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
84         ucb1x00_adc_disable(ucb);
85         gpiod_set_value(bat->gpio_temp, 0);
86         mutex_unlock(&bat_lock);
87
88         value = value * 10000 / bat->adc_temp_divider;
89
90         return value;
91 }
92
93 static int collie_bat_get_property(struct power_supply *psy,
94                             enum power_supply_property psp,
95                             union power_supply_propval *val)
96 {
97         int ret = 0;
98         struct collie_bat *bat = power_supply_get_drvdata(psy);
99
100         if (bat->is_present && !bat->is_present(bat)
101                         && psp != POWER_SUPPLY_PROP_PRESENT) {
102                 return -ENODEV;
103         }
104
105         switch (psp) {
106         case POWER_SUPPLY_PROP_STATUS:
107                 val->intval = bat->status;
108                 break;
109         case POWER_SUPPLY_PROP_TECHNOLOGY:
110                 val->intval = bat->technology;
111                 break;
112         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
113                 val->intval = collie_read_bat(bat);
114                 break;
115         case POWER_SUPPLY_PROP_VOLTAGE_MAX:
116                 if (bat->full_chrg == -1)
117                         val->intval = bat->bat_max;
118                 else
119                         val->intval = bat->full_chrg;
120                 break;
121         case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
122                 val->intval = bat->bat_max;
123                 break;
124         case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
125                 val->intval = bat->bat_min;
126                 break;
127         case POWER_SUPPLY_PROP_TEMP:
128                 val->intval = collie_read_temp(bat);
129                 break;
130         case POWER_SUPPLY_PROP_PRESENT:
131                 val->intval = bat->is_present ? bat->is_present(bat) : 1;
132                 break;
133         default:
134                 ret = -EINVAL;
135                 break;
136         }
137         return ret;
138 }
139
140 static void collie_bat_external_power_changed(struct power_supply *psy)
141 {
142         schedule_work(&bat_work);
143 }
144
145 static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
146 {
147         pr_info("collie_bat_gpio irq\n");
148         schedule_work(&bat_work);
149         return IRQ_HANDLED;
150 }
151
152 static void collie_bat_update(struct collie_bat *bat)
153 {
154         int old;
155         struct power_supply *psy = bat->psy;
156
157         mutex_lock(&bat->work_lock);
158
159         old = bat->status;
160
161         if (bat->is_present && !bat->is_present(bat)) {
162                 printk(KERN_NOTICE "%s not present\n", psy->desc->name);
163                 bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
164                 bat->full_chrg = -1;
165         } else if (power_supply_am_i_supplied(psy)) {
166                 if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
167                         gpiod_set_value(bat->gpio_charge_on, 1);
168                         mdelay(15);
169                 }
170
171                 if (gpiod_get_value(bat->gpio_full)) {
172                         if (old == POWER_SUPPLY_STATUS_CHARGING ||
173                                         bat->full_chrg == -1)
174                                 bat->full_chrg = collie_read_bat(bat);
175
176                         gpiod_set_value(bat->gpio_charge_on, 0);
177                         bat->status = POWER_SUPPLY_STATUS_FULL;
178                 } else {
179                         gpiod_set_value(bat->gpio_charge_on, 1);
180                         bat->status = POWER_SUPPLY_STATUS_CHARGING;
181                 }
182         } else {
183                 gpiod_set_value(bat->gpio_charge_on, 0);
184                 bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
185         }
186
187         if (old != bat->status)
188                 power_supply_changed(psy);
189
190         mutex_unlock(&bat->work_lock);
191 }
192
193 static void collie_bat_work(struct work_struct *work)
194 {
195         collie_bat_update(&collie_bat_main);
196 }
197
198
199 static enum power_supply_property collie_bat_main_props[] = {
200         POWER_SUPPLY_PROP_STATUS,
201         POWER_SUPPLY_PROP_TECHNOLOGY,
202         POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
203         POWER_SUPPLY_PROP_VOLTAGE_NOW,
204         POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
205         POWER_SUPPLY_PROP_VOLTAGE_MAX,
206         POWER_SUPPLY_PROP_PRESENT,
207         POWER_SUPPLY_PROP_TEMP,
208 };
209
210 static enum power_supply_property collie_bat_bu_props[] = {
211         POWER_SUPPLY_PROP_STATUS,
212         POWER_SUPPLY_PROP_TECHNOLOGY,
213         POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
214         POWER_SUPPLY_PROP_VOLTAGE_NOW,
215         POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
216         POWER_SUPPLY_PROP_VOLTAGE_MAX,
217         POWER_SUPPLY_PROP_PRESENT,
218 };
219
220 static const struct power_supply_desc collie_bat_main_desc = {
221         .name           = "main-battery",
222         .type           = POWER_SUPPLY_TYPE_BATTERY,
223         .properties     = collie_bat_main_props,
224         .num_properties = ARRAY_SIZE(collie_bat_main_props),
225         .get_property   = collie_bat_get_property,
226         .external_power_changed = collie_bat_external_power_changed,
227         .use_for_apm    = 1,
228 };
229
230 static struct collie_bat collie_bat_main = {
231         .status = POWER_SUPPLY_STATUS_DISCHARGING,
232         .full_chrg = -1,
233         .psy = NULL,
234
235         .gpio_full = NULL,
236         .gpio_charge_on = NULL,
237
238         .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
239
240         .gpio_bat = NULL,
241         .adc_bat = UCB_ADC_INP_AD1,
242         .adc_bat_divider = 155,
243         .bat_max = 4310000,
244         .bat_min = 1551 * 1000000 / 414,
245
246         .gpio_temp = NULL,
247         .adc_temp = UCB_ADC_INP_AD0,
248         .adc_temp_divider = 10000,
249 };
250
251 static const struct power_supply_desc collie_bat_bu_desc = {
252         .name           = "backup-battery",
253         .type           = POWER_SUPPLY_TYPE_BATTERY,
254         .properties     = collie_bat_bu_props,
255         .num_properties = ARRAY_SIZE(collie_bat_bu_props),
256         .get_property   = collie_bat_get_property,
257         .external_power_changed = collie_bat_external_power_changed,
258 };
259
260 static struct collie_bat collie_bat_bu = {
261         .status = POWER_SUPPLY_STATUS_UNKNOWN,
262         .full_chrg = -1,
263         .psy = NULL,
264
265         .gpio_full = NULL,
266         .gpio_charge_on = NULL,
267
268         .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
269
270         .gpio_bat = NULL,
271         .adc_bat = UCB_ADC_INP_AD1,
272         .adc_bat_divider = 155,
273         .bat_max = 3000000,
274         .bat_min = 1900000,
275
276         .gpio_temp = NULL,
277         .adc_temp = -1,
278         .adc_temp_divider = -1,
279 };
280
281 /* Obtained but unused GPIO */
282 static struct gpio_desc *collie_mbat_low;
283
284 #ifdef CONFIG_PM
285 static int wakeup_enabled;
286
287 static int collie_bat_suspend(struct ucb1x00_dev *dev)
288 {
289         /* flush all pending status updates */
290         flush_work(&bat_work);
291
292         if (device_may_wakeup(&dev->ucb->dev) &&
293             collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
294                 wakeup_enabled = !enable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full));
295         else
296                 wakeup_enabled = 0;
297
298         return 0;
299 }
300
301 static int collie_bat_resume(struct ucb1x00_dev *dev)
302 {
303         if (wakeup_enabled)
304                 disable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full));
305
306         /* things may have changed while we were away */
307         schedule_work(&bat_work);
308         return 0;
309 }
310 #else
311 #define collie_bat_suspend NULL
312 #define collie_bat_resume NULL
313 #endif
314
315 static int collie_bat_probe(struct ucb1x00_dev *dev)
316 {
317         int ret;
318         struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {};
319         struct gpio_chip *gc = &dev->ucb->gpio;
320
321         if (!machine_is_collie())
322                 return -ENODEV;
323
324         ucb = dev->ucb;
325
326         /* Obtain all the main battery GPIOs */
327         collie_bat_main.gpio_full = gpiod_get(&dev->ucb->dev,
328                                               "main battery full",
329                                               GPIOD_IN);
330         if (IS_ERR(collie_bat_main.gpio_full))
331                 return PTR_ERR(collie_bat_main.gpio_full);
332
333         collie_mbat_low = gpiod_get(&dev->ucb->dev,
334                                     "main battery low",
335                                     GPIOD_IN);
336         if (IS_ERR(collie_mbat_low)) {
337                 ret = PTR_ERR(collie_mbat_low);
338                 goto err_put_gpio_full;
339         }
340
341         collie_bat_main.gpio_charge_on = gpiod_get(&dev->ucb->dev,
342                                                    "main charge on",
343                                                    GPIOD_OUT_LOW);
344         if (IS_ERR(collie_bat_main.gpio_charge_on)) {
345                 ret = PTR_ERR(collie_bat_main.gpio_charge_on);
346                 goto err_put_mbat_low;
347         }
348
349         /* COLLIE_GPIO_MBAT_ON = GPIO 7 on the UCB (TC35143) */
350         collie_bat_main.gpio_bat = gpiochip_request_own_desc(gc,
351                                                 7,
352                                                 "main battery",
353                                                 GPIO_ACTIVE_HIGH,
354                                                 GPIOD_OUT_LOW);
355         if (IS_ERR(collie_bat_main.gpio_bat)) {
356                 ret = PTR_ERR(collie_bat_main.gpio_bat);
357                 goto err_put_gpio_charge_on;
358         }
359
360         /* COLLIE_GPIO_TMP_ON = GPIO 9 on the UCB (TC35143) */
361         collie_bat_main.gpio_temp = gpiochip_request_own_desc(gc,
362                                                 9,
363                                                 "main battery temp",
364                                                 GPIO_ACTIVE_HIGH,
365                                                 GPIOD_OUT_LOW);
366         if (IS_ERR(collie_bat_main.gpio_temp)) {
367                 ret = PTR_ERR(collie_bat_main.gpio_temp);
368                 goto err_free_gpio_bat;
369         }
370
371         /*
372          * Obtain the backup battery COLLIE_GPIO_BBAT_ON which is
373          * GPIO 8 on the UCB (TC35143)
374          */
375         collie_bat_bu.gpio_bat = gpiochip_request_own_desc(gc,
376                                                 8,
377                                                 "backup battery",
378                                                 GPIO_ACTIVE_HIGH,
379                                                 GPIOD_OUT_LOW);
380         if (IS_ERR(collie_bat_bu.gpio_bat)) {
381                 ret = PTR_ERR(collie_bat_bu.gpio_bat);
382                 goto err_free_gpio_temp;
383         }
384
385         mutex_init(&collie_bat_main.work_lock);
386
387         INIT_WORK(&bat_work, collie_bat_work);
388
389         psy_main_cfg.drv_data = &collie_bat_main;
390         collie_bat_main.psy = power_supply_register(&dev->ucb->dev,
391                                                     &collie_bat_main_desc,
392                                                     &psy_main_cfg);
393         if (IS_ERR(collie_bat_main.psy)) {
394                 ret = PTR_ERR(collie_bat_main.psy);
395                 goto err_psy_reg_main;
396         }
397
398         psy_bu_cfg.drv_data = &collie_bat_bu;
399         collie_bat_bu.psy = power_supply_register(&dev->ucb->dev,
400                                                   &collie_bat_bu_desc,
401                                                   &psy_bu_cfg);
402         if (IS_ERR(collie_bat_bu.psy)) {
403                 ret = PTR_ERR(collie_bat_bu.psy);
404                 goto err_psy_reg_bu;
405         }
406
407         ret = request_irq(gpiod_to_irq(collie_bat_main.gpio_full),
408                                 collie_bat_gpio_isr,
409                                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
410                                 "main full", &collie_bat_main);
411         if (ret)
412                 goto err_irq;
413
414         device_init_wakeup(&ucb->dev, 1);
415         schedule_work(&bat_work);
416
417         return 0;
418
419 err_irq:
420         power_supply_unregister(collie_bat_bu.psy);
421 err_psy_reg_bu:
422         power_supply_unregister(collie_bat_main.psy);
423 err_psy_reg_main:
424         /* see comment in collie_bat_remove */
425         cancel_work_sync(&bat_work);
426         gpiochip_free_own_desc(collie_bat_bu.gpio_bat);
427 err_free_gpio_temp:
428         gpiochip_free_own_desc(collie_bat_main.gpio_temp);
429 err_free_gpio_bat:
430         gpiochip_free_own_desc(collie_bat_main.gpio_bat);
431 err_put_gpio_charge_on:
432         gpiod_put(collie_bat_main.gpio_charge_on);
433 err_put_mbat_low:
434         gpiod_put(collie_mbat_low);
435 err_put_gpio_full:
436         gpiod_put(collie_bat_main.gpio_full);
437
438         return ret;
439 }
440
441 static void collie_bat_remove(struct ucb1x00_dev *dev)
442 {
443         free_irq(gpiod_to_irq(collie_bat_main.gpio_full), &collie_bat_main);
444         power_supply_unregister(collie_bat_bu.psy);
445         power_supply_unregister(collie_bat_main.psy);
446
447         /* These are obtained from the machine */
448         gpiod_put(collie_bat_main.gpio_full);
449         gpiod_put(collie_mbat_low);
450         gpiod_put(collie_bat_main.gpio_charge_on);
451         /* These are directly from the UCB so let's free them */
452         gpiochip_free_own_desc(collie_bat_main.gpio_bat);
453         gpiochip_free_own_desc(collie_bat_main.gpio_temp);
454         gpiochip_free_own_desc(collie_bat_bu.gpio_bat);
455         /*
456          * Now cancel the bat_work.  We won't get any more schedules,
457          * since all sources (isr and external_power_changed) are
458          * unregistered now.
459          */
460         cancel_work_sync(&bat_work);
461 }
462
463 static struct ucb1x00_driver collie_bat_driver = {
464         .add            = collie_bat_probe,
465         .remove         = collie_bat_remove,
466         .suspend        = collie_bat_suspend,
467         .resume         = collie_bat_resume,
468 };
469
470 static int __init collie_bat_init(void)
471 {
472         return ucb1x00_register_driver(&collie_bat_driver);
473 }
474
475 static void __exit collie_bat_exit(void)
476 {
477         ucb1x00_unregister_driver(&collie_bat_driver);
478 }
479
480 module_init(collie_bat_init);
481 module_exit(collie_bat_exit);
482
483 MODULE_LICENSE("GPL");
484 MODULE_AUTHOR("Thomas Kunze");
485 MODULE_DESCRIPTION("Collie battery driver");