linux/drivers/soc/bcm/bcm63xx/bcm63xx-power.c
<<
>>
Prefs
   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