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