diff options
-rw-r--r-- | Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt | 3 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/power/brcm,bcm63xx-power.yaml | 44 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | arch/mips/boot/dts/brcm/bcm63268.dtsi | 6 | ||||
-rw-r--r-- | arch/mips/boot/dts/brcm/bcm6328.dtsi | 6 | ||||
-rw-r--r-- | arch/mips/boot/dts/brcm/bcm6362.dtsi | 6 | ||||
-rw-r--r-- | drivers/bus/brcmstb_gisb.c | 100 | ||||
-rw-r--r-- | drivers/soc/bcm/Kconfig | 10 | ||||
-rw-r--r-- | drivers/soc/bcm/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/bcm/bcm63xx/Kconfig | 12 | ||||
-rw-r--r-- | drivers/soc/bcm/bcm63xx/Makefile | 2 | ||||
-rw-r--r-- | drivers/soc/bcm/bcm63xx/bcm63xx-power.c | 378 | ||||
-rw-r--r-- | drivers/soc/bcm/brcmstb/biuctrl.c | 105 | ||||
-rw-r--r-- | include/dt-bindings/soc/bcm6318-pm.h | 17 | ||||
-rw-r--r-- | include/dt-bindings/soc/bcm63268-pm.h | 21 | ||||
-rw-r--r-- | include/dt-bindings/soc/bcm6328-pm.h | 17 | ||||
-rw-r--r-- | include/dt-bindings/soc/bcm6362-pm.h | 21 |
17 files changed, 736 insertions, 14 deletions
diff --git a/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt b/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt index 729def62f0c5..10f6d0a8159d 100644 --- a/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt +++ b/Documentation/devicetree/bindings/bus/brcm,gisb-arb.txt @@ -10,7 +10,8 @@ Required properties: "brcm,bcm7038-gisb-arb" for 130nm chips - reg: specifies the base physical address and size of the registers - interrupts: specifies the two interrupts (timeout and TEA) to be used from - the parent interrupt controller + the parent interrupt controller. A third optional interrupt may be specified + for breakpoints. Optional properties: diff --git a/Documentation/devicetree/bindings/power/brcm,bcm63xx-power.yaml b/Documentation/devicetree/bindings/power/brcm,bcm63xx-power.yaml new file mode 100644 index 000000000000..63b15ac6dde4 --- /dev/null +++ b/Documentation/devicetree/bindings/power/brcm,bcm63xx-power.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/power/brcm,bcm63xx-power.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: BCM63xx power domain driver + +maintainers: + - Álvaro Fernández Rojas <noltari@gmail.com> + +description: | + BCM6318, BCM6328, BCM6362 and BCM63268 SoCs have a power domain controller + to enable/disable certain components in order to save power. + +properties: + compatible: + items: + - enum: + - brcm,bcm6318-power-controller + - brcm,bcm6328-power-controller + - brcm,bcm6362-power-controller + - brcm,bcm63268-power-controller + + reg: + maxItems: 1 + + "#power-domain-cells": + const: 1 + +required: + - compatible + - reg + - "#power-domain-cells" + +additionalProperties: false + +examples: + - | + periph_pwr: power-controller@10001848 { + compatible = "brcm,bcm6328-power-controller"; + reg = <0x10001848 0x4>; + #power-domain-cells = <1>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index deaafb617361..c742864ab699 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3491,6 +3491,7 @@ F: arch/mips/bmips/* F: arch/mips/boot/dts/brcm/bcm*.dts* F: arch/mips/include/asm/mach-bmips/* F: arch/mips/kernel/*bmips* +F: drivers/soc/bcm/bcm63xx F: drivers/irqchip/irq-bcm63* F: drivers/irqchip/irq-bcm7* F: drivers/irqchip/irq-brcmstb* diff --git a/arch/mips/boot/dts/brcm/bcm63268.dtsi b/arch/mips/boot/dts/brcm/bcm63268.dtsi index beec24145af7..5acb49b61867 100644 --- a/arch/mips/boot/dts/brcm/bcm63268.dtsi +++ b/arch/mips/boot/dts/brcm/bcm63268.dtsi @@ -117,6 +117,12 @@ status = "disabled"; }; + periph_pwr: power-controller@1000184c { + compatible = "brcm,bcm6328-power-controller"; + reg = <0x1000184c 0x4>; + #power-domain-cells = <1>; + }; + ehci: usb@10002500 { compatible = "brcm,bcm63268-ehci", "generic-ehci"; reg = <0x10002500 0x100>; diff --git a/arch/mips/boot/dts/brcm/bcm6328.dtsi b/arch/mips/boot/dts/brcm/bcm6328.dtsi index af860d06def6..1f9edd710392 100644 --- a/arch/mips/boot/dts/brcm/bcm6328.dtsi +++ b/arch/mips/boot/dts/brcm/bcm6328.dtsi @@ -110,6 +110,12 @@ status = "disabled"; }; + periph_pwr: power-controller@10001848 { + compatible = "brcm,bcm6328-power-controller"; + reg = <0x10001848 0x4>; + #power-domain-cells = <1>; + }; + ehci: usb@10002500 { compatible = "brcm,bcm6328-ehci", "generic-ehci"; reg = <0x10002500 0x100>; diff --git a/arch/mips/boot/dts/brcm/bcm6362.dtsi b/arch/mips/boot/dts/brcm/bcm6362.dtsi index 8ae6981735b8..c98f9111e3c8 100644 --- a/arch/mips/boot/dts/brcm/bcm6362.dtsi +++ b/arch/mips/boot/dts/brcm/bcm6362.dtsi @@ -108,6 +108,12 @@ status = "disabled"; }; + periph_pwr: power-controller@10001848 { + compatible = "brcm,bcm6362-power-controller"; + reg = <0x10001848 0x4>; + #power-domain-cells = <1>; + }; + leds0: led-controller@10001900 { #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index ec1004c858b8..7355fa2cb439 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -30,8 +30,22 @@ #define ARB_ERR_CAP_STATUS_WRITE (1 << 1) #define ARB_ERR_CAP_STATUS_VALID (1 << 0) +#define ARB_BP_CAP_CLEAR (1 << 0) +#define ARB_BP_CAP_STATUS_PROT_SHIFT 14 +#define ARB_BP_CAP_STATUS_TYPE (1 << 13) +#define ARB_BP_CAP_STATUS_RSP_SHIFT 10 +#define ARB_BP_CAP_STATUS_MASK GENMASK(1, 0) +#define ARB_BP_CAP_STATUS_BS_SHIFT 2 +#define ARB_BP_CAP_STATUS_WRITE (1 << 1) +#define ARB_BP_CAP_STATUS_VALID (1 << 0) + enum { ARB_TIMER, + ARB_BP_CAP_CLR, + ARB_BP_CAP_HI_ADDR, + ARB_BP_CAP_ADDR, + ARB_BP_CAP_STATUS, + ARB_BP_CAP_MASTER, ARB_ERR_CAP_CLR, ARB_ERR_CAP_HI_ADDR, ARB_ERR_CAP_ADDR, @@ -41,6 +55,11 @@ enum { static const int gisb_offsets_bcm7038[] = { [ARB_TIMER] = 0x00c, + [ARB_BP_CAP_CLR] = 0x014, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x0b8, + [ARB_BP_CAP_STATUS] = 0x0c0, + [ARB_BP_CAP_MASTER] = -1, [ARB_ERR_CAP_CLR] = 0x0c4, [ARB_ERR_CAP_HI_ADDR] = -1, [ARB_ERR_CAP_ADDR] = 0x0c8, @@ -50,6 +69,11 @@ static const int gisb_offsets_bcm7038[] = { static const int gisb_offsets_bcm7278[] = { [ARB_TIMER] = 0x008, + [ARB_BP_CAP_CLR] = 0x01c, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x220, + [ARB_BP_CAP_STATUS] = 0x230, + [ARB_BP_CAP_MASTER] = 0x234, [ARB_ERR_CAP_CLR] = 0x7f8, [ARB_ERR_CAP_HI_ADDR] = -1, [ARB_ERR_CAP_ADDR] = 0x7e0, @@ -59,6 +83,11 @@ static const int gisb_offsets_bcm7278[] = { static const int gisb_offsets_bcm7400[] = { [ARB_TIMER] = 0x00c, + [ARB_BP_CAP_CLR] = 0x014, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x0b8, + [ARB_BP_CAP_STATUS] = 0x0c0, + [ARB_BP_CAP_MASTER] = 0x0c4, [ARB_ERR_CAP_CLR] = 0x0c8, [ARB_ERR_CAP_HI_ADDR] = -1, [ARB_ERR_CAP_ADDR] = 0x0cc, @@ -68,6 +97,11 @@ static const int gisb_offsets_bcm7400[] = { static const int gisb_offsets_bcm7435[] = { [ARB_TIMER] = 0x00c, + [ARB_BP_CAP_CLR] = 0x014, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x158, + [ARB_BP_CAP_STATUS] = 0x160, + [ARB_BP_CAP_MASTER] = 0x164, [ARB_ERR_CAP_CLR] = 0x168, [ARB_ERR_CAP_HI_ADDR] = -1, [ARB_ERR_CAP_ADDR] = 0x16c, @@ -77,6 +111,11 @@ static const int gisb_offsets_bcm7435[] = { static const int gisb_offsets_bcm7445[] = { [ARB_TIMER] = 0x008, + [ARB_BP_CAP_CLR] = 0x010, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x1d8, + [ARB_BP_CAP_STATUS] = 0x1e0, + [ARB_BP_CAP_MASTER] = 0x1e4, [ARB_ERR_CAP_CLR] = 0x7e4, [ARB_ERR_CAP_HI_ADDR] = 0x7e8, [ARB_ERR_CAP_ADDR] = 0x7ec, @@ -125,6 +164,16 @@ static u64 gisb_read_address(struct brcmstb_gisb_arb_device *gdev) return value; } +static u64 gisb_read_bp_address(struct brcmstb_gisb_arb_device *gdev) +{ + u64 value; + + value = gisb_read(gdev, ARB_BP_CAP_ADDR); + value |= (u64)gisb_read(gdev, ARB_BP_CAP_HI_ADDR) << 32; + + return value; +} + static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg) { int offset = gdev->gisb_offsets[reg]; @@ -210,8 +259,8 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, m_name = m_fmt; } - pr_crit("%s: %s at 0x%llx [%c %s], core: %s\n", - __func__, reason, arb_addr, + pr_crit("GISB: %s at 0x%llx [%c %s], core: %s\n", + reason, arb_addr, cap_status & ARB_ERR_CAP_STATUS_WRITE ? 'W' : 'R', cap_status & ARB_ERR_CAP_STATUS_TIMEOUT ? "timeout" : "", m_name); @@ -259,6 +308,41 @@ static irqreturn_t brcmstb_gisb_tea_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t brcmstb_gisb_bp_handler(int irq, void *dev_id) +{ + struct brcmstb_gisb_arb_device *gdev = dev_id; + const char *m_name; + u32 bp_status; + u64 arb_addr; + u32 master; + char m_fmt[11]; + + bp_status = gisb_read(gdev, ARB_BP_CAP_STATUS); + + /* Invalid captured address, bail out */ + if (!(bp_status & ARB_BP_CAP_STATUS_VALID)) + return IRQ_HANDLED; + + /* Read the address and master */ + arb_addr = gisb_read_bp_address(gdev); + master = gisb_read(gdev, ARB_BP_CAP_MASTER); + + m_name = brcmstb_gisb_master_to_str(gdev, master); + if (!m_name) { + snprintf(m_fmt, sizeof(m_fmt), "0x%08x", master); + m_name = m_fmt; + } + + pr_crit("GISB: breakpoint at 0x%llx [%c], core: %s\n", + arb_addr, bp_status & ARB_BP_CAP_STATUS_WRITE ? 'W' : 'R', + m_name); + + /* clear the GISB error */ + gisb_write(gdev, ARB_ERR_CAP_CLEAR, ARB_ERR_CAP_CLR); + + return IRQ_HANDLED; +} + /* * Dump out gisb errors on die or panic. */ @@ -317,13 +401,14 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev) struct brcmstb_gisb_arb_device *gdev; const struct of_device_id *of_id; struct resource *r; - int err, timeout_irq, tea_irq; + int err, timeout_irq, tea_irq, bp_irq; unsigned int num_masters, j = 0; int i, first, last; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); timeout_irq = platform_get_irq(pdev, 0); tea_irq = platform_get_irq(pdev, 1); + bp_irq = platform_get_irq(pdev, 2); gdev = devm_kzalloc(&pdev->dev, sizeof(*gdev), GFP_KERNEL); if (!gdev) @@ -356,6 +441,15 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev) if (err < 0) return err; + /* Interrupt is optional */ + if (bp_irq > 0) { + err = devm_request_irq(&pdev->dev, bp_irq, + brcmstb_gisb_bp_handler, 0, pdev->name, + gdev); + if (err < 0) + return err; + } + /* If we do not have a valid mask, assume all masters are enabled */ if (of_property_read_u32(dn, "brcm,gisb-arb-master-mask", &gdev->valid_mask)) diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig index 648e32693b7e..24f92a6e882a 100644 --- a/drivers/soc/bcm/Kconfig +++ b/drivers/soc/bcm/Kconfig @@ -22,6 +22,15 @@ config RASPBERRYPI_POWER This enables support for the RPi power domains which can be enabled or disabled via the RPi firmware. +config SOC_BCM63XX + bool "Broadcom 63xx SoC drivers" + depends on BMIPS_GENERIC || COMPILE_TEST + help + Enables drivers for the Broadcom 63xx series of chips. + Drivers can be enabled individually within this menu. + + If unsure, say N. + config SOC_BRCMSTB bool "Broadcom STB SoC drivers" depends on ARM || ARM64 || BMIPS_GENERIC || COMPILE_TEST @@ -33,6 +42,7 @@ config SOC_BRCMSTB If unsure, say N. +source "drivers/soc/bcm/bcm63xx/Kconfig" source "drivers/soc/bcm/brcmstb/Kconfig" endmenu diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile index d92268a829a9..7bc90e0bd773 100644 --- a/drivers/soc/bcm/Makefile +++ b/drivers/soc/bcm/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_BCM2835_POWER) += bcm2835-power.o obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o +obj-$(CONFIG_SOC_BCM63XX) += bcm63xx/ obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ diff --git a/drivers/soc/bcm/bcm63xx/Kconfig b/drivers/soc/bcm/bcm63xx/Kconfig new file mode 100644 index 000000000000..16f648a6c70a --- /dev/null +++ b/drivers/soc/bcm/bcm63xx/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +if SOC_BCM63XX + +config BCM63XX_POWER + bool "BCM63xx power domain driver" + depends on BMIPS_GENERIC || (COMPILE_TEST && OF) + select PM_GENERIC_DOMAINS if PM + help + This enables support for the BCM63xx power domains controller on + BCM6318, BCM6328, BCM6362 and BCM63268 SoCs. + +endif # SOC_BCM63XX diff --git a/drivers/soc/bcm/bcm63xx/Makefile b/drivers/soc/bcm/bcm63xx/Makefile new file mode 100644 index 000000000000..0710d5e018cc --- /dev/null +++ b/drivers/soc/bcm/bcm63xx/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_BCM63XX_POWER) += bcm63xx-power.o diff --git a/drivers/soc/bcm/bcm63xx/bcm63xx-power.c b/drivers/soc/bcm/bcm63xx/bcm63xx-power.c new file mode 100644 index 000000000000..515fe182dc34 --- /dev/null +++ b/drivers/soc/bcm/bcm63xx/bcm63xx-power.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * BCM63xx Power Domain Controller Driver + * + * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com> + */ + +#include <dt-bindings/soc/bcm6318-pm.h> +#include <dt-bindings/soc/bcm6328-pm.h> +#include <dt-bindings/soc/bcm6362-pm.h> +#include <dt-bindings/soc/bcm63268-pm.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/of.h> +#include <linux/of_device.h> + +struct bcm63xx_power_dev { + struct generic_pm_domain genpd; + struct bcm63xx_power *power; + uint32_t mask; +}; + +struct bcm63xx_power { + void __iomem *base; + spinlock_t lock; + struct bcm63xx_power_dev *dev; + struct genpd_onecell_data genpd_data; + struct generic_pm_domain **genpd; +}; + +struct bcm63xx_power_data { + const char * const name; + uint8_t bit; + unsigned int flags; +}; + +static int bcm63xx_power_get_state(struct bcm63xx_power_dev *pmd, bool *is_on) +{ + struct bcm63xx_power *power = pmd->power; + + if (!pmd->mask) { + *is_on = false; + return -EINVAL; + } + + *is_on = !(__raw_readl(power->base) & pmd->mask); + + return 0; +} + +static int bcm63xx_power_set_state(struct bcm63xx_power_dev *pmd, bool on) +{ + struct bcm63xx_power *power = pmd->power; + unsigned long flags; + uint32_t val; + + if (!pmd->mask) + return -EINVAL; + + spin_lock_irqsave(&power->lock, flags); + val = __raw_readl(power->base); + if (on) + val &= ~pmd->mask; + else + val |= pmd->mask; + __raw_writel(val, power->base); + spin_unlock_irqrestore(&power->lock, flags); + + return 0; +} + +static int bcm63xx_power_on(struct generic_pm_domain *genpd) +{ + struct bcm63xx_power_dev *pmd = container_of(genpd, + struct bcm63xx_power_dev, genpd); + + return bcm63xx_power_set_state(pmd, true); +} + +static int bcm63xx_power_off(struct generic_pm_domain *genpd) +{ + struct bcm63xx_power_dev *pmd = container_of(genpd, + struct bcm63xx_power_dev, genpd); + + return bcm63xx_power_set_state(pmd, false); +} + +static int bcm63xx_power_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct resource *res; + const struct bcm63xx_power_data *entry, *table; + struct bcm63xx_power *power; + unsigned int ndom; + uint8_t max_bit = 0; + int ret; + + power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); + if (!power) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + power->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(power->base)) + return PTR_ERR(power->base); + + table = of_device_get_match_data(dev); + if (!table) + return -EINVAL; + + power->genpd_data.num_domains = 0; + ndom = 0; + for (entry = table; entry->name; entry++) { + max_bit = max(max_bit, entry->bit); + ndom++; + } + + if (!ndom) + return -ENODEV; + + power->genpd_data.num_domains = max_bit + 1; + + power->dev = devm_kcalloc(dev, power->genpd_data.num_domains, + sizeof(struct bcm63xx_power_dev), + GFP_KERNEL); + if (!power->dev) + return -ENOMEM; + + power->genpd = devm_kcalloc(dev, power->genpd_data.num_domains, + sizeof(struct generic_pm_domain *), + GFP_KERNEL); + if (!power->genpd) + return -ENOMEM; + + power->genpd_data.domains = power->genpd; + + ndom = 0; + for (entry = table; entry->name; entry++) { + struct bcm63xx_power_dev *pmd = &power->dev[ndom]; + bool is_on; + + pmd->power = power; + pmd->mask = BIT(entry->bit); + pmd->genpd.name = entry->name; + pmd->genpd.flags = entry->flags; + + ret = bcm63xx_power_get_state(pmd, &is_on); + if (ret) + dev_warn(dev, "unable to get current state for %s\n", + pmd->genpd.name); + + pmd->genpd.power_on = bcm63xx_power_on; + pmd->genpd.power_off = bcm63xx_power_off; + + pm_genpd_init(&pmd->genpd, NULL, !is_on); + power->genpd[entry->bit] = &pmd->genpd; + + ndom++; + } + + spin_lock_init(&power->lock); + + ret = of_genpd_add_provider_onecell(np, &power->genpd_data); + if (ret) { + dev_err(dev, "failed to register genpd driver: %d\n", ret); + return ret; + } + + dev_info(dev, "registered %u power domains\n", ndom); + + return 0; +} + +static const struct bcm63xx_power_data bcm6318_power_domains[] = { + { + .name = "pcie", + .bit = BCM6318_POWER_DOMAIN_PCIE, + }, { + .name = "usb", + .bit = BCM6318_POWER_DOMAIN_USB, + }, { + .name = "ephy0", + .bit = BCM6318_POWER_DOMAIN_EPHY0, + }, { + .name = "ephy1", + .bit = BCM6318_POWER_DOMAIN_EPHY1, + }, { + .name = "ephy2", + .bit = BCM6318_POWER_DOMAIN_EPHY2, + }, { + .name = "ephy3", + .bit = BCM6318_POWER_DOMAIN_EPHY3, + }, { + .name = "ldo2p5", + .bit = BCM6318_POWER_DOMAIN_LDO2P5, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "ldo2p9", + .bit = BCM6318_POWER_DOMAIN_LDO2P9, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "sw1p0", + .bit = BCM6318_POWER_DOMAIN_SW1P0, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "pad", + .bit = BCM6318_POWER_DOMAIN_PAD, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + /* sentinel */ + }, +}; + +static const struct bcm63xx_power_data bcm6328_power_domains[] = { + { + .name = "adsl2-mips", + .bit = BCM6328_POWER_DOMAIN_ADSL2_MIPS, + }, { + .name = "adsl2-phy", + .bit = BCM6328_POWER_DOMAIN_ADSL2_PHY, + }, { + .name = "adsl2-afe", + .bit = BCM6328_POWER_DOMAIN_ADSL2_AFE, + }, { + .name = "sar", + .bit = BCM6328_POWER_DOMAIN_SAR, + }, { + .name = "pcm", + .bit = BCM6328_POWER_DOMAIN_PCM, + }, { + .name = "usbd", + .bit = BCM6328_POWER_DOMAIN_USBD, + }, { + .name = "usbh", + .bit = BCM6328_POWER_DOMAIN_USBH, + }, { + .name = "pcie", + .bit = BCM6328_POWER_DOMAIN_PCIE, + }, { + .name = "robosw", + .bit = BCM6328_POWER_DOMAIN_ROBOSW, + }, { + .name = "ephy", + .bit = BCM6328_POWER_DOMAIN_EPHY, + }, { + /* sentinel */ + }, +}; + +static const struct bcm63xx_power_data bcm6362_power_domains[] = { + { + .name = "sar", + .bit = BCM6362_POWER_DOMAIN_SAR, + }, { + .name = "ipsec", + .bit = BCM6362_POWER_DOMAIN_IPSEC, + }, { + .name = "mips", + .bit = BCM6362_POWER_DOMAIN_MIPS, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "dect", + .bit = BCM6362_POWER_DOMAIN_DECT, + }, { + .name = "usbh", + .bit = BCM6362_POWER_DOMAIN_USBH, + }, { + .name = "usbd", + .bit = BCM6362_POWER_DOMAIN_USBD, + }, { + .name = "robosw", + .bit = BCM6362_POWER_DOMAIN_ROBOSW, + }, { + .name = "pcm", + .bit = BCM6362_POWER_DOMAIN_PCM, + }, { + .name = "periph", + .bit = BCM6362_POWER_DOMAIN_PERIPH, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "adsl-phy", + .bit = BCM6362_POWER_DOMAIN_ADSL_PHY, + }, { + .name = "gmii-pads", + .bit = BCM6362_POWER_DOMAIN_GMII_PADS, + }, { + .name = "fap", + .bit = BCM6362_POWER_DOMAIN_FAP, + }, { + .name = "pcie", + .bit = BCM6362_POWER_DOMAIN_PCIE, + }, { + .name = "wlan-pads", + .bit = BCM6362_POWER_DOMAIN_WLAN_PADS, + }, { + /* sentinel */ + }, +}; + +static const struct bcm63xx_power_data bcm63268_power_domains[] = { + { + .name = "sar", + .bit = BCM63268_POWER_DOMAIN_SAR, + }, { + .name = "ipsec", + .bit = BCM63268_POWER_DOMAIN_IPSEC, + }, { + .name = "mips", + .bit = BCM63268_POWER_DOMAIN_MIPS, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "dect", + .bit = BCM63268_POWER_DOMAIN_DECT, + }, { + .name = "usbh", + .bit = BCM63268_POWER_DOMAIN_USBH, + }, { + .name = "usbd", + .bit = BCM63268_POWER_DOMAIN_USBD, + }, { + .name = "robosw", + .bit = BCM63268_POWER_DOMAIN_ROBOSW, + }, { + .name = "pcm", + .bit = BCM63268_POWER_DOMAIN_PCM, + }, { + .name = "periph", + .bit = BCM63268_POWER_DOMAIN_PERIPH, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "vdsl-phy", + .bit = BCM63268_POWER_DOMAIN_VDSL_PHY, + }, { + .name = "vdsl-mips", + .bit = BCM63268_POWER_DOMAIN_VDSL_MIPS, + }, { + .name = "fap", + .bit = BCM63268_POWER_DOMAIN_FAP, + }, { + .name = "pcie", + .bit = BCM63268_POWER_DOMAIN_PCIE, + }, { + .name = "wlan-pads", + .bit = BCM63268_POWER_DOMAIN_WLAN_PADS, + }, { + /* sentinel */ + }, +}; + +static const struct of_device_id bcm63xx_power_of_match[] = { + { + .compatible = "brcm,bcm6318-power-controller", + .data = &bcm6318_power_domains, + }, { + .compatible = "brcm,bcm6328-power-controller", + .data = &bcm6328_power_domains, + }, { + .compatible = "brcm,bcm6362-power-controller", + .data = &bcm6362_power_domains, + }, { + .compatible = "brcm,bcm63268-power-controller", + .data = &bcm63268_power_domains, + }, { + /* sentinel */ + } +}; + +static struct platform_driver bcm63xx_power_driver = { + .driver = { + .name = "bcm63xx-power-controller", + .of_match_table = bcm63xx_power_of_match, + }, + .probe = bcm63xx_power_probe, +}; +builtin_platform_driver(bcm63xx_power_driver); diff --git a/drivers/soc/bcm/brcmstb/biuctrl.c b/drivers/soc/bcm/brcmstb/biuctrl.c index 61731e01f94b..7f8dc302ae6e 100644 --- a/drivers/soc/bcm/brcmstb/biuctrl.c +++ b/drivers/soc/bcm/brcmstb/biuctrl.c @@ -13,6 +13,22 @@ #include <linux/syscore_ops.h> #include <linux/soc/brcmstb/brcmstb.h> +#define RACENPREF_MASK 0x3 +#define RACPREFINST_SHIFT 0 +#define RACENINST_SHIFT 2 +#define RACPREFDATA_SHIFT 4 +#define RACENDATA_SHIFT 6 +#define RAC_CPU_SHIFT 8 +#define RACCFG_MASK 0xff +#define DPREF_LINE_2_SHIFT 24 +#define DPREF_LINE_2_MASK 0xff + +/* Bitmask to enable instruction and data prefetching with a 256-bytes stride */ +#define RAC_DATA_INST_EN_MASK (1 << RACPREFINST_SHIFT | \ + RACENPREF_MASK << RACENINST_SHIFT | \ + 1 << RACPREFDATA_SHIFT | \ + RACENPREF_MASK << RACENDATA_SHIFT) + #define CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK 0x70000000 #define CPU_CREDIT_REG_MCPx_READ_CRED_MASK 0xf #define CPU_CREDIT_REG_MCPx_WRITE_CRED_MASK 0xf @@ -31,11 +47,21 @@ static void __iomem *cpubiuctrl_base; static bool mcp_wr_pairing_en; static const int *cpubiuctrl_regs; +enum cpubiuctrl_regs { + CPU_CREDIT_REG = 0, + CPU_MCP_FLOW_REG, + CPU_WRITEBACK_CTRL_REG, + RAC_CONFIG0_REG, + RAC_CONFIG1_REG, + NUM_CPU_BIUCTRL_REGS, +}; + static inline u32 cbc_readl(int reg) { int offset = cpubiuctrl_regs[reg]; - if (offset == -1) + if (offset == -1 || + (IS_ENABLED(CONFIG_CACHE_B15_RAC) && reg >= RAC_CONFIG0_REG)) return (u32)-1; return readl_relaxed(cpubiuctrl_base + offset); @@ -45,22 +71,19 @@ static inline void cbc_writel(u32 val, int reg) { int offset = cpubiuctrl_regs[reg]; - if (offset == -1) + if (offset == -1 || + (IS_ENABLED(CONFIG_CACHE_B15_RAC) && reg >= RAC_CONFIG0_REG)) return; writel(val, cpubiuctrl_base + offset); } -enum cpubiuctrl_regs { - CPU_CREDIT_REG = 0, - CPU_MCP_FLOW_REG, - CPU_WRITEBACK_CTRL_REG -}; - static const int b15_cpubiuctrl_regs[] = { [CPU_CREDIT_REG] = 0x184, [CPU_MCP_FLOW_REG] = -1, [CPU_WRITEBACK_CTRL_REG] = -1, + [RAC_CONFIG0_REG] = -1, + [RAC_CONFIG1_REG] = -1, }; /* Odd cases, e.g: 7260A0 */ @@ -68,22 +91,26 @@ static const int b53_cpubiuctrl_no_wb_regs[] = { [CPU_CREDIT_REG] = 0x0b0, [CPU_MCP_FLOW_REG] = 0x0b4, [CPU_WRITEBACK_CTRL_REG] = -1, + [RAC_CONFIG0_REG] = 0x78, + [RAC_CONFIG1_REG] = 0x7c, }; static const int b53_cpubiuctrl_regs[] = { [CPU_CREDIT_REG] = 0x0b0, [CPU_MCP_FLOW_REG] = 0x0b4, [CPU_WRITEBACK_CTRL_REG] = 0x22c, + [RAC_CONFIG0_REG] = 0x78, + [RAC_CONFIG1_REG] = 0x7c, }; static const int a72_cpubiuctrl_regs[] = { [CPU_CREDIT_REG] = 0x18, [CPU_MCP_FLOW_REG] = 0x1c, [CPU_WRITEBACK_CTRL_REG] = 0x20, + [RAC_CONFIG0_REG] = 0x08, + [RAC_CONFIG1_REG] = 0x0c, }; -#define NUM_CPU_BIUCTRL_REGS 3 - static int __init mcp_write_pairing_set(void) { u32 creds = 0; @@ -110,6 +137,8 @@ static int __init mcp_write_pairing_set(void) static const u32 a72_b53_mach_compat[] = { 0x7211, 0x7216, + 0x72164, + 0x72165, 0x7255, 0x7260, 0x7268, @@ -117,6 +146,61 @@ static const u32 a72_b53_mach_compat[] = { 0x7278, }; +/* The read-ahead cache present in the Brahma-B53 CPU is a special piece of + * hardware after the integrated L2 cache of the B53 CPU complex whose purpose + * is to prefetch instruction and/or data with a line size of either 64 bytes + * or 256 bytes. The rationale is that the data-bus of the CPU interface is + * optimized for 256-byte transactions, and enabling the read-ahead cache + * provides a significant performance boost (typically twice the performance + * for a memcpy benchmark application). + * + * The read-ahead cache is transparent for Virtual Address cache maintenance + * operations: IC IVAU, DC IVAC, DC CVAC, DC CVAU and DC CIVAC. So no special + * handling is needed for the DMA API above and beyond what is included in the + * arm64 implementation. + * + * In addition, since the Point of Unification is typically between L1 and L2 + * for the Brahma-B53 processor no special read-ahead cache handling is needed + * for the IC IALLU and IC IALLUIS cache maintenance operations. + * + * However, it is not possible to specify the cache level (L3) for the cache + * maintenance instructions operating by set/way to operate on the read-ahead + * cache. The read-ahead cache will maintain coherency when inner cache lines + * are cleaned by set/way, but if it is necessary to invalidate inner cache + * lines by set/way to maintain coherency with system masters operating on + * shared memory that does not have hardware support for coherency, then it + * will also be necessary to explicitly invalidate the read-ahead cache. + */ +static void __init a72_b53_rac_enable_all(struct device_node *np) +{ + unsigned int cpu; + u32 enable = 0, pref_dist, shift; + + if (IS_ENABLED(CONFIG_CACHE_B15_RAC)) + return; + + if (WARN(num_possible_cpus() > 4, "RAC only supports 4 CPUs\n")) + return; + + pref_dist = cbc_readl(RAC_CONFIG1_REG); + for_each_possible_cpu(cpu) { + shift = cpu * RAC_CPU_SHIFT + RACPREFDATA_SHIFT; + enable |= RAC_DATA_INST_EN_MASK << (cpu * RAC_CPU_SHIFT); + if (cpubiuctrl_regs == a72_cpubiuctrl_regs) { + enable &= ~(RACENPREF_MASK << shift); + enable |= 3 << shift; + pref_dist |= 1 << (cpu + DPREF_LINE_2_SHIFT); + } + } + + cbc_writel(enable, RAC_CONFIG0_REG); + cbc_writel(pref_dist, RAC_CONFIG1_REG); + + pr_info("%pOF: Broadcom %s read-ahead cache\n", + np, cpubiuctrl_regs == a72_cpubiuctrl_regs ? + "Cortex-A72" : "Brahma-B53"); +} + static void __init mcp_a72_b53_set(void) { unsigned int i; @@ -262,6 +346,7 @@ static int __init brcmstb_biuctrl_init(void) return ret; } + a72_b53_rac_enable_all(np); mcp_a72_b53_set(); #ifdef CONFIG_PM_SLEEP register_syscore_ops(&brcmstb_cpu_credit_syscore_ops); diff --git a/include/dt-bindings/soc/bcm6318-pm.h b/include/dt-bindings/soc/bcm6318-pm.h new file mode 100644 index 000000000000..05931dce8333 --- /dev/null +++ b/include/dt-bindings/soc/bcm6318-pm.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_BMIPS_BCM6318_PM_H +#define __DT_BINDINGS_BMIPS_BCM6318_PM_H + +#define BCM6318_POWER_DOMAIN_PCIE 0 +#define BCM6318_POWER_DOMAIN_USB 1 +#define BCM6318_POWER_DOMAIN_EPHY0 2 +#define BCM6318_POWER_DOMAIN_EPHY1 3 +#define BCM6318_POWER_DOMAIN_EPHY2 4 +#define BCM6318_POWER_DOMAIN_EPHY3 5 +#define BCM6318_POWER_DOMAIN_LDO2P5 6 +#define BCM6318_POWER_DOMAIN_LDO2P9 7 +#define BCM6318_POWER_DOMAIN_SW1P0 8 +#define BCM6318_POWER_DOMAIN_PAD 9 + +#endif /* __DT_BINDINGS_BMIPS_BCM6318_PM_H */ diff --git a/include/dt-bindings/soc/bcm63268-pm.h b/include/dt-bindings/soc/bcm63268-pm.h new file mode 100644 index 000000000000..84ded53a732f --- /dev/null +++ b/include/dt-bindings/soc/bcm63268-pm.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_BMIPS_BCM63268_PM_H +#define __DT_BINDINGS_BMIPS_BCM63268_PM_H + +#define BCM63268_POWER_DOMAIN_SAR 0 +#define BCM63268_POWER_DOMAIN_IPSEC 1 +#define BCM63268_POWER_DOMAIN_MIPS 2 +#define BCM63268_POWER_DOMAIN_DECT 3 +#define BCM63268_POWER_DOMAIN_USBH 4 +#define BCM63268_POWER_DOMAIN_USBD 5 +#define BCM63268_POWER_DOMAIN_ROBOSW 6 +#define BCM63268_POWER_DOMAIN_PCM 7 +#define BCM63268_POWER_DOMAIN_PERIPH 8 +#define BCM63268_POWER_DOMAIN_VDSL_PHY 9 +#define BCM63268_POWER_DOMAIN_VDSL_MIPS 10 +#define BCM63268_POWER_DOMAIN_FAP 11 +#define BCM63268_POWER_DOMAIN_PCIE 12 +#define BCM63268_POWER_DOMAIN_WLAN_PADS 13 + +#endif /* __DT_BINDINGS_BMIPS_BCM63268_PM_H */ diff --git a/include/dt-bindings/soc/bcm6328-pm.h b/include/dt-bindings/soc/bcm6328-pm.h new file mode 100644 index 000000000000..557e1a69b7f7 --- /dev/null +++ b/include/dt-bindings/soc/bcm6328-pm.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_BMIPS_BCM6328_PM_H +#define __DT_BINDINGS_BMIPS_BCM6328_PM_H + +#define BCM6328_POWER_DOMAIN_ADSL2_MIPS 0 +#define BCM6328_POWER_DOMAIN_ADSL2_PHY 1 +#define BCM6328_POWER_DOMAIN_ADSL2_AFE 2 +#define BCM6328_POWER_DOMAIN_SAR 3 +#define BCM6328_POWER_DOMAIN_PCM 4 +#define BCM6328_POWER_DOMAIN_USBD 5 +#define BCM6328_POWER_DOMAIN_USBH 6 +#define BCM6328_POWER_DOMAIN_PCIE 7 +#define BCM6328_POWER_DOMAIN_ROBOSW 8 +#define BCM6328_POWER_DOMAIN_EPHY 9 + +#endif /* __DT_BINDINGS_BMIPS_BCM6328_PM_H */ diff --git a/include/dt-bindings/soc/bcm6362-pm.h b/include/dt-bindings/soc/bcm6362-pm.h new file mode 100644 index 000000000000..d087ba63c7a1 --- /dev/null +++ b/include/dt-bindings/soc/bcm6362-pm.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_BMIPS_BCM6362_PM_H +#define __DT_BINDINGS_BMIPS_BCM6362_PM_H + +#define BCM6362_POWER_DOMAIN_SAR 0 +#define BCM6362_POWER_DOMAIN_IPSEC 1 +#define BCM6362_POWER_DOMAIN_MIPS 2 +#define BCM6362_POWER_DOMAIN_DECT 3 +#define BCM6362_POWER_DOMAIN_USBH 4 +#define BCM6362_POWER_DOMAIN_USBD 5 +#define BCM6362_POWER_DOMAIN_ROBOSW 6 +#define BCM6362_POWER_DOMAIN_PCM 7 +#define BCM6362_POWER_DOMAIN_PERIPH 8 +#define BCM6362_POWER_DOMAIN_ADSL_PHY 9 +#define BCM6362_POWER_DOMAIN_GMII_PADS 10 +#define BCM6362_POWER_DOMAIN_FAP 11 +#define BCM6362_POWER_DOMAIN_PCIE 12 +#define BCM6362_POWER_DOMAIN_WLAN_PADS 13 + +#endif /* __DT_BINDINGS_BMIPS_BCM6362_PM_H */ |