xref: /openbmc/linux/arch/arm/mach-bcm/bcm63xx_pmb.c (revision eb11b5a9)
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  */
bpcm_wr_rd_mask(void __iomem * master,unsigned int addr,u32 off,u32 * val,u32 shift,u32 mask,u32 cond)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 
bcm63xx_pmb_get_resources(struct device_node * dn,void __iomem ** base,unsigned int * cpu,unsigned int * addr)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 
94*eb11b5a9SRob Herring 	*cpu = of_get_cpu_hwid(dn, 0);
95*eb11b5a9SRob Herring 	if (*cpu == ~0U) {
963f2a43c9SFlorian Fainelli 		pr_err("CPU is missing a reg node\n");
97*eb11b5a9SRob Herring 		return -ENODEV;
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 
bcm63xx_pmb_power_on_cpu(struct device_node * dn)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