xref: /openbmc/linux/arch/arm/mach-bcm/bcm63xx_pmb.c (revision 3f2a43c9)
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