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 /* 89 * Use the AB WD to reset the platform. It will perform a hard 90 * reset instead of a soft reset. Write the reset reason to 91 * the AB before reset, which can be read upon restart. 92 */ 93 void ab8500_restart(char mode, const char *cmd) 94 { 95 struct ab8500_platform_data *plat; 96 struct ab8500_sysctrl_platform_data *pdata; 97 u16 reason = 0; 98 u8 val; 99 100 if (sysctrl_dev == NULL) { 101 pr_err("%s: sysctrl not initialized\n", __func__); 102 return; 103 } 104 105 plat = dev_get_platdata(sysctrl_dev->parent); 106 pdata = plat->sysctrl; 107 if (pdata && pdata->reboot_reason_code) 108 reason = pdata->reboot_reason_code(cmd); 109 else 110 pr_warn("[%s] No reboot reason set. Default reason %d\n", 111 __func__, reason); 112 113 /* 114 * Disable RTC alarm, just a precaution so that no alarm 115 * is running when WD reset is executed. 116 */ 117 abx500_get_register_interruptible(sysctrl_dev, AB8500_RTC, 118 RTC_CTRL , &val); 119 abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, 120 RTC_CTRL , (val & ~RTC_ALARM_ENABLE)); 121 122 /* 123 * Android is not using the RTC alarm registers during reboot 124 * so we borrow them for writing the reason of reset 125 */ 126 127 /* reason[8 LSB] */ 128 val = reason & 0xFF; 129 abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, 130 AB8500_ALARM_MIN_LOW , val); 131 132 /* reason[8 MSB] */ 133 val = (reason>>8) & 0xFF; 134 abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, 135 AB8500_ALARM_MIN_MID , val); 136 137 /* Setting WD timeout to 0 */ 138 ab8500_sysctrl_write(AB8500_MAINWDOGTIMER, 0xFF, 0x0); 139 140 /* Setting the parameters to AB8500 WD*/ 141 ab8500_sysctrl_write(AB8500_MAINWDOGCTRL, 0xFF, (AB8500_ENABLE_WD | 142 AB8500_WD_RESTART_ON_EXPIRE | AB8500_KICK_WD)); 143 } 144 145 static inline bool valid_bank(u8 bank) 146 { 147 return ((bank == AB8500_SYS_CTRL1_BLOCK) || 148 (bank == AB8500_SYS_CTRL2_BLOCK)); 149 } 150 151 int ab8500_sysctrl_read(u16 reg, u8 *value) 152 { 153 u8 bank; 154 155 if (sysctrl_dev == NULL) 156 return -EINVAL; 157 158 bank = (reg >> 8); 159 if (!valid_bank(bank)) 160 return -EINVAL; 161 162 return abx500_get_register_interruptible(sysctrl_dev, bank, 163 (u8)(reg & 0xFF), value); 164 } 165 EXPORT_SYMBOL(ab8500_sysctrl_read); 166 167 int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value) 168 { 169 u8 bank; 170 171 if (sysctrl_dev == NULL) 172 return -EINVAL; 173 174 bank = (reg >> 8); 175 if (!valid_bank(bank)) 176 return -EINVAL; 177 178 return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank, 179 (u8)(reg & 0xFF), mask, value); 180 } 181 EXPORT_SYMBOL(ab8500_sysctrl_write); 182 183 static int ab8500_sysctrl_probe(struct platform_device *pdev) 184 { 185 struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); 186 struct ab8500_platform_data *plat; 187 struct ab8500_sysctrl_platform_data *pdata; 188 189 plat = dev_get_platdata(pdev->dev.parent); 190 191 if (!plat) 192 return -EINVAL; 193 194 sysctrl_dev = &pdev->dev; 195 196 if (!pm_power_off) 197 pm_power_off = ab8500_power_off; 198 199 pdata = plat->sysctrl; 200 if (pdata) { 201 int last, ret, i, j; 202 203 if (is_ab8505(ab8500)) 204 last = AB8500_SYSCLKREQ4RFCLKBUF; 205 else 206 last = AB8500_SYSCLKREQ8RFCLKBUF; 207 208 for (i = AB8500_SYSCLKREQ1RFCLKBUF; i <= last; i++) { 209 j = i - AB8500_SYSCLKREQ1RFCLKBUF; 210 ret = ab8500_sysctrl_write(i, 0xff, 211 pdata->initial_req_buf_config[j]); 212 dev_dbg(&pdev->dev, 213 "Setting SysClkReq%dRfClkBuf 0x%X\n", 214 j + 1, 215 pdata->initial_req_buf_config[j]); 216 if (ret < 0) { 217 dev_err(&pdev->dev, 218 "unable to set sysClkReq%dRfClkBuf: " 219 "%d\n", j + 1, ret); 220 } 221 } 222 } 223 224 return 0; 225 } 226 227 static int ab8500_sysctrl_remove(struct platform_device *pdev) 228 { 229 sysctrl_dev = NULL; 230 231 if (pm_power_off == ab8500_power_off) 232 pm_power_off = NULL; 233 234 return 0; 235 } 236 237 static struct platform_driver ab8500_sysctrl_driver = { 238 .driver = { 239 .name = "ab8500-sysctrl", 240 .owner = THIS_MODULE, 241 }, 242 .probe = ab8500_sysctrl_probe, 243 .remove = ab8500_sysctrl_remove, 244 }; 245 246 static int __init ab8500_sysctrl_init(void) 247 { 248 return platform_driver_register(&ab8500_sysctrl_driver); 249 } 250 arch_initcall(ab8500_sysctrl_init); 251 252 MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com"); 253 MODULE_DESCRIPTION("AB8500 system control driver"); 254 MODULE_LICENSE("GPL v2"); 255