12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 23f2a43c9SFlorian Fainelli /* 33f2a43c9SFlorian Fainelli * Broadcom BCM63138 PMB initialization for secondary CPU(s) 43f2a43c9SFlorian Fainelli * 53f2a43c9SFlorian Fainelli * Copyright (C) 2015 Broadcom Corporation 63f2a43c9SFlorian Fainelli * Author: Florian Fainelli <f.fainelli@gmail.com> 73f2a43c9SFlorian Fainelli */ 83f2a43c9SFlorian Fainelli #include <linux/kernel.h> 93f2a43c9SFlorian Fainelli #include <linux/io.h> 103f2a43c9SFlorian Fainelli #include <linux/spinlock.h> 113f2a43c9SFlorian Fainelli #include <linux/reset/bcm63xx_pmb.h> 123f2a43c9SFlorian Fainelli #include <linux/of.h> 133f2a43c9SFlorian Fainelli #include <linux/of_address.h> 143f2a43c9SFlorian Fainelli 153f2a43c9SFlorian Fainelli #include "bcm63xx_smp.h" 163f2a43c9SFlorian Fainelli 173f2a43c9SFlorian Fainelli /* ARM Control register definitions */ 183f2a43c9SFlorian Fainelli #define CORE_PWR_CTRL_SHIFT 0 193f2a43c9SFlorian Fainelli #define CORE_PWR_CTRL_MASK 0x3 203f2a43c9SFlorian Fainelli #define PLL_PWR_ON BIT(8) 213f2a43c9SFlorian Fainelli #define PLL_LDO_PWR_ON BIT(9) 223f2a43c9SFlorian Fainelli #define PLL_CLAMP_ON BIT(10) 233f2a43c9SFlorian Fainelli #define CPU_RESET_N(x) BIT(13 + (x)) 243f2a43c9SFlorian Fainelli #define NEON_RESET_N BIT(15) 253f2a43c9SFlorian Fainelli #define PWR_CTRL_STATUS_SHIFT 28 263f2a43c9SFlorian Fainelli #define PWR_CTRL_STATUS_MASK 0x3 273f2a43c9SFlorian Fainelli #define PWR_DOWN_SHIFT 30 283f2a43c9SFlorian Fainelli #define PWR_DOWN_MASK 0x3 293f2a43c9SFlorian Fainelli 303f2a43c9SFlorian Fainelli /* CPU Power control register definitions */ 313f2a43c9SFlorian Fainelli #define MEM_PWR_OK BIT(0) 323f2a43c9SFlorian Fainelli #define MEM_PWR_ON BIT(1) 333f2a43c9SFlorian Fainelli #define MEM_CLAMP_ON BIT(2) 343f2a43c9SFlorian Fainelli #define MEM_PWR_OK_STATUS BIT(4) 353f2a43c9SFlorian Fainelli #define MEM_PWR_ON_STATUS BIT(5) 363f2a43c9SFlorian Fainelli #define MEM_PDA_SHIFT 8 373f2a43c9SFlorian Fainelli #define MEM_PDA_MASK 0xf 383f2a43c9SFlorian Fainelli #define MEM_PDA_CPU_MASK 0x1 393f2a43c9SFlorian Fainelli #define MEM_PDA_NEON_MASK 0xf 403f2a43c9SFlorian Fainelli #define CLAMP_ON BIT(15) 413f2a43c9SFlorian Fainelli #define PWR_OK_SHIFT 16 423f2a43c9SFlorian Fainelli #define PWR_OK_MASK 0xf 433f2a43c9SFlorian Fainelli #define PWR_ON_SHIFT 20 443f2a43c9SFlorian Fainelli #define PWR_CPU_MASK 0x03 453f2a43c9SFlorian Fainelli #define PWR_NEON_MASK 0x01 463f2a43c9SFlorian Fainelli #define PWR_ON_MASK 0xf 473f2a43c9SFlorian Fainelli #define PWR_OK_STATUS_SHIFT 24 483f2a43c9SFlorian Fainelli #define PWR_OK_STATUS_MASK 0xf 493f2a43c9SFlorian Fainelli #define PWR_ON_STATUS_SHIFT 28 503f2a43c9SFlorian Fainelli #define PWR_ON_STATUS_MASK 0xf 513f2a43c9SFlorian Fainelli 523f2a43c9SFlorian Fainelli #define ARM_CONTROL 0x30 533f2a43c9SFlorian Fainelli #define ARM_PWR_CONTROL_BASE 0x34 543f2a43c9SFlorian Fainelli #define ARM_PWR_CONTROL(x) (ARM_PWR_CONTROL_BASE + (x) * 0x4) 553f2a43c9SFlorian Fainelli #define ARM_NEON_L2 0x3c 563f2a43c9SFlorian Fainelli 573f2a43c9SFlorian Fainelli /* Perform a value write, then spin until the value shifted by 583f2a43c9SFlorian Fainelli * shift is seen, masked with mask and is different from cond. 593f2a43c9SFlorian Fainelli */ 603f2a43c9SFlorian Fainelli static int bpcm_wr_rd_mask(void __iomem *master, 613f2a43c9SFlorian Fainelli unsigned int addr, u32 off, u32 *val, 623f2a43c9SFlorian Fainelli u32 shift, u32 mask, u32 cond) 633f2a43c9SFlorian Fainelli { 643f2a43c9SFlorian Fainelli int ret; 653f2a43c9SFlorian Fainelli 663f2a43c9SFlorian Fainelli ret = bpcm_wr(master, addr, off, *val); 673f2a43c9SFlorian Fainelli if (ret) 683f2a43c9SFlorian Fainelli return ret; 693f2a43c9SFlorian Fainelli 703f2a43c9SFlorian Fainelli do { 713f2a43c9SFlorian Fainelli ret = bpcm_rd(master, addr, off, val); 723f2a43c9SFlorian Fainelli if (ret) 733f2a43c9SFlorian Fainelli return ret; 743f2a43c9SFlorian Fainelli 753f2a43c9SFlorian Fainelli cpu_relax(); 763f2a43c9SFlorian Fainelli } while (((*val >> shift) & mask) != cond); 773f2a43c9SFlorian Fainelli 783f2a43c9SFlorian Fainelli return ret; 793f2a43c9SFlorian Fainelli } 803f2a43c9SFlorian Fainelli 813f2a43c9SFlorian Fainelli /* Global lock to serialize accesses to the PMB registers while we 823f2a43c9SFlorian Fainelli * are bringing up the secondary CPU 833f2a43c9SFlorian Fainelli */ 843f2a43c9SFlorian Fainelli static DEFINE_SPINLOCK(pmb_lock); 853f2a43c9SFlorian Fainelli 863f2a43c9SFlorian Fainelli static int bcm63xx_pmb_get_resources(struct device_node *dn, 873f2a43c9SFlorian Fainelli void __iomem **base, 883f2a43c9SFlorian Fainelli unsigned int *cpu, 893f2a43c9SFlorian Fainelli unsigned int *addr) 903f2a43c9SFlorian Fainelli { 913f2a43c9SFlorian Fainelli struct of_phandle_args args; 923f2a43c9SFlorian Fainelli int ret; 933f2a43c9SFlorian Fainelli 943f2a43c9SFlorian Fainelli ret = of_property_read_u32(dn, "reg", cpu); 953f2a43c9SFlorian Fainelli if (ret) { 963f2a43c9SFlorian Fainelli pr_err("CPU is missing a reg node\n"); 973f2a43c9SFlorian Fainelli return ret; 983f2a43c9SFlorian Fainelli } 993f2a43c9SFlorian Fainelli 1003f2a43c9SFlorian Fainelli ret = of_parse_phandle_with_args(dn, "resets", "#reset-cells", 1013f2a43c9SFlorian Fainelli 0, &args); 1023f2a43c9SFlorian Fainelli if (ret) { 1033f2a43c9SFlorian Fainelli pr_err("CPU is missing a resets phandle\n"); 1043f2a43c9SFlorian Fainelli return ret; 1053f2a43c9SFlorian Fainelli } 1063f2a43c9SFlorian Fainelli 1073f2a43c9SFlorian Fainelli if (args.args_count != 2) { 1083f2a43c9SFlorian Fainelli pr_err("reset-controller does not conform to reset-cells\n"); 1093f2a43c9SFlorian Fainelli return -EINVAL; 1103f2a43c9SFlorian Fainelli } 1113f2a43c9SFlorian Fainelli 1123f2a43c9SFlorian Fainelli *base = of_iomap(args.np, 0); 1133f2a43c9SFlorian Fainelli if (!*base) { 1143f2a43c9SFlorian Fainelli pr_err("failed remapping PMB register\n"); 1153f2a43c9SFlorian Fainelli return -ENOMEM; 1163f2a43c9SFlorian Fainelli } 1173f2a43c9SFlorian Fainelli 1183f2a43c9SFlorian Fainelli /* We do not need the number of zones */ 1193f2a43c9SFlorian Fainelli *addr = args.args[0]; 1203f2a43c9SFlorian Fainelli 1213f2a43c9SFlorian Fainelli return 0; 1223f2a43c9SFlorian Fainelli } 1233f2a43c9SFlorian Fainelli 1243f2a43c9SFlorian Fainelli int bcm63xx_pmb_power_on_cpu(struct device_node *dn) 1253f2a43c9SFlorian Fainelli { 1263f2a43c9SFlorian Fainelli void __iomem *base; 1273f2a43c9SFlorian Fainelli unsigned int cpu, addr; 1283f2a43c9SFlorian Fainelli unsigned long flags; 1293f2a43c9SFlorian Fainelli u32 val, ctrl; 1303f2a43c9SFlorian Fainelli int ret; 1313f2a43c9SFlorian Fainelli 1323f2a43c9SFlorian Fainelli ret = bcm63xx_pmb_get_resources(dn, &base, &cpu, &addr); 1333f2a43c9SFlorian Fainelli if (ret) 1343f2a43c9SFlorian Fainelli return ret; 1353f2a43c9SFlorian Fainelli 1363f2a43c9SFlorian Fainelli /* We would not know how to enable a third and greater CPU */ 1373f2a43c9SFlorian Fainelli WARN_ON(cpu > 1); 1383f2a43c9SFlorian Fainelli 1393f2a43c9SFlorian Fainelli spin_lock_irqsave(&pmb_lock, flags); 1403f2a43c9SFlorian Fainelli 1413f2a43c9SFlorian Fainelli /* Check if the CPU is already on and save the ARM_CONTROL register 1423f2a43c9SFlorian Fainelli * value since we will use it later for CPU de-assert once done with 1433f2a43c9SFlorian Fainelli * the CPU-specific power sequence 1443f2a43c9SFlorian Fainelli */ 1453f2a43c9SFlorian Fainelli ret = bpcm_rd(base, addr, ARM_CONTROL, &ctrl); 1463f2a43c9SFlorian Fainelli if (ret) 1473cc63056SDan Carpenter goto out; 1483f2a43c9SFlorian Fainelli 1493f2a43c9SFlorian Fainelli if (ctrl & CPU_RESET_N(cpu)) { 1503f2a43c9SFlorian Fainelli pr_info("PMB: CPU%d is already powered on\n", cpu); 1513f2a43c9SFlorian Fainelli ret = 0; 1523f2a43c9SFlorian Fainelli goto out; 1533f2a43c9SFlorian Fainelli } 1543f2a43c9SFlorian Fainelli 1553f2a43c9SFlorian Fainelli /* Power on PLL */ 1563f2a43c9SFlorian Fainelli ret = bpcm_rd(base, addr, ARM_PWR_CONTROL(cpu), &val); 1573f2a43c9SFlorian Fainelli if (ret) 1583f2a43c9SFlorian Fainelli goto out; 1593f2a43c9SFlorian Fainelli 1603f2a43c9SFlorian Fainelli val |= (PWR_CPU_MASK << PWR_ON_SHIFT); 1613f2a43c9SFlorian Fainelli 1623f2a43c9SFlorian Fainelli ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val, 1633f2a43c9SFlorian Fainelli PWR_ON_STATUS_SHIFT, PWR_CPU_MASK, PWR_CPU_MASK); 1643f2a43c9SFlorian Fainelli if (ret) 1653f2a43c9SFlorian Fainelli goto out; 1663f2a43c9SFlorian Fainelli 1673f2a43c9SFlorian Fainelli val |= (PWR_CPU_MASK << PWR_OK_SHIFT); 1683f2a43c9SFlorian Fainelli 1693f2a43c9SFlorian Fainelli ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val, 1703f2a43c9SFlorian Fainelli PWR_OK_STATUS_SHIFT, PWR_CPU_MASK, PWR_CPU_MASK); 1713f2a43c9SFlorian Fainelli if (ret) 1723f2a43c9SFlorian Fainelli goto out; 1733f2a43c9SFlorian Fainelli 1743f2a43c9SFlorian Fainelli val &= ~CLAMP_ON; 1753f2a43c9SFlorian Fainelli 1763f2a43c9SFlorian Fainelli ret = bpcm_wr(base, addr, ARM_PWR_CONTROL(cpu), val); 1773f2a43c9SFlorian Fainelli if (ret) 1783f2a43c9SFlorian Fainelli goto out; 1793f2a43c9SFlorian Fainelli 1803f2a43c9SFlorian Fainelli /* Power on CPU<N> RAM */ 1813f2a43c9SFlorian Fainelli val &= ~(MEM_PDA_MASK << MEM_PDA_SHIFT); 1823f2a43c9SFlorian Fainelli 1833f2a43c9SFlorian Fainelli ret = bpcm_wr(base, addr, ARM_PWR_CONTROL(cpu), val); 1843f2a43c9SFlorian Fainelli if (ret) 1853f2a43c9SFlorian Fainelli goto out; 1863f2a43c9SFlorian Fainelli 1873f2a43c9SFlorian Fainelli val |= MEM_PWR_ON; 1883f2a43c9SFlorian Fainelli 1893f2a43c9SFlorian Fainelli ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val, 1903f2a43c9SFlorian Fainelli 0, MEM_PWR_ON_STATUS, MEM_PWR_ON_STATUS); 1913f2a43c9SFlorian Fainelli if (ret) 1923f2a43c9SFlorian Fainelli goto out; 1933f2a43c9SFlorian Fainelli 1943f2a43c9SFlorian Fainelli val |= MEM_PWR_OK; 1953f2a43c9SFlorian Fainelli 1963f2a43c9SFlorian Fainelli ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val, 1973f2a43c9SFlorian Fainelli 0, MEM_PWR_OK_STATUS, MEM_PWR_OK_STATUS); 1983f2a43c9SFlorian Fainelli if (ret) 1993f2a43c9SFlorian Fainelli goto out; 2003f2a43c9SFlorian Fainelli 2013f2a43c9SFlorian Fainelli val &= ~MEM_CLAMP_ON; 2023f2a43c9SFlorian Fainelli 2033f2a43c9SFlorian Fainelli ret = bpcm_wr(base, addr, ARM_PWR_CONTROL(cpu), val); 2043f2a43c9SFlorian Fainelli if (ret) 2053f2a43c9SFlorian Fainelli goto out; 2063f2a43c9SFlorian Fainelli 2073f2a43c9SFlorian Fainelli /* De-assert CPU reset */ 2083f2a43c9SFlorian Fainelli ctrl |= CPU_RESET_N(cpu); 2093f2a43c9SFlorian Fainelli 2103f2a43c9SFlorian Fainelli ret = bpcm_wr(base, addr, ARM_CONTROL, ctrl); 2113f2a43c9SFlorian Fainelli out: 2123f2a43c9SFlorian Fainelli spin_unlock_irqrestore(&pmb_lock, flags); 2133f2a43c9SFlorian Fainelli iounmap(base); 2143f2a43c9SFlorian Fainelli return ret; 2153f2a43c9SFlorian Fainelli } 216