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