1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * BCM63xx Power Domain Controller Driver 4 * 5 * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com> 6 */ 7 8 #include <dt-bindings/soc/bcm6318-pm.h> 9 #include <dt-bindings/soc/bcm6328-pm.h> 10 #include <dt-bindings/soc/bcm6362-pm.h> 11 #include <dt-bindings/soc/bcm63268-pm.h> 12 #include <linux/io.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/pm_domain.h> 16 #include <linux/of.h> 17 18 struct bcm63xx_power_dev { 19 struct generic_pm_domain genpd; 20 struct bcm63xx_power *power; 21 uint32_t mask; 22 }; 23 24 struct bcm63xx_power { 25 void __iomem *base; 26 spinlock_t lock; 27 struct bcm63xx_power_dev *dev; 28 struct genpd_onecell_data genpd_data; 29 struct generic_pm_domain **genpd; 30 }; 31 32 struct bcm63xx_power_data { 33 const char * const name; 34 uint8_t bit; 35 unsigned int flags; 36 }; 37 38 static int bcm63xx_power_get_state(struct bcm63xx_power_dev *pmd, bool *is_on) 39 { 40 struct bcm63xx_power *power = pmd->power; 41 42 if (!pmd->mask) { 43 *is_on = false; 44 return -EINVAL; 45 } 46 47 *is_on = !(__raw_readl(power->base) & pmd->mask); 48 49 return 0; 50 } 51 52 static int bcm63xx_power_set_state(struct bcm63xx_power_dev *pmd, bool on) 53 { 54 struct bcm63xx_power *power = pmd->power; 55 unsigned long flags; 56 uint32_t val; 57 58 if (!pmd->mask) 59 return -EINVAL; 60 61 spin_lock_irqsave(&power->lock, flags); 62 val = __raw_readl(power->base); 63 if (on) 64 val &= ~pmd->mask; 65 else 66 val |= pmd->mask; 67 __raw_writel(val, power->base); 68 spin_unlock_irqrestore(&power->lock, flags); 69 70 return 0; 71 } 72 73 static int bcm63xx_power_on(struct generic_pm_domain *genpd) 74 { 75 struct bcm63xx_power_dev *pmd = container_of(genpd, 76 struct bcm63xx_power_dev, genpd); 77 78 return bcm63xx_power_set_state(pmd, true); 79 } 80 81 static int bcm63xx_power_off(struct generic_pm_domain *genpd) 82 { 83 struct bcm63xx_power_dev *pmd = container_of(genpd, 84 struct bcm63xx_power_dev, genpd); 85 86 return bcm63xx_power_set_state(pmd, false); 87 } 88 89 static int bcm63xx_power_probe(struct platform_device *pdev) 90 { 91 struct device *dev = &pdev->dev; 92 struct device_node *np = dev->of_node; 93 const struct bcm63xx_power_data *entry, *table; 94 struct bcm63xx_power *power; 95 unsigned int ndom; 96 uint8_t max_bit = 0; 97 int ret; 98 99 power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); 100 if (!power) 101 return -ENOMEM; 102 103 power->base = devm_platform_ioremap_resource(pdev, 0); 104 if (IS_ERR(power->base)) 105 return PTR_ERR(power->base); 106 107 table = of_device_get_match_data(dev); 108 if (!table) 109 return -EINVAL; 110 111 power->genpd_data.num_domains = 0; 112 ndom = 0; 113 for (entry = table; entry->name; entry++) { 114 max_bit = max(max_bit, entry->bit); 115 ndom++; 116 } 117 118 if (!ndom) 119 return -ENODEV; 120 121 power->genpd_data.num_domains = max_bit + 1; 122 123 power->dev = devm_kcalloc(dev, power->genpd_data.num_domains, 124 sizeof(struct bcm63xx_power_dev), 125 GFP_KERNEL); 126 if (!power->dev) 127 return -ENOMEM; 128 129 power->genpd = devm_kcalloc(dev, power->genpd_data.num_domains, 130 sizeof(struct generic_pm_domain *), 131 GFP_KERNEL); 132 if (!power->genpd) 133 return -ENOMEM; 134 135 power->genpd_data.domains = power->genpd; 136 137 ndom = 0; 138 for (entry = table; entry->name; entry++) { 139 struct bcm63xx_power_dev *pmd = &power->dev[ndom]; 140 bool is_on; 141 142 pmd->power = power; 143 pmd->mask = BIT(entry->bit); 144 pmd->genpd.name = entry->name; 145 pmd->genpd.flags = entry->flags; 146 147 ret = bcm63xx_power_get_state(pmd, &is_on); 148 if (ret) 149 dev_warn(dev, "unable to get current state for %s\n", 150 pmd->genpd.name); 151 152 pmd->genpd.power_on = bcm63xx_power_on; 153 pmd->genpd.power_off = bcm63xx_power_off; 154 155 pm_genpd_init(&pmd->genpd, NULL, !is_on); 156 power->genpd[entry->bit] = &pmd->genpd; 157 158 ndom++; 159 } 160 161 spin_lock_init(&power->lock); 162 163 ret = of_genpd_add_provider_onecell(np, &power->genpd_data); 164 if (ret) { 165 dev_err(dev, "failed to register genpd driver: %d\n", ret); 166 return ret; 167 } 168 169 dev_info(dev, "registered %u power domains\n", ndom); 170 171 return 0; 172 } 173 174 static const struct bcm63xx_power_data bcm6318_power_domains[] = { 175 { 176 .name = "pcie", 177 .bit = BCM6318_POWER_DOMAIN_PCIE, 178 }, { 179 .name = "usb", 180 .bit = BCM6318_POWER_DOMAIN_USB, 181 }, { 182 .name = "ephy0", 183 .bit = BCM6318_POWER_DOMAIN_EPHY0, 184 }, { 185 .name = "ephy1", 186 .bit = BCM6318_POWER_DOMAIN_EPHY1, 187 }, { 188 .name = "ephy2", 189 .bit = BCM6318_POWER_DOMAIN_EPHY2, 190 }, { 191 .name = "ephy3", 192 .bit = BCM6318_POWER_DOMAIN_EPHY3, 193 }, { 194 .name = "ldo2p5", 195 .bit = BCM6318_POWER_DOMAIN_LDO2P5, 196 .flags = GENPD_FLAG_ALWAYS_ON, 197 }, { 198 .name = "ldo2p9", 199 .bit = BCM6318_POWER_DOMAIN_LDO2P9, 200 .flags = GENPD_FLAG_ALWAYS_ON, 201 }, { 202 .name = "sw1p0", 203 .bit = BCM6318_POWER_DOMAIN_SW1P0, 204 .flags = GENPD_FLAG_ALWAYS_ON, 205 }, { 206 .name = "pad", 207 .bit = BCM6318_POWER_DOMAIN_PAD, 208 .flags = GENPD_FLAG_ALWAYS_ON, 209 }, { 210 /* sentinel */ 211 }, 212 }; 213 214 static const struct bcm63xx_power_data bcm6328_power_domains[] = { 215 { 216 .name = "adsl2-mips", 217 .bit = BCM6328_POWER_DOMAIN_ADSL2_MIPS, 218 }, { 219 .name = "adsl2-phy", 220 .bit = BCM6328_POWER_DOMAIN_ADSL2_PHY, 221 }, { 222 .name = "adsl2-afe", 223 .bit = BCM6328_POWER_DOMAIN_ADSL2_AFE, 224 }, { 225 .name = "sar", 226 .bit = BCM6328_POWER_DOMAIN_SAR, 227 }, { 228 .name = "pcm", 229 .bit = BCM6328_POWER_DOMAIN_PCM, 230 }, { 231 .name = "usbd", 232 .bit = BCM6328_POWER_DOMAIN_USBD, 233 }, { 234 .name = "usbh", 235 .bit = BCM6328_POWER_DOMAIN_USBH, 236 }, { 237 .name = "pcie", 238 .bit = BCM6328_POWER_DOMAIN_PCIE, 239 }, { 240 .name = "robosw", 241 .bit = BCM6328_POWER_DOMAIN_ROBOSW, 242 }, { 243 .name = "ephy", 244 .bit = BCM6328_POWER_DOMAIN_EPHY, 245 }, { 246 /* sentinel */ 247 }, 248 }; 249 250 static const struct bcm63xx_power_data bcm6362_power_domains[] = { 251 { 252 .name = "sar", 253 .bit = BCM6362_POWER_DOMAIN_SAR, 254 }, { 255 .name = "ipsec", 256 .bit = BCM6362_POWER_DOMAIN_IPSEC, 257 }, { 258 .name = "mips", 259 .bit = BCM6362_POWER_DOMAIN_MIPS, 260 .flags = GENPD_FLAG_ALWAYS_ON, 261 }, { 262 .name = "dect", 263 .bit = BCM6362_POWER_DOMAIN_DECT, 264 }, { 265 .name = "usbh", 266 .bit = BCM6362_POWER_DOMAIN_USBH, 267 }, { 268 .name = "usbd", 269 .bit = BCM6362_POWER_DOMAIN_USBD, 270 }, { 271 .name = "robosw", 272 .bit = BCM6362_POWER_DOMAIN_ROBOSW, 273 }, { 274 .name = "pcm", 275 .bit = BCM6362_POWER_DOMAIN_PCM, 276 }, { 277 .name = "periph", 278 .bit = BCM6362_POWER_DOMAIN_PERIPH, 279 .flags = GENPD_FLAG_ALWAYS_ON, 280 }, { 281 .name = "adsl-phy", 282 .bit = BCM6362_POWER_DOMAIN_ADSL_PHY, 283 }, { 284 .name = "gmii-pads", 285 .bit = BCM6362_POWER_DOMAIN_GMII_PADS, 286 }, { 287 .name = "fap", 288 .bit = BCM6362_POWER_DOMAIN_FAP, 289 }, { 290 .name = "pcie", 291 .bit = BCM6362_POWER_DOMAIN_PCIE, 292 }, { 293 .name = "wlan-pads", 294 .bit = BCM6362_POWER_DOMAIN_WLAN_PADS, 295 }, { 296 /* sentinel */ 297 }, 298 }; 299 300 static const struct bcm63xx_power_data bcm63268_power_domains[] = { 301 { 302 .name = "sar", 303 .bit = BCM63268_POWER_DOMAIN_SAR, 304 }, { 305 .name = "ipsec", 306 .bit = BCM63268_POWER_DOMAIN_IPSEC, 307 }, { 308 .name = "mips", 309 .bit = BCM63268_POWER_DOMAIN_MIPS, 310 .flags = GENPD_FLAG_ALWAYS_ON, 311 }, { 312 .name = "dect", 313 .bit = BCM63268_POWER_DOMAIN_DECT, 314 }, { 315 .name = "usbh", 316 .bit = BCM63268_POWER_DOMAIN_USBH, 317 }, { 318 .name = "usbd", 319 .bit = BCM63268_POWER_DOMAIN_USBD, 320 }, { 321 .name = "robosw", 322 .bit = BCM63268_POWER_DOMAIN_ROBOSW, 323 }, { 324 .name = "pcm", 325 .bit = BCM63268_POWER_DOMAIN_PCM, 326 }, { 327 .name = "periph", 328 .bit = BCM63268_POWER_DOMAIN_PERIPH, 329 .flags = GENPD_FLAG_ALWAYS_ON, 330 }, { 331 .name = "vdsl-phy", 332 .bit = BCM63268_POWER_DOMAIN_VDSL_PHY, 333 }, { 334 .name = "vdsl-mips", 335 .bit = BCM63268_POWER_DOMAIN_VDSL_MIPS, 336 }, { 337 .name = "fap", 338 .bit = BCM63268_POWER_DOMAIN_FAP, 339 }, { 340 .name = "pcie", 341 .bit = BCM63268_POWER_DOMAIN_PCIE, 342 }, { 343 .name = "wlan-pads", 344 .bit = BCM63268_POWER_DOMAIN_WLAN_PADS, 345 }, { 346 /* sentinel */ 347 }, 348 }; 349 350 static const struct of_device_id bcm63xx_power_of_match[] = { 351 { 352 .compatible = "brcm,bcm6318-power-controller", 353 .data = &bcm6318_power_domains, 354 }, { 355 .compatible = "brcm,bcm6328-power-controller", 356 .data = &bcm6328_power_domains, 357 }, { 358 .compatible = "brcm,bcm6362-power-controller", 359 .data = &bcm6362_power_domains, 360 }, { 361 .compatible = "brcm,bcm63268-power-controller", 362 .data = &bcm63268_power_domains, 363 }, { 364 /* sentinel */ 365 } 366 }; 367 368 static struct platform_driver bcm63xx_power_driver = { 369 .driver = { 370 .name = "bcm63xx-power-controller", 371 .of_match_table = bcm63xx_power_of_match, 372 }, 373 .probe = bcm63xx_power_probe, 374 }; 375 builtin_platform_driver(bcm63xx_power_driver); 376