1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. 4 * Copyright (c) 2014,2015, Linaro Ltd. 5 * 6 * SAW power controller driver 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/init.h> 11 #include <linux/io.h> 12 #include <linux/module.h> 13 #include <linux/slab.h> 14 #include <linux/of.h> 15 #include <linux/of_address.h> 16 #include <linux/of_device.h> 17 #include <linux/err.h> 18 #include <linux/platform_device.h> 19 #include <soc/qcom/spm.h> 20 21 #define SPM_CTL_INDEX 0x7f 22 #define SPM_CTL_INDEX_SHIFT 4 23 #define SPM_CTL_EN BIT(0) 24 25 enum spm_reg { 26 SPM_REG_CFG, 27 SPM_REG_SPM_CTL, 28 SPM_REG_DLY, 29 SPM_REG_PMIC_DLY, 30 SPM_REG_PMIC_DATA_0, 31 SPM_REG_PMIC_DATA_1, 32 SPM_REG_VCTL, 33 SPM_REG_SEQ_ENTRY, 34 SPM_REG_SPM_STS, 35 SPM_REG_PMIC_STS, 36 SPM_REG_AVS_CTL, 37 SPM_REG_AVS_LIMIT, 38 SPM_REG_NR, 39 }; 40 41 static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = { 42 [SPM_REG_AVS_CTL] = 0x904, 43 [SPM_REG_AVS_LIMIT] = 0x908, 44 }; 45 46 static const struct spm_reg_data spm_reg_660_gold_l2 = { 47 .reg_offset = spm_reg_offset_v4_1, 48 .avs_ctl = 0x1010031, 49 .avs_limit = 0x4580458, 50 }; 51 52 static const struct spm_reg_data spm_reg_660_silver_l2 = { 53 .reg_offset = spm_reg_offset_v4_1, 54 .avs_ctl = 0x101c031, 55 .avs_limit = 0x4580458, 56 }; 57 58 static const struct spm_reg_data spm_reg_8998_gold_l2 = { 59 .reg_offset = spm_reg_offset_v4_1, 60 .avs_ctl = 0x1010031, 61 .avs_limit = 0x4700470, 62 }; 63 64 static const struct spm_reg_data spm_reg_8998_silver_l2 = { 65 .reg_offset = spm_reg_offset_v4_1, 66 .avs_ctl = 0x1010031, 67 .avs_limit = 0x4200420, 68 }; 69 70 static const u16 spm_reg_offset_v3_0[SPM_REG_NR] = { 71 [SPM_REG_CFG] = 0x08, 72 [SPM_REG_SPM_CTL] = 0x30, 73 [SPM_REG_DLY] = 0x34, 74 [SPM_REG_SEQ_ENTRY] = 0x400, 75 }; 76 77 /* SPM register data for 8909 */ 78 static const struct spm_reg_data spm_reg_8909_cpu = { 79 .reg_offset = spm_reg_offset_v3_0, 80 .spm_cfg = 0x1, 81 .spm_dly = 0x3C102800, 82 .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, 83 0x5B, 0x60, 0x03, 0x60, 0x76, 0x76, 0x0B, 0x94, 0x5B, 0x80, 84 0x10, 0x26, 0x30, 0x0F }, 85 .start_index[PM_SLEEP_MODE_STBY] = 0, 86 .start_index[PM_SLEEP_MODE_SPC] = 5, 87 }; 88 89 /* SPM register data for 8916 */ 90 static const struct spm_reg_data spm_reg_8916_cpu = { 91 .reg_offset = spm_reg_offset_v3_0, 92 .spm_cfg = 0x1, 93 .spm_dly = 0x3C102800, 94 .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, 95 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B, 96 0x80, 0x10, 0x26, 0x30, 0x0F }, 97 .start_index[PM_SLEEP_MODE_STBY] = 0, 98 .start_index[PM_SLEEP_MODE_SPC] = 5, 99 }; 100 101 static const struct spm_reg_data spm_reg_8939_cpu = { 102 .reg_offset = spm_reg_offset_v3_0, 103 .spm_cfg = 0x1, 104 .spm_dly = 0x3C102800, 105 .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x50, 0x1B, 0x10, 0x80, 106 0x30, 0x90, 0x5B, 0x60, 0x50, 0x03, 0x60, 0x76, 0x76, 0x0B, 107 0x50, 0x1B, 0x94, 0x5B, 0x80, 0x10, 0x26, 0x30, 0x50, 0x0F }, 108 .start_index[PM_SLEEP_MODE_STBY] = 0, 109 .start_index[PM_SLEEP_MODE_SPC] = 5, 110 }; 111 112 static const u16 spm_reg_offset_v2_3[SPM_REG_NR] = { 113 [SPM_REG_CFG] = 0x08, 114 [SPM_REG_SPM_CTL] = 0x30, 115 [SPM_REG_DLY] = 0x34, 116 [SPM_REG_PMIC_DATA_0] = 0x40, 117 [SPM_REG_PMIC_DATA_1] = 0x44, 118 }; 119 120 /* SPM register data for 8976 */ 121 static const struct spm_reg_data spm_reg_8976_gold_l2 = { 122 .reg_offset = spm_reg_offset_v2_3, 123 .spm_cfg = 0x14, 124 .spm_dly = 0x3c11840a, 125 .pmic_data[0] = 0x03030080, 126 .pmic_data[1] = 0x00030000, 127 .start_index[PM_SLEEP_MODE_STBY] = 0, 128 .start_index[PM_SLEEP_MODE_SPC] = 3, 129 }; 130 131 static const struct spm_reg_data spm_reg_8976_silver_l2 = { 132 .reg_offset = spm_reg_offset_v2_3, 133 .spm_cfg = 0x14, 134 .spm_dly = 0x3c102800, 135 .pmic_data[0] = 0x03030080, 136 .pmic_data[1] = 0x00030000, 137 .start_index[PM_SLEEP_MODE_STBY] = 0, 138 .start_index[PM_SLEEP_MODE_SPC] = 2, 139 }; 140 141 static const u16 spm_reg_offset_v2_1[SPM_REG_NR] = { 142 [SPM_REG_CFG] = 0x08, 143 [SPM_REG_SPM_CTL] = 0x30, 144 [SPM_REG_DLY] = 0x34, 145 [SPM_REG_SEQ_ENTRY] = 0x80, 146 }; 147 148 /* SPM register data for 8974, 8084 */ 149 static const struct spm_reg_data spm_reg_8974_8084_cpu = { 150 .reg_offset = spm_reg_offset_v2_1, 151 .spm_cfg = 0x1, 152 .spm_dly = 0x3C102800, 153 .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03, 154 0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30, 155 0x0F }, 156 .start_index[PM_SLEEP_MODE_STBY] = 0, 157 .start_index[PM_SLEEP_MODE_SPC] = 3, 158 }; 159 160 /* SPM register data for 8226 */ 161 static const struct spm_reg_data spm_reg_8226_cpu = { 162 .reg_offset = spm_reg_offset_v2_1, 163 .spm_cfg = 0x0, 164 .spm_dly = 0x3C102800, 165 .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90, 166 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B, 167 0x80, 0x10, 0x26, 0x30, 0x0F }, 168 .start_index[PM_SLEEP_MODE_STBY] = 0, 169 .start_index[PM_SLEEP_MODE_SPC] = 5, 170 }; 171 172 static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = { 173 [SPM_REG_CFG] = 0x08, 174 [SPM_REG_SPM_CTL] = 0x20, 175 [SPM_REG_PMIC_DLY] = 0x24, 176 [SPM_REG_PMIC_DATA_0] = 0x28, 177 [SPM_REG_PMIC_DATA_1] = 0x2C, 178 [SPM_REG_SEQ_ENTRY] = 0x80, 179 }; 180 181 /* SPM register data for 8064 */ 182 static const struct spm_reg_data spm_reg_8064_cpu = { 183 .reg_offset = spm_reg_offset_v1_1, 184 .spm_cfg = 0x1F, 185 .pmic_dly = 0x02020004, 186 .pmic_data[0] = 0x0084009C, 187 .pmic_data[1] = 0x00A4001C, 188 .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01, 189 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F }, 190 .start_index[PM_SLEEP_MODE_STBY] = 0, 191 .start_index[PM_SLEEP_MODE_SPC] = 2, 192 }; 193 194 static inline void spm_register_write(struct spm_driver_data *drv, 195 enum spm_reg reg, u32 val) 196 { 197 if (drv->reg_data->reg_offset[reg]) 198 writel_relaxed(val, drv->reg_base + 199 drv->reg_data->reg_offset[reg]); 200 } 201 202 /* Ensure a guaranteed write, before return */ 203 static inline void spm_register_write_sync(struct spm_driver_data *drv, 204 enum spm_reg reg, u32 val) 205 { 206 u32 ret; 207 208 if (!drv->reg_data->reg_offset[reg]) 209 return; 210 211 do { 212 writel_relaxed(val, drv->reg_base + 213 drv->reg_data->reg_offset[reg]); 214 ret = readl_relaxed(drv->reg_base + 215 drv->reg_data->reg_offset[reg]); 216 if (ret == val) 217 break; 218 cpu_relax(); 219 } while (1); 220 } 221 222 static inline u32 spm_register_read(struct spm_driver_data *drv, 223 enum spm_reg reg) 224 { 225 return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]); 226 } 227 228 void spm_set_low_power_mode(struct spm_driver_data *drv, 229 enum pm_sleep_mode mode) 230 { 231 u32 start_index; 232 u32 ctl_val; 233 234 start_index = drv->reg_data->start_index[mode]; 235 236 ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL); 237 ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT); 238 ctl_val |= start_index << SPM_CTL_INDEX_SHIFT; 239 ctl_val |= SPM_CTL_EN; 240 spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val); 241 } 242 243 static const struct of_device_id spm_match_table[] = { 244 { .compatible = "qcom,sdm660-gold-saw2-v4.1-l2", 245 .data = &spm_reg_660_gold_l2 }, 246 { .compatible = "qcom,sdm660-silver-saw2-v4.1-l2", 247 .data = &spm_reg_660_silver_l2 }, 248 { .compatible = "qcom,msm8226-saw2-v2.1-cpu", 249 .data = &spm_reg_8226_cpu }, 250 { .compatible = "qcom,msm8909-saw2-v3.0-cpu", 251 .data = &spm_reg_8909_cpu }, 252 { .compatible = "qcom,msm8916-saw2-v3.0-cpu", 253 .data = &spm_reg_8916_cpu }, 254 { .compatible = "qcom,msm8939-saw2-v3.0-cpu", 255 .data = &spm_reg_8939_cpu }, 256 { .compatible = "qcom,msm8974-saw2-v2.1-cpu", 257 .data = &spm_reg_8974_8084_cpu }, 258 { .compatible = "qcom,msm8976-gold-saw2-v2.3-l2", 259 .data = &spm_reg_8976_gold_l2 }, 260 { .compatible = "qcom,msm8976-silver-saw2-v2.3-l2", 261 .data = &spm_reg_8976_silver_l2 }, 262 { .compatible = "qcom,msm8998-gold-saw2-v4.1-l2", 263 .data = &spm_reg_8998_gold_l2 }, 264 { .compatible = "qcom,msm8998-silver-saw2-v4.1-l2", 265 .data = &spm_reg_8998_silver_l2 }, 266 { .compatible = "qcom,apq8084-saw2-v2.1-cpu", 267 .data = &spm_reg_8974_8084_cpu }, 268 { .compatible = "qcom,apq8064-saw2-v1.1-cpu", 269 .data = &spm_reg_8064_cpu }, 270 { }, 271 }; 272 MODULE_DEVICE_TABLE(of, spm_match_table); 273 274 static int spm_dev_probe(struct platform_device *pdev) 275 { 276 const struct of_device_id *match_id; 277 struct spm_driver_data *drv; 278 struct resource *res; 279 void __iomem *addr; 280 281 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); 282 if (!drv) 283 return -ENOMEM; 284 285 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 286 drv->reg_base = devm_ioremap_resource(&pdev->dev, res); 287 if (IS_ERR(drv->reg_base)) 288 return PTR_ERR(drv->reg_base); 289 290 match_id = of_match_node(spm_match_table, pdev->dev.of_node); 291 if (!match_id) 292 return -ENODEV; 293 294 drv->reg_data = match_id->data; 295 platform_set_drvdata(pdev, drv); 296 297 /* Write the SPM sequences first.. */ 298 addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY]; 299 __iowrite32_copy(addr, drv->reg_data->seq, 300 ARRAY_SIZE(drv->reg_data->seq) / 4); 301 302 /* 303 * ..and then the control registers. 304 * On some SoC if the control registers are written first and if the 305 * CPU was held in reset, the reset signal could trigger the SPM state 306 * machine, before the sequences are completely written. 307 */ 308 spm_register_write(drv, SPM_REG_AVS_CTL, drv->reg_data->avs_ctl); 309 spm_register_write(drv, SPM_REG_AVS_LIMIT, drv->reg_data->avs_limit); 310 spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg); 311 spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly); 312 spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly); 313 spm_register_write(drv, SPM_REG_PMIC_DATA_0, 314 drv->reg_data->pmic_data[0]); 315 spm_register_write(drv, SPM_REG_PMIC_DATA_1, 316 drv->reg_data->pmic_data[1]); 317 318 /* Set up Standby as the default low power mode */ 319 if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL]) 320 spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); 321 322 return 0; 323 } 324 325 static struct platform_driver spm_driver = { 326 .probe = spm_dev_probe, 327 .driver = { 328 .name = "qcom_spm", 329 .of_match_table = spm_match_table, 330 }, 331 }; 332 333 static int __init qcom_spm_init(void) 334 { 335 return platform_driver_register(&spm_driver); 336 } 337 arch_initcall(qcom_spm_init); 338 339 MODULE_LICENSE("GPL v2"); 340