1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * StarFive JH71XX PMU (Power Management Unit) Controller Driver 4 * 5 * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. 6 */ 7 8 #include <linux/interrupt.h> 9 #include <linux/io.h> 10 #include <linux/iopoll.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 #include <linux/pm_domain.h> 15 #include <dt-bindings/power/starfive,jh7110-pmu.h> 16 17 /* register offset */ 18 #define JH71XX_PMU_SW_TURN_ON_POWER 0x0C 19 #define JH71XX_PMU_SW_TURN_OFF_POWER 0x10 20 #define JH71XX_PMU_SW_ENCOURAGE 0x44 21 #define JH71XX_PMU_TIMER_INT_MASK 0x48 22 #define JH71XX_PMU_CURR_POWER_MODE 0x80 23 #define JH71XX_PMU_EVENT_STATUS 0x88 24 #define JH71XX_PMU_INT_STATUS 0x8C 25 26 /* aon pmu register offset */ 27 #define JH71XX_AON_PMU_SWITCH 0x00 28 29 /* sw encourage cfg */ 30 #define JH71XX_PMU_SW_ENCOURAGE_EN_LO 0x05 31 #define JH71XX_PMU_SW_ENCOURAGE_EN_HI 0x50 32 #define JH71XX_PMU_SW_ENCOURAGE_DIS_LO 0x0A 33 #define JH71XX_PMU_SW_ENCOURAGE_DIS_HI 0xA0 34 #define JH71XX_PMU_SW_ENCOURAGE_ON 0xFF 35 36 /* pmu int status */ 37 #define JH71XX_PMU_INT_SEQ_DONE BIT(0) 38 #define JH71XX_PMU_INT_HW_REQ BIT(1) 39 #define JH71XX_PMU_INT_SW_FAIL GENMASK(3, 2) 40 #define JH71XX_PMU_INT_HW_FAIL GENMASK(5, 4) 41 #define JH71XX_PMU_INT_PCH_FAIL GENMASK(8, 6) 42 #define JH71XX_PMU_INT_ALL_MASK GENMASK(8, 0) 43 44 /* 45 * The time required for switching power status is based on the time 46 * to turn on the largest domain's power, which is at microsecond level 47 */ 48 #define JH71XX_PMU_TIMEOUT_US 100 49 50 struct jh71xx_domain_info { 51 const char * const name; 52 unsigned int flags; 53 u8 bit; 54 }; 55 56 struct jh71xx_pmu; 57 struct jh71xx_pmu_dev; 58 59 struct jh71xx_pmu_match_data { 60 const struct jh71xx_domain_info *domain_info; 61 int num_domains; 62 unsigned int pmu_status; 63 int (*pmu_parse_irq)(struct platform_device *pdev, 64 struct jh71xx_pmu *pmu); 65 int (*pmu_set_state)(struct jh71xx_pmu_dev *pmd, 66 u32 mask, bool on); 67 }; 68 69 struct jh71xx_pmu { 70 struct device *dev; 71 const struct jh71xx_pmu_match_data *match_data; 72 void __iomem *base; 73 struct generic_pm_domain **genpd; 74 struct genpd_onecell_data genpd_data; 75 int irq; 76 spinlock_t lock; /* protects pmu reg */ 77 }; 78 79 struct jh71xx_pmu_dev { 80 const struct jh71xx_domain_info *domain_info; 81 struct jh71xx_pmu *pmu; 82 struct generic_pm_domain genpd; 83 }; 84 85 static int jh71xx_pmu_get_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool *is_on) 86 { 87 struct jh71xx_pmu *pmu = pmd->pmu; 88 89 if (!mask) 90 return -EINVAL; 91 92 *is_on = readl(pmu->base + pmu->match_data->pmu_status) & mask; 93 94 return 0; 95 } 96 97 static int jh7110_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) 98 { 99 struct jh71xx_pmu *pmu = pmd->pmu; 100 unsigned long flags; 101 u32 val; 102 u32 mode; 103 u32 encourage_lo; 104 u32 encourage_hi; 105 int ret; 106 107 spin_lock_irqsave(&pmu->lock, flags); 108 109 /* 110 * The PMU accepts software encourage to switch power mode in the following 2 steps: 111 * 112 * 1.Configure the register SW_TURN_ON_POWER (offset 0x0c) by writing 1 to 113 * the bit corresponding to the power domain that will be turned on 114 * and writing 0 to the others. 115 * Likewise, configure the register SW_TURN_OFF_POWER (offset 0x10) by 116 * writing 1 to the bit corresponding to the power domain that will be 117 * turned off and writing 0 to the others. 118 */ 119 if (on) { 120 mode = JH71XX_PMU_SW_TURN_ON_POWER; 121 encourage_lo = JH71XX_PMU_SW_ENCOURAGE_EN_LO; 122 encourage_hi = JH71XX_PMU_SW_ENCOURAGE_EN_HI; 123 } else { 124 mode = JH71XX_PMU_SW_TURN_OFF_POWER; 125 encourage_lo = JH71XX_PMU_SW_ENCOURAGE_DIS_LO; 126 encourage_hi = JH71XX_PMU_SW_ENCOURAGE_DIS_HI; 127 } 128 129 writel(mask, pmu->base + mode); 130 131 /* 132 * 2.Write SW encourage command sequence to the Software Encourage Reg (offset 0x44) 133 * First write SW_MODE_ENCOURAGE_ON to JH71XX_PMU_SW_ENCOURAGE. This will reset 134 * the state machine which parses the command sequence. This register must be 135 * written every time software wants to power on/off a domain. 136 * Then write the lower bits of the command sequence, followed by the upper 137 * bits. The sequence differs between powering on & off a domain. 138 */ 139 writel(JH71XX_PMU_SW_ENCOURAGE_ON, pmu->base + JH71XX_PMU_SW_ENCOURAGE); 140 writel(encourage_lo, pmu->base + JH71XX_PMU_SW_ENCOURAGE); 141 writel(encourage_hi, pmu->base + JH71XX_PMU_SW_ENCOURAGE); 142 143 spin_unlock_irqrestore(&pmu->lock, flags); 144 145 /* Wait for the power domain bit to be enabled / disabled */ 146 if (on) { 147 ret = readl_poll_timeout_atomic(pmu->base + JH71XX_PMU_CURR_POWER_MODE, 148 val, val & mask, 149 1, JH71XX_PMU_TIMEOUT_US); 150 } else { 151 ret = readl_poll_timeout_atomic(pmu->base + JH71XX_PMU_CURR_POWER_MODE, 152 val, !(val & mask), 153 1, JH71XX_PMU_TIMEOUT_US); 154 } 155 156 if (ret) { 157 dev_err(pmu->dev, "%s: failed to power %s\n", 158 pmd->genpd.name, on ? "on" : "off"); 159 return -ETIMEDOUT; 160 } 161 162 return 0; 163 } 164 165 static int jh7110_aon_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) 166 { 167 struct jh71xx_pmu *pmu = pmd->pmu; 168 unsigned long flags; 169 u32 val; 170 171 spin_lock_irqsave(&pmu->lock, flags); 172 val = readl(pmu->base + JH71XX_AON_PMU_SWITCH); 173 174 if (on) 175 val |= mask; 176 else 177 val &= ~mask; 178 179 writel(val, pmu->base + JH71XX_AON_PMU_SWITCH); 180 spin_unlock_irqrestore(&pmu->lock, flags); 181 182 return 0; 183 } 184 185 static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) 186 { 187 struct jh71xx_pmu *pmu = pmd->pmu; 188 const struct jh71xx_pmu_match_data *match_data = pmu->match_data; 189 bool is_on; 190 int ret; 191 192 ret = jh71xx_pmu_get_state(pmd, mask, &is_on); 193 if (ret) { 194 dev_dbg(pmu->dev, "unable to get current state for %s\n", 195 pmd->genpd.name); 196 return ret; 197 } 198 199 if (is_on == on) { 200 dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n", 201 pmd->genpd.name, on ? "en" : "dis"); 202 return 0; 203 } 204 205 return match_data->pmu_set_state(pmd, mask, on); 206 } 207 208 static int jh71xx_pmu_on(struct generic_pm_domain *genpd) 209 { 210 struct jh71xx_pmu_dev *pmd = container_of(genpd, 211 struct jh71xx_pmu_dev, genpd); 212 u32 pwr_mask = BIT(pmd->domain_info->bit); 213 214 return jh71xx_pmu_set_state(pmd, pwr_mask, true); 215 } 216 217 static int jh71xx_pmu_off(struct generic_pm_domain *genpd) 218 { 219 struct jh71xx_pmu_dev *pmd = container_of(genpd, 220 struct jh71xx_pmu_dev, genpd); 221 u32 pwr_mask = BIT(pmd->domain_info->bit); 222 223 return jh71xx_pmu_set_state(pmd, pwr_mask, false); 224 } 225 226 static void jh71xx_pmu_int_enable(struct jh71xx_pmu *pmu, u32 mask, bool enable) 227 { 228 u32 val; 229 unsigned long flags; 230 231 spin_lock_irqsave(&pmu->lock, flags); 232 val = readl(pmu->base + JH71XX_PMU_TIMER_INT_MASK); 233 234 if (enable) 235 val &= ~mask; 236 else 237 val |= mask; 238 239 writel(val, pmu->base + JH71XX_PMU_TIMER_INT_MASK); 240 spin_unlock_irqrestore(&pmu->lock, flags); 241 } 242 243 static irqreturn_t jh71xx_pmu_interrupt(int irq, void *data) 244 { 245 struct jh71xx_pmu *pmu = data; 246 u32 val; 247 248 val = readl(pmu->base + JH71XX_PMU_INT_STATUS); 249 250 if (val & JH71XX_PMU_INT_SEQ_DONE) 251 dev_dbg(pmu->dev, "sequence done.\n"); 252 if (val & JH71XX_PMU_INT_HW_REQ) 253 dev_dbg(pmu->dev, "hardware encourage requestion.\n"); 254 if (val & JH71XX_PMU_INT_SW_FAIL) 255 dev_err(pmu->dev, "software encourage fail.\n"); 256 if (val & JH71XX_PMU_INT_HW_FAIL) 257 dev_err(pmu->dev, "hardware encourage fail.\n"); 258 if (val & JH71XX_PMU_INT_PCH_FAIL) 259 dev_err(pmu->dev, "p-channel fail event.\n"); 260 261 /* clear interrupts */ 262 writel(val, pmu->base + JH71XX_PMU_INT_STATUS); 263 writel(val, pmu->base + JH71XX_PMU_EVENT_STATUS); 264 265 return IRQ_HANDLED; 266 } 267 268 static int jh7110_pmu_parse_irq(struct platform_device *pdev, struct jh71xx_pmu *pmu) 269 { 270 struct device *dev = &pdev->dev; 271 int ret; 272 273 pmu->irq = platform_get_irq(pdev, 0); 274 if (pmu->irq < 0) 275 return pmu->irq; 276 277 ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt, 278 0, pdev->name, pmu); 279 if (ret) 280 dev_err(dev, "failed to request irq\n"); 281 282 jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true); 283 284 return 0; 285 } 286 287 static int jh71xx_pmu_init_domain(struct jh71xx_pmu *pmu, int index) 288 { 289 struct jh71xx_pmu_dev *pmd; 290 u32 pwr_mask; 291 int ret; 292 bool is_on = false; 293 294 pmd = devm_kzalloc(pmu->dev, sizeof(*pmd), GFP_KERNEL); 295 if (!pmd) 296 return -ENOMEM; 297 298 pmd->domain_info = &pmu->match_data->domain_info[index]; 299 pmd->pmu = pmu; 300 pwr_mask = BIT(pmd->domain_info->bit); 301 302 pmd->genpd.name = pmd->domain_info->name; 303 pmd->genpd.flags = pmd->domain_info->flags; 304 305 ret = jh71xx_pmu_get_state(pmd, pwr_mask, &is_on); 306 if (ret) 307 dev_warn(pmu->dev, "unable to get current state for %s\n", 308 pmd->genpd.name); 309 310 pmd->genpd.power_on = jh71xx_pmu_on; 311 pmd->genpd.power_off = jh71xx_pmu_off; 312 pm_genpd_init(&pmd->genpd, NULL, !is_on); 313 314 pmu->genpd_data.domains[index] = &pmd->genpd; 315 316 return 0; 317 } 318 319 static int jh71xx_pmu_probe(struct platform_device *pdev) 320 { 321 struct device *dev = &pdev->dev; 322 struct device_node *np = dev->of_node; 323 const struct jh71xx_pmu_match_data *match_data; 324 struct jh71xx_pmu *pmu; 325 unsigned int i; 326 int ret; 327 328 pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL); 329 if (!pmu) 330 return -ENOMEM; 331 332 pmu->base = devm_platform_ioremap_resource(pdev, 0); 333 if (IS_ERR(pmu->base)) 334 return PTR_ERR(pmu->base); 335 336 spin_lock_init(&pmu->lock); 337 338 match_data = of_device_get_match_data(dev); 339 if (!match_data) 340 return -EINVAL; 341 342 if (match_data->pmu_parse_irq) { 343 ret = match_data->pmu_parse_irq(pdev, pmu); 344 if (ret) { 345 dev_err(dev, "failed to parse irq\n"); 346 return ret; 347 } 348 } 349 350 pmu->genpd = devm_kcalloc(dev, match_data->num_domains, 351 sizeof(struct generic_pm_domain *), 352 GFP_KERNEL); 353 if (!pmu->genpd) 354 return -ENOMEM; 355 356 pmu->dev = dev; 357 pmu->match_data = match_data; 358 pmu->genpd_data.domains = pmu->genpd; 359 pmu->genpd_data.num_domains = match_data->num_domains; 360 361 for (i = 0; i < match_data->num_domains; i++) { 362 ret = jh71xx_pmu_init_domain(pmu, i); 363 if (ret) { 364 dev_err(dev, "failed to initialize power domain\n"); 365 return ret; 366 } 367 } 368 369 ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data); 370 if (ret) { 371 dev_err(dev, "failed to register genpd driver: %d\n", ret); 372 return ret; 373 } 374 375 dev_dbg(dev, "registered %u power domains\n", i); 376 377 return 0; 378 } 379 380 static const struct jh71xx_domain_info jh7110_power_domains[] = { 381 [JH7110_PD_SYSTOP] = { 382 .name = "SYSTOP", 383 .bit = 0, 384 .flags = GENPD_FLAG_ALWAYS_ON, 385 }, 386 [JH7110_PD_CPU] = { 387 .name = "CPU", 388 .bit = 1, 389 .flags = GENPD_FLAG_ALWAYS_ON, 390 }, 391 [JH7110_PD_GPUA] = { 392 .name = "GPUA", 393 .bit = 2, 394 }, 395 [JH7110_PD_VDEC] = { 396 .name = "VDEC", 397 .bit = 3, 398 }, 399 [JH7110_PD_VOUT] = { 400 .name = "VOUT", 401 .bit = 4, 402 }, 403 [JH7110_PD_ISP] = { 404 .name = "ISP", 405 .bit = 5, 406 }, 407 [JH7110_PD_VENC] = { 408 .name = "VENC", 409 .bit = 6, 410 }, 411 }; 412 413 static const struct jh71xx_pmu_match_data jh7110_pmu = { 414 .num_domains = ARRAY_SIZE(jh7110_power_domains), 415 .domain_info = jh7110_power_domains, 416 .pmu_status = JH71XX_PMU_CURR_POWER_MODE, 417 .pmu_parse_irq = jh7110_pmu_parse_irq, 418 .pmu_set_state = jh7110_pmu_set_state, 419 }; 420 421 static const struct jh71xx_domain_info jh7110_aon_power_domains[] = { 422 [JH7110_AON_PD_DPHY_TX] = { 423 .name = "DPHY-TX", 424 .bit = 30, 425 }, 426 [JH7110_AON_PD_DPHY_RX] = { 427 .name = "DPHY-RX", 428 .bit = 31, 429 }, 430 }; 431 432 static const struct jh71xx_pmu_match_data jh7110_aon_pmu = { 433 .num_domains = ARRAY_SIZE(jh7110_aon_power_domains), 434 .domain_info = jh7110_aon_power_domains, 435 .pmu_status = JH71XX_AON_PMU_SWITCH, 436 .pmu_set_state = jh7110_aon_pmu_set_state, 437 }; 438 439 static const struct of_device_id jh71xx_pmu_of_match[] = { 440 { 441 .compatible = "starfive,jh7110-pmu", 442 .data = (void *)&jh7110_pmu, 443 }, { 444 .compatible = "starfive,jh7110-aon-syscon", 445 .data = (void *)&jh7110_aon_pmu, 446 }, { 447 /* sentinel */ 448 } 449 }; 450 451 static struct platform_driver jh71xx_pmu_driver = { 452 .probe = jh71xx_pmu_probe, 453 .driver = { 454 .name = "jh71xx-pmu", 455 .of_match_table = jh71xx_pmu_of_match, 456 .suppress_bind_attrs = true, 457 }, 458 }; 459 builtin_platform_driver(jh71xx_pmu_driver); 460 461 MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>"); 462 MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>"); 463 MODULE_DESCRIPTION("StarFive JH71XX PMU Driver"); 464 MODULE_LICENSE("GPL"); 465