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