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