Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * BCM63xx Power Domain Controller Driver
0004  *
0005  * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
0006  */
0007 
0008 #include <dt-bindings/soc/bcm6318-pm.h>
0009 #include <dt-bindings/soc/bcm6328-pm.h>
0010 #include <dt-bindings/soc/bcm6362-pm.h>
0011 #include <dt-bindings/soc/bcm63268-pm.h>
0012 #include <linux/io.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/pm_domain.h>
0016 #include <linux/of.h>
0017 #include <linux/of_device.h>
0018 
0019 struct bcm63xx_power_dev {
0020     struct generic_pm_domain genpd;
0021     struct bcm63xx_power *power;
0022     uint32_t mask;
0023 };
0024 
0025 struct bcm63xx_power {
0026     void __iomem *base;
0027     spinlock_t lock;
0028     struct bcm63xx_power_dev *dev;
0029     struct genpd_onecell_data genpd_data;
0030     struct generic_pm_domain **genpd;
0031 };
0032 
0033 struct bcm63xx_power_data {
0034     const char * const name;
0035     uint8_t bit;
0036     unsigned int flags;
0037 };
0038 
0039 static int bcm63xx_power_get_state(struct bcm63xx_power_dev *pmd, bool *is_on)
0040 {
0041     struct bcm63xx_power *power = pmd->power;
0042 
0043     if (!pmd->mask) {
0044         *is_on = false;
0045         return -EINVAL;
0046     }
0047 
0048     *is_on = !(__raw_readl(power->base) & pmd->mask);
0049 
0050     return 0;
0051 }
0052 
0053 static int bcm63xx_power_set_state(struct bcm63xx_power_dev *pmd, bool on)
0054 {
0055     struct bcm63xx_power *power = pmd->power;
0056     unsigned long flags;
0057     uint32_t val;
0058 
0059     if (!pmd->mask)
0060         return -EINVAL;
0061 
0062     spin_lock_irqsave(&power->lock, flags);
0063     val = __raw_readl(power->base);
0064     if (on)
0065         val &= ~pmd->mask;
0066     else
0067         val |= pmd->mask;
0068     __raw_writel(val, power->base);
0069     spin_unlock_irqrestore(&power->lock, flags);
0070 
0071     return 0;
0072 }
0073 
0074 static int bcm63xx_power_on(struct generic_pm_domain *genpd)
0075 {
0076     struct bcm63xx_power_dev *pmd = container_of(genpd,
0077         struct bcm63xx_power_dev, genpd);
0078 
0079     return bcm63xx_power_set_state(pmd, true);
0080 }
0081 
0082 static int bcm63xx_power_off(struct generic_pm_domain *genpd)
0083 {
0084     struct bcm63xx_power_dev *pmd = container_of(genpd,
0085         struct bcm63xx_power_dev, genpd);
0086 
0087     return bcm63xx_power_set_state(pmd, false);
0088 }
0089 
0090 static int bcm63xx_power_probe(struct platform_device *pdev)
0091 {
0092     struct device *dev = &pdev->dev;
0093     struct device_node *np = dev->of_node;
0094     const struct bcm63xx_power_data *entry, *table;
0095     struct bcm63xx_power *power;
0096     unsigned int ndom;
0097     uint8_t max_bit = 0;
0098     int ret;
0099 
0100     power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL);
0101     if (!power)
0102         return -ENOMEM;
0103 
0104     power->base = devm_platform_ioremap_resource(pdev, 0);
0105     if (IS_ERR(power->base))
0106         return PTR_ERR(power->base);
0107 
0108     table = of_device_get_match_data(dev);
0109     if (!table)
0110         return -EINVAL;
0111 
0112     power->genpd_data.num_domains = 0;
0113     ndom = 0;
0114     for (entry = table; entry->name; entry++) {
0115         max_bit = max(max_bit, entry->bit);
0116         ndom++;
0117     }
0118 
0119     if (!ndom)
0120         return -ENODEV;
0121 
0122     power->genpd_data.num_domains = max_bit + 1;
0123 
0124     power->dev = devm_kcalloc(dev, power->genpd_data.num_domains,
0125                   sizeof(struct bcm63xx_power_dev),
0126                   GFP_KERNEL);
0127     if (!power->dev)
0128         return -ENOMEM;
0129 
0130     power->genpd = devm_kcalloc(dev, power->genpd_data.num_domains,
0131                     sizeof(struct generic_pm_domain *),
0132                     GFP_KERNEL);
0133     if (!power->genpd)
0134         return -ENOMEM;
0135 
0136     power->genpd_data.domains = power->genpd;
0137 
0138     ndom = 0;
0139     for (entry = table; entry->name; entry++) {
0140         struct bcm63xx_power_dev *pmd = &power->dev[ndom];
0141         bool is_on;
0142 
0143         pmd->power = power;
0144         pmd->mask = BIT(entry->bit);
0145         pmd->genpd.name = entry->name;
0146         pmd->genpd.flags = entry->flags;
0147 
0148         ret = bcm63xx_power_get_state(pmd, &is_on);
0149         if (ret)
0150             dev_warn(dev, "unable to get current state for %s\n",
0151                  pmd->genpd.name);
0152 
0153         pmd->genpd.power_on = bcm63xx_power_on;
0154         pmd->genpd.power_off = bcm63xx_power_off;
0155 
0156         pm_genpd_init(&pmd->genpd, NULL, !is_on);
0157         power->genpd[entry->bit] = &pmd->genpd;
0158 
0159         ndom++;
0160     }
0161 
0162     spin_lock_init(&power->lock);
0163 
0164     ret = of_genpd_add_provider_onecell(np, &power->genpd_data);
0165     if (ret) {
0166         dev_err(dev, "failed to register genpd driver: %d\n", ret);
0167         return ret;
0168     }
0169 
0170     dev_info(dev, "registered %u power domains\n", ndom);
0171 
0172     return 0;
0173 }
0174 
0175 static const struct bcm63xx_power_data bcm6318_power_domains[] = {
0176     {
0177         .name = "pcie",
0178         .bit = BCM6318_POWER_DOMAIN_PCIE,
0179     }, {
0180         .name = "usb",
0181         .bit = BCM6318_POWER_DOMAIN_USB,
0182     }, {
0183         .name = "ephy0",
0184         .bit = BCM6318_POWER_DOMAIN_EPHY0,
0185     }, {
0186         .name = "ephy1",
0187         .bit = BCM6318_POWER_DOMAIN_EPHY1,
0188     }, {
0189         .name = "ephy2",
0190         .bit = BCM6318_POWER_DOMAIN_EPHY2,
0191     }, {
0192         .name = "ephy3",
0193         .bit = BCM6318_POWER_DOMAIN_EPHY3,
0194     }, {
0195         .name = "ldo2p5",
0196         .bit = BCM6318_POWER_DOMAIN_LDO2P5,
0197         .flags = GENPD_FLAG_ALWAYS_ON,
0198     }, {
0199         .name = "ldo2p9",
0200         .bit = BCM6318_POWER_DOMAIN_LDO2P9,
0201         .flags = GENPD_FLAG_ALWAYS_ON,
0202     }, {
0203         .name = "sw1p0",
0204         .bit = BCM6318_POWER_DOMAIN_SW1P0,
0205         .flags = GENPD_FLAG_ALWAYS_ON,
0206     }, {
0207         .name = "pad",
0208         .bit = BCM6318_POWER_DOMAIN_PAD,
0209         .flags = GENPD_FLAG_ALWAYS_ON,
0210     }, {
0211         /* sentinel */
0212     },
0213 };
0214 
0215 static const struct bcm63xx_power_data bcm6328_power_domains[] = {
0216     {
0217         .name = "adsl2-mips",
0218         .bit = BCM6328_POWER_DOMAIN_ADSL2_MIPS,
0219     }, {
0220         .name = "adsl2-phy",
0221         .bit = BCM6328_POWER_DOMAIN_ADSL2_PHY,
0222     }, {
0223         .name = "adsl2-afe",
0224         .bit = BCM6328_POWER_DOMAIN_ADSL2_AFE,
0225     }, {
0226         .name = "sar",
0227         .bit = BCM6328_POWER_DOMAIN_SAR,
0228     }, {
0229         .name = "pcm",
0230         .bit = BCM6328_POWER_DOMAIN_PCM,
0231     }, {
0232         .name = "usbd",
0233         .bit = BCM6328_POWER_DOMAIN_USBD,
0234     }, {
0235         .name = "usbh",
0236         .bit = BCM6328_POWER_DOMAIN_USBH,
0237     }, {
0238         .name = "pcie",
0239         .bit = BCM6328_POWER_DOMAIN_PCIE,
0240     }, {
0241         .name = "robosw",
0242         .bit = BCM6328_POWER_DOMAIN_ROBOSW,
0243     }, {
0244         .name = "ephy",
0245         .bit = BCM6328_POWER_DOMAIN_EPHY,
0246     }, {
0247         /* sentinel */
0248     },
0249 };
0250 
0251 static const struct bcm63xx_power_data bcm6362_power_domains[] = {
0252     {
0253         .name = "sar",
0254         .bit = BCM6362_POWER_DOMAIN_SAR,
0255     }, {
0256         .name = "ipsec",
0257         .bit = BCM6362_POWER_DOMAIN_IPSEC,
0258     }, {
0259         .name = "mips",
0260         .bit = BCM6362_POWER_DOMAIN_MIPS,
0261         .flags = GENPD_FLAG_ALWAYS_ON,
0262     }, {
0263         .name = "dect",
0264         .bit = BCM6362_POWER_DOMAIN_DECT,
0265     }, {
0266         .name = "usbh",
0267         .bit = BCM6362_POWER_DOMAIN_USBH,
0268     }, {
0269         .name = "usbd",
0270         .bit = BCM6362_POWER_DOMAIN_USBD,
0271     }, {
0272         .name = "robosw",
0273         .bit = BCM6362_POWER_DOMAIN_ROBOSW,
0274     }, {
0275         .name = "pcm",
0276         .bit = BCM6362_POWER_DOMAIN_PCM,
0277     }, {
0278         .name = "periph",
0279         .bit = BCM6362_POWER_DOMAIN_PERIPH,
0280         .flags = GENPD_FLAG_ALWAYS_ON,
0281     }, {
0282         .name = "adsl-phy",
0283         .bit = BCM6362_POWER_DOMAIN_ADSL_PHY,
0284     }, {
0285         .name = "gmii-pads",
0286         .bit = BCM6362_POWER_DOMAIN_GMII_PADS,
0287     }, {
0288         .name = "fap",
0289         .bit = BCM6362_POWER_DOMAIN_FAP,
0290     }, {
0291         .name = "pcie",
0292         .bit = BCM6362_POWER_DOMAIN_PCIE,
0293     }, {
0294         .name = "wlan-pads",
0295         .bit = BCM6362_POWER_DOMAIN_WLAN_PADS,
0296     }, {
0297         /* sentinel */
0298     },
0299 };
0300 
0301 static const struct bcm63xx_power_data bcm63268_power_domains[] = {
0302     {
0303         .name = "sar",
0304         .bit = BCM63268_POWER_DOMAIN_SAR,
0305     }, {
0306         .name = "ipsec",
0307         .bit = BCM63268_POWER_DOMAIN_IPSEC,
0308     }, {
0309         .name = "mips",
0310         .bit = BCM63268_POWER_DOMAIN_MIPS,
0311         .flags = GENPD_FLAG_ALWAYS_ON,
0312     }, {
0313         .name = "dect",
0314         .bit = BCM63268_POWER_DOMAIN_DECT,
0315     }, {
0316         .name = "usbh",
0317         .bit = BCM63268_POWER_DOMAIN_USBH,
0318     }, {
0319         .name = "usbd",
0320         .bit = BCM63268_POWER_DOMAIN_USBD,
0321     }, {
0322         .name = "robosw",
0323         .bit = BCM63268_POWER_DOMAIN_ROBOSW,
0324     }, {
0325         .name = "pcm",
0326         .bit = BCM63268_POWER_DOMAIN_PCM,
0327     }, {
0328         .name = "periph",
0329         .bit = BCM63268_POWER_DOMAIN_PERIPH,
0330         .flags = GENPD_FLAG_ALWAYS_ON,
0331     }, {
0332         .name = "vdsl-phy",
0333         .bit = BCM63268_POWER_DOMAIN_VDSL_PHY,
0334     }, {
0335         .name = "vdsl-mips",
0336         .bit = BCM63268_POWER_DOMAIN_VDSL_MIPS,
0337     }, {
0338         .name = "fap",
0339         .bit = BCM63268_POWER_DOMAIN_FAP,
0340     }, {
0341         .name = "pcie",
0342         .bit = BCM63268_POWER_DOMAIN_PCIE,
0343     }, {
0344         .name = "wlan-pads",
0345         .bit = BCM63268_POWER_DOMAIN_WLAN_PADS,
0346     }, {
0347         /* sentinel */
0348     },
0349 };
0350 
0351 static const struct of_device_id bcm63xx_power_of_match[] = {
0352     {
0353         .compatible = "brcm,bcm6318-power-controller",
0354         .data = &bcm6318_power_domains,
0355     }, {
0356         .compatible = "brcm,bcm6328-power-controller",
0357         .data = &bcm6328_power_domains,
0358     }, {
0359         .compatible = "brcm,bcm6362-power-controller",
0360         .data = &bcm6362_power_domains,
0361     }, {
0362         .compatible = "brcm,bcm63268-power-controller",
0363         .data = &bcm63268_power_domains,
0364     }, {
0365         /* sentinel */
0366     }
0367 };
0368 
0369 static struct platform_driver bcm63xx_power_driver = {
0370     .driver = {
0371         .name = "bcm63xx-power-controller",
0372         .of_match_table = bcm63xx_power_of_match,
0373     },
0374     .probe  = bcm63xx_power_probe,
0375 };
0376 builtin_platform_driver(bcm63xx_power_driver);