Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soc: bcm: add BCM63xx power domain driver

BCM6318, BCM6328, BCM6362 and BCM63268 SoCs have a power domain controller
to enable/disable certain components in order to save power.

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Reviewed-by: Florian Fainelli <F.fainelli@gmail.com>
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

authored by

Álvaro Fernández Rojas and committed by
Florian Fainelli
6c41106f 8bf86a15

+404
+1
MAINTAINERS
··· 3491 3491 F: arch/mips/boot/dts/brcm/bcm*.dts* 3492 3492 F: arch/mips/include/asm/mach-bmips/* 3493 3493 F: arch/mips/kernel/*bmips* 3494 + F: drivers/soc/bcm/bcm63xx 3494 3495 F: drivers/irqchip/irq-bcm63* 3495 3496 F: drivers/irqchip/irq-bcm7* 3496 3497 F: drivers/irqchip/irq-brcmstb*
+10
drivers/soc/bcm/Kconfig
··· 22 22 This enables support for the RPi power domains which can be enabled 23 23 or disabled via the RPi firmware. 24 24 25 + config SOC_BCM63XX 26 + bool "Broadcom 63xx SoC drivers" 27 + depends on BMIPS_GENERIC || COMPILE_TEST 28 + help 29 + Enables drivers for the Broadcom 63xx series of chips. 30 + Drivers can be enabled individually within this menu. 31 + 32 + If unsure, say N. 33 + 25 34 config SOC_BRCMSTB 26 35 bool "Broadcom STB SoC drivers" 27 36 depends on ARM || ARM64 || BMIPS_GENERIC || COMPILE_TEST ··· 42 33 43 34 If unsure, say N. 44 35 36 + source "drivers/soc/bcm/bcm63xx/Kconfig" 45 37 source "drivers/soc/bcm/brcmstb/Kconfig" 46 38 47 39 endmenu
+1
drivers/soc/bcm/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 obj-$(CONFIG_BCM2835_POWER) += bcm2835-power.o 3 3 obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o 4 + obj-$(CONFIG_SOC_BCM63XX) += bcm63xx/ 4 5 obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/
+12
drivers/soc/bcm/bcm63xx/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + if SOC_BCM63XX 3 + 4 + config BCM63XX_POWER 5 + bool "BCM63xx power domain driver" 6 + depends on BMIPS_GENERIC || (COMPILE_TEST && OF) 7 + select PM_GENERIC_DOMAINS if PM 8 + help 9 + This enables support for the BCM63xx power domains controller on 10 + BCM6318, BCM6328, BCM6362 and BCM63268 SoCs. 11 + 12 + endif # SOC_BCM63XX
+2
drivers/soc/bcm/bcm63xx/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + obj-$(CONFIG_BCM63XX_POWER) += bcm63xx-power.o
+378
drivers/soc/bcm/bcm63xx/bcm63xx-power.c
··· 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 + 19 + struct bcm63xx_power_dev { 20 + struct generic_pm_domain genpd; 21 + struct bcm63xx_power *power; 22 + uint32_t mask; 23 + }; 24 + 25 + struct 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 + 33 + struct bcm63xx_power_data { 34 + const char * const name; 35 + uint8_t bit; 36 + unsigned int flags; 37 + }; 38 + 39 + static 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 + 53 + static 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 + 74 + static 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 + 82 + static 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 + 90 + static 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 + 177 + static 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 + 217 + static 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 + 253 + static 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 + 303 + static 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 + 353 + static 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 + 371 + static 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 + }; 378 + builtin_platform_driver(bcm63xx_power_driver);