1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * AB8500 system control driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) ST-Ericsson SA 2010
6*4882a593Smuzhiyun * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> for ST Ericsson.
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/err.h>
10*4882a593Smuzhiyun #include <linux/init.h>
11*4882a593Smuzhiyun #include <linux/export.h>
12*4882a593Smuzhiyun #include <linux/platform_device.h>
13*4882a593Smuzhiyun #include <linux/pm.h>
14*4882a593Smuzhiyun #include <linux/reboot.h>
15*4882a593Smuzhiyun #include <linux/signal.h>
16*4882a593Smuzhiyun #include <linux/power_supply.h>
17*4882a593Smuzhiyun #include <linux/mfd/abx500.h>
18*4882a593Smuzhiyun #include <linux/mfd/abx500/ab8500.h>
19*4882a593Smuzhiyun #include <linux/mfd/abx500/ab8500-sysctrl.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun /* RtcCtrl bits */
22*4882a593Smuzhiyun #define AB8500_ALARM_MIN_LOW 0x08
23*4882a593Smuzhiyun #define AB8500_ALARM_MIN_MID 0x09
24*4882a593Smuzhiyun #define RTC_CTRL 0x0B
25*4882a593Smuzhiyun #define RTC_ALARM_ENABLE 0x4
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static struct device *sysctrl_dev;
28*4882a593Smuzhiyun
ab8500_power_off(void)29*4882a593Smuzhiyun static void ab8500_power_off(void)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun sigset_t old;
32*4882a593Smuzhiyun sigset_t all;
33*4882a593Smuzhiyun static const char * const pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"};
34*4882a593Smuzhiyun int i;
35*4882a593Smuzhiyun bool charger_present = false;
36*4882a593Smuzhiyun union power_supply_propval val;
37*4882a593Smuzhiyun struct power_supply *psy;
38*4882a593Smuzhiyun int ret;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun if (sysctrl_dev == NULL) {
41*4882a593Smuzhiyun pr_err("%s: sysctrl not initialized\n", __func__);
42*4882a593Smuzhiyun return;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /*
46*4882a593Smuzhiyun * If we have a charger connected and we're powering off,
47*4882a593Smuzhiyun * reboot into charge-only mode.
48*4882a593Smuzhiyun */
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(pss); i++) {
51*4882a593Smuzhiyun psy = power_supply_get_by_name(pss[i]);
52*4882a593Smuzhiyun if (!psy)
53*4882a593Smuzhiyun continue;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
56*4882a593Smuzhiyun &val);
57*4882a593Smuzhiyun power_supply_put(psy);
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun if (!ret && val.intval) {
60*4882a593Smuzhiyun charger_present = true;
61*4882a593Smuzhiyun break;
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun if (!charger_present)
66*4882a593Smuzhiyun goto shutdown;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun /* Check if battery is known */
69*4882a593Smuzhiyun psy = power_supply_get_by_name("ab8500_btemp");
70*4882a593Smuzhiyun if (psy) {
71*4882a593Smuzhiyun ret = power_supply_get_property(psy,
72*4882a593Smuzhiyun POWER_SUPPLY_PROP_TECHNOLOGY, &val);
73*4882a593Smuzhiyun if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) {
74*4882a593Smuzhiyun pr_info("Charger '%s' is connected with known battery",
75*4882a593Smuzhiyun pss[i]);
76*4882a593Smuzhiyun pr_info(" - Rebooting.\n");
77*4882a593Smuzhiyun machine_restart("charging");
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun power_supply_put(psy);
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun shutdown:
83*4882a593Smuzhiyun sigfillset(&all);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun if (!sigprocmask(SIG_BLOCK, &all, &old)) {
86*4882a593Smuzhiyun (void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
87*4882a593Smuzhiyun AB8500_STW4500CTRL1_SWOFF |
88*4882a593Smuzhiyun AB8500_STW4500CTRL1_SWRESET4500N);
89*4882a593Smuzhiyun (void)sigprocmask(SIG_SETMASK, &old, NULL);
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
valid_bank(u8 bank)93*4882a593Smuzhiyun static inline bool valid_bank(u8 bank)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
96*4882a593Smuzhiyun (bank == AB8500_SYS_CTRL2_BLOCK));
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
ab8500_sysctrl_read(u16 reg,u8 * value)99*4882a593Smuzhiyun int ab8500_sysctrl_read(u16 reg, u8 *value)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun u8 bank;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun if (sysctrl_dev == NULL)
104*4882a593Smuzhiyun return -EPROBE_DEFER;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun bank = (reg >> 8);
107*4882a593Smuzhiyun if (!valid_bank(bank))
108*4882a593Smuzhiyun return -EINVAL;
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun return abx500_get_register_interruptible(sysctrl_dev, bank,
111*4882a593Smuzhiyun (u8)(reg & 0xFF), value);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun EXPORT_SYMBOL(ab8500_sysctrl_read);
114*4882a593Smuzhiyun
ab8500_sysctrl_write(u16 reg,u8 mask,u8 value)115*4882a593Smuzhiyun int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun u8 bank;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun if (sysctrl_dev == NULL)
120*4882a593Smuzhiyun return -EPROBE_DEFER;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun bank = (reg >> 8);
123*4882a593Smuzhiyun if (!valid_bank(bank)) {
124*4882a593Smuzhiyun pr_err("invalid bank\n");
125*4882a593Smuzhiyun return -EINVAL;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
129*4882a593Smuzhiyun (u8)(reg & 0xFF), mask, value);
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun EXPORT_SYMBOL(ab8500_sysctrl_write);
132*4882a593Smuzhiyun
ab8500_sysctrl_probe(struct platform_device * pdev)133*4882a593Smuzhiyun static int ab8500_sysctrl_probe(struct platform_device *pdev)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun sysctrl_dev = &pdev->dev;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun if (!pm_power_off)
138*4882a593Smuzhiyun pm_power_off = ab8500_power_off;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun return 0;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
ab8500_sysctrl_remove(struct platform_device * pdev)143*4882a593Smuzhiyun static int ab8500_sysctrl_remove(struct platform_device *pdev)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun sysctrl_dev = NULL;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (pm_power_off == ab8500_power_off)
148*4882a593Smuzhiyun pm_power_off = NULL;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun return 0;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun static const struct of_device_id ab8500_sysctrl_match[] = {
154*4882a593Smuzhiyun { .compatible = "stericsson,ab8500-sysctrl", },
155*4882a593Smuzhiyun {}
156*4882a593Smuzhiyun };
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun static struct platform_driver ab8500_sysctrl_driver = {
159*4882a593Smuzhiyun .driver = {
160*4882a593Smuzhiyun .name = "ab8500-sysctrl",
161*4882a593Smuzhiyun .of_match_table = ab8500_sysctrl_match,
162*4882a593Smuzhiyun },
163*4882a593Smuzhiyun .probe = ab8500_sysctrl_probe,
164*4882a593Smuzhiyun .remove = ab8500_sysctrl_remove,
165*4882a593Smuzhiyun };
166*4882a593Smuzhiyun
ab8500_sysctrl_init(void)167*4882a593Smuzhiyun static int __init ab8500_sysctrl_init(void)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun return platform_driver_register(&ab8500_sysctrl_driver);
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun arch_initcall(ab8500_sysctrl_init);
172