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 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->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 && plat->sysctrl)) 192 return -EINVAL; 193 194 if (plat->pm_power_off) 195 pm_power_off = ab8500_power_off; 196 197 pdata = plat->sysctrl; 198 199 if (pdata) { 200 int last, ret, i, j; 201 202 if (is_ab8505(ab8500)) 203 last = AB8500_SYSCLKREQ4RFCLKBUF; 204 else 205 last = AB8500_SYSCLKREQ8RFCLKBUF; 206 207 for (i = AB8500_SYSCLKREQ1RFCLKBUF; i <= last; i++) { 208 j = i - AB8500_SYSCLKREQ1RFCLKBUF; 209 ret = ab8500_sysctrl_write(i, 0xff, 210 pdata->initial_req_buf_config[j]); 211 dev_dbg(&pdev->dev, 212 "Setting SysClkReq%dRfClkBuf 0x%X\n", 213 j + 1, 214 pdata->initial_req_buf_config[j]); 215 if (ret < 0) { 216 dev_err(&pdev->dev, 217 "unable to set sysClkReq%dRfClkBuf: " 218 "%d\n", j + 1, ret); 219 } 220 } 221 } 222 223 return 0; 224 } 225 226 static int ab8500_sysctrl_remove(struct platform_device *pdev) 227 { 228 sysctrl_dev = NULL; 229 return 0; 230 } 231 232 static struct platform_driver ab8500_sysctrl_driver = { 233 .driver = { 234 .name = "ab8500-sysctrl", 235 .owner = THIS_MODULE, 236 }, 237 .probe = ab8500_sysctrl_probe, 238 .remove = ab8500_sysctrl_remove, 239 }; 240 241 static int __init ab8500_sysctrl_init(void) 242 { 243 return platform_driver_register(&ab8500_sysctrl_driver); 244 } 245 arch_initcall(ab8500_sysctrl_init); 246 247 MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com"); 248 MODULE_DESCRIPTION("AB8500 system control driver"); 249 MODULE_LICENSE("GPL v2"); 250