Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * AB8500 system control driver
0004  *
0005  * Copyright (C) ST-Ericsson SA 2010
0006  * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> for ST Ericsson.
0007  */
0008 
0009 #include <linux/err.h>
0010 #include <linux/init.h>
0011 #include <linux/export.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/pm.h>
0014 #include <linux/reboot.h>
0015 #include <linux/signal.h>
0016 #include <linux/power_supply.h>
0017 #include <linux/mfd/abx500.h>
0018 #include <linux/mfd/abx500/ab8500.h>
0019 #include <linux/mfd/abx500/ab8500-sysctrl.h>
0020 
0021 /* RtcCtrl bits */
0022 #define AB8500_ALARM_MIN_LOW  0x08
0023 #define AB8500_ALARM_MIN_MID 0x09
0024 #define RTC_CTRL 0x0B
0025 #define RTC_ALARM_ENABLE 0x4
0026 
0027 static struct device *sysctrl_dev;
0028 
0029 static void ab8500_power_off(void)
0030 {
0031     sigset_t old;
0032     sigset_t all;
0033     static const char * const pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"};
0034     int i;
0035     bool charger_present = false;
0036     union power_supply_propval val;
0037     struct power_supply *psy;
0038     int ret;
0039 
0040     if (sysctrl_dev == NULL) {
0041         pr_err("%s: sysctrl not initialized\n", __func__);
0042         return;
0043     }
0044 
0045     /*
0046      * If we have a charger connected and we're powering off,
0047      * reboot into charge-only mode.
0048      */
0049 
0050     for (i = 0; i < ARRAY_SIZE(pss); i++) {
0051         psy = power_supply_get_by_name(pss[i]);
0052         if (!psy)
0053             continue;
0054 
0055         ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
0056                 &val);
0057         power_supply_put(psy);
0058 
0059         if (!ret && val.intval) {
0060             charger_present = true;
0061             break;
0062         }
0063     }
0064 
0065     if (!charger_present)
0066         goto shutdown;
0067 
0068     /* Check if battery is known */
0069     psy = power_supply_get_by_name("ab8500_btemp");
0070     if (psy) {
0071         ret = power_supply_get_property(psy,
0072                 POWER_SUPPLY_PROP_TECHNOLOGY, &val);
0073         if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) {
0074             pr_info("Charger '%s' is connected with known battery",
0075                 pss[i]);
0076             pr_info(" - Rebooting.\n");
0077             machine_restart("charging");
0078         }
0079         power_supply_put(psy);
0080     }
0081 
0082 shutdown:
0083     sigfillset(&all);
0084 
0085     if (!sigprocmask(SIG_BLOCK, &all, &old)) {
0086         (void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
0087                      AB8500_STW4500CTRL1_SWOFF |
0088                      AB8500_STW4500CTRL1_SWRESET4500N);
0089         (void)sigprocmask(SIG_SETMASK, &old, NULL);
0090     }
0091 }
0092 
0093 static inline bool valid_bank(u8 bank)
0094 {
0095     return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
0096         (bank == AB8500_SYS_CTRL2_BLOCK));
0097 }
0098 
0099 int ab8500_sysctrl_read(u16 reg, u8 *value)
0100 {
0101     u8 bank;
0102 
0103     if (sysctrl_dev == NULL)
0104         return -EPROBE_DEFER;
0105 
0106     bank = (reg >> 8);
0107     if (!valid_bank(bank))
0108         return -EINVAL;
0109 
0110     return abx500_get_register_interruptible(sysctrl_dev, bank,
0111         (u8)(reg & 0xFF), value);
0112 }
0113 EXPORT_SYMBOL(ab8500_sysctrl_read);
0114 
0115 int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
0116 {
0117     u8 bank;
0118 
0119     if (sysctrl_dev == NULL)
0120         return -EPROBE_DEFER;
0121 
0122     bank = (reg >> 8);
0123     if (!valid_bank(bank)) {
0124         pr_err("invalid bank\n");
0125         return -EINVAL;
0126     }
0127 
0128     return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
0129         (u8)(reg & 0xFF), mask, value);
0130 }
0131 EXPORT_SYMBOL(ab8500_sysctrl_write);
0132 
0133 static int ab8500_sysctrl_probe(struct platform_device *pdev)
0134 {
0135     sysctrl_dev = &pdev->dev;
0136 
0137     if (!pm_power_off)
0138         pm_power_off = ab8500_power_off;
0139 
0140     return 0;
0141 }
0142 
0143 static int ab8500_sysctrl_remove(struct platform_device *pdev)
0144 {
0145     sysctrl_dev = NULL;
0146 
0147     if (pm_power_off == ab8500_power_off)
0148         pm_power_off = NULL;
0149 
0150     return 0;
0151 }
0152 
0153 static const struct of_device_id ab8500_sysctrl_match[] = {
0154     { .compatible = "stericsson,ab8500-sysctrl", },
0155     {}
0156 };
0157 
0158 static struct platform_driver ab8500_sysctrl_driver = {
0159     .driver = {
0160         .name = "ab8500-sysctrl",
0161         .of_match_table = ab8500_sysctrl_match,
0162     },
0163     .probe = ab8500_sysctrl_probe,
0164     .remove = ab8500_sysctrl_remove,
0165 };
0166 
0167 static int __init ab8500_sysctrl_init(void)
0168 {
0169     return platform_driver_register(&ab8500_sysctrl_driver);
0170 }
0171 arch_initcall(ab8500_sysctrl_init);