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