diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-11 01:56:23 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-11 01:56:23 +0300 |
commit | 56e0464980febfa50432a070261579415c72664e (patch) | |
tree | ec4753abd2562c317ed5f33b81c156b319456fd5 /arch/arm | |
parent | a5e1d715a8d0696961d99d31d869aa522f1cad5a (diff) | |
parent | b1e4006aeda8c8784029de17d47987c21ea75f6d (diff) | |
download | linux-56e0464980febfa50432a070261579415c72664e.tar.xz |
Merge tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC platform updates from Olof Johansson:
"New and/or improved SoC support for this release:
Marvell Berlin:
- Enable standard DT-based cpufreq
- Add CPU hotplug support
Freescale:
- Ethernet init for i.MX7D
- Suspend/resume support for i.MX6UL
Allwinner:
- Support for R8 chipset (used on NTC's $9 C.H.I.P board)
Mediatek:
- SMP support for some platforms
Uniphier:
- L2 support
- Cleaned up SMP support, etc.
plus a handful of other patches around above functionality, and a few
other smaller changes"
* tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (42 commits)
ARM: uniphier: rework SMP operations to use trampoline code
ARM: uniphier: add outer cache support
Documentation: EXYNOS: Update bootloader interface on exynos542x
ARM: mvebu: add broken-idle option
ARM: orion5x: use mac_pton() helper
ARM: at91: pm: at91_pm_suspend_in_sram() must be 8-byte aligned
ARM: sunxi: Add R8 support
ARM: digicolor: select pinctrl/gpio driver
arm: berlin: add CPU hotplug support
arm: berlin: use non-self-cleared reset register to reset cpu
ARM: mediatek: add smp bringup code
ARM: mediatek: enable gpt6 on boot up to make arch timer working
soc: mediatek: Fix random hang up issue while kernel init
soc: ti: qmss: make acc queue support optional in the driver
soc: ti: add firmware file name as part of the driver
Documentation: dt: soc: Add description for knav qmss driver
ARM: S3C64XX: Use PWM lookup table for mach-smartq
ARM: S3C64XX: Use PWM lookup table for mach-hmt
ARM: S3C64XX: Use PWM lookup table for mach-crag6410
ARM: S3C64XX: Use PWM lookup table for smdk6410
...
Diffstat (limited to 'arch/arm')
43 files changed, 1381 insertions, 229 deletions
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 40a3c25c1e0d..259c0ca9c99a 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -123,29 +123,23 @@ choice 0x80020000 | 0xf0020000 | UART8 0x80024000 | 0xf0024000 | UART9 - config AT91_DEBUG_LL_DBGU0 - bool "Kernel low-level debugging on rm9200, 9260/9g20, 9261/9g10, 9rl, 9x5, 9n12" - select DEBUG_AT91_UART + config DEBUG_AT91_UART + bool "Kernel low-level debugging on Atmel SoCs" depends on ARCH_AT91 - depends on SOC_AT91RM9200 || SOC_AT91SAM9 + help + Say Y here if you want the debug print routines to direct + their output to the serial port on atmel devices. - config AT91_DEBUG_LL_DBGU1 - bool "Kernel low-level debugging on 9263, 9g45 and sama5d3" - select DEBUG_AT91_UART - depends on ARCH_AT91 - depends on SOC_AT91SAM9 || SOC_SAMA5 + SOC DEBUG_UART_PHYS DEBUG_UART_VIRT PORT + rm9200, 9260/9g20, 0xfffff200 0xfefff200 DBGU + 9261/9g10, 9rl + 9263, 9g45, sama5d3 0xffffee00 0xfeffee00 DBGU + sama5d4 0xfc00c000 0xfb00c000 USART3 + sama5d4 0xfc069000 0xfb069000 DBGU + sama5d2 0xf8020000 0xf7020000 UART1 - config AT91_DEBUG_LL_DBGU2 - bool "Kernel low-level debugging on sama5d4" - select DEBUG_AT91_UART - depends on ARCH_AT91 - depends on SOC_SAMA5 - - config AT91_DEBUG_LL_DBGU3 - bool "Kernel low-level debugging on sama5d2" - select DEBUG_AT91_UART - depends on ARCH_AT91 - depends on SOC_SAMA5 + Please adjust DEBUG_UART_PHYS configuration options based on + your needs. config DEBUG_BCM2835 bool "Kernel low-level debugging on BCM2835 PL011 UART" @@ -1249,10 +1243,6 @@ choice endchoice -config DEBUG_AT91_UART - bool - depends on ARCH_AT91 - config DEBUG_EXYNOS_UART bool @@ -1485,7 +1475,8 @@ config DEBUG_UART_PHYS DEBUG_RMOBILE_SCIFA0 || DEBUG_RMOBILE_SCIFA1 || \ DEBUG_RMOBILE_SCIFA4 || DEBUG_S3C24XX_UART || \ DEBUG_UART_BCM63XX || DEBUG_ASM9260_UART || \ - DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 + DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 || \ + DEBUG_AT91_UART config DEBUG_UART_VIRT hex "Virtual base address of debug UART" diff --git a/arch/arm/include/asm/hardware/cache-uniphier.h b/arch/arm/include/asm/hardware/cache-uniphier.h new file mode 100644 index 000000000000..102e3fbe1e10 --- /dev/null +++ b/arch/arm/include/asm/hardware/cache-uniphier.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CACHE_UNIPHIER_H +#define __CACHE_UNIPHIER_H + +#include <linux/types.h> + +#ifdef CONFIG_CACHE_UNIPHIER +int uniphier_cache_init(void); +int uniphier_cache_l2_is_enabled(void); +void uniphier_cache_l2_touch_range(unsigned long start, unsigned long end); +void uniphier_cache_l2_set_locked_ways(u32 way_mask); +#else +static inline int uniphier_cache_init(void) +{ + return -ENODEV; +} + +static inline int uniphier_cache_l2_is_enabled(void) +{ + return 0; +} + +static inline void uniphier_cache_l2_touch_range(unsigned long start, + unsigned long end) +{ +} + +static inline void uniphier_cache_l2_set_locked_ways(u32 way_mask) +{ +} +#endif + +#endif /* __CACHE_UNIPHIER_H */ diff --git a/arch/arm/include/debug/at91.S b/arch/arm/include/debug/at91.S index 2556a8801c8c..43243be94cfc 100644 --- a/arch/arm/include/debug/at91.S +++ b/arch/arm/include/debug/at91.S @@ -9,32 +9,22 @@ * */ -#if defined(CONFIG_AT91_DEBUG_LL_DBGU0) -#define AT91_DBGU 0xfffff200 /* AT91_BASE_DBGU0 */ -#elif defined(CONFIG_AT91_DEBUG_LL_DBGU1) -#define AT91_DBGU 0xffffee00 /* AT91_BASE_DBGU1 */ -#elif defined(CONFIG_AT91_DEBUG_LL_DBGU2) -/* On sama5d4, use USART3 as low level serial console */ -#define AT91_DBGU 0xfc00c000 /* SAMA5D4_BASE_USART3 */ -#else -/* On sama5d2, use UART1 as low level serial console */ -#define AT91_DBGU 0xf8020000 -#endif - #ifdef CONFIG_MMU #define AT91_IO_P2V(x) ((x) - 0x01000000) #else #define AT91_IO_P2V(x) (x) #endif +#define CONFIG_DEBUG_UART_VIRT AT91_IO_P2V(CONFIG_DEBUG_UART_PHYS) + #define AT91_DBGU_SR (0x14) /* Status Register */ #define AT91_DBGU_THR (0x1c) /* Transmitter Holding Register */ #define AT91_DBGU_TXRDY (1 << 1) /* Transmitter Ready */ #define AT91_DBGU_TXEMPTY (1 << 9) /* Transmitter Empty */ .macro addruart, rp, rv, tmp - ldr \rp, =AT91_DBGU @ System peripherals (phys address) - ldr \rv, =AT91_IO_P2V(AT91_DBGU) @ System peripherals (virt address) + ldr \rp, =CONFIG_DEBUG_UART_PHYS @ System peripherals (phys address) + ldr \rv, =CONFIG_DEBUG_UART_VIRT @ System peripherals (virt address) .endm .macro senduart,rd,rx diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 2766183e69df..1d45320ee125 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -39,6 +39,7 @@ #include <linux/export.h> #include <asm/hardware/cache-l2x0.h> +#include <asm/hardware/cache-uniphier.h> #include <asm/outercache.h> #include <asm/exception.h> #include <asm/mach/arch.h> @@ -97,6 +98,8 @@ void __init init_IRQ(void) if (ret) pr_err("L2C: failed to init: %d\n", ret); } + + uniphier_cache_init(); } #ifdef CONFIG_MULTI_IRQ_HANDLER diff --git a/arch/arm/mach-at91/pm_suspend.S b/arch/arm/mach-at91/pm_suspend.S index 0d95f488b47a..a25defda3d22 100644 --- a/arch/arm/mach-at91/pm_suspend.S +++ b/arch/arm/mach-at91/pm_suspend.S @@ -80,6 +80,8 @@ tmp2 .req r5 * @r2: base address of second SDRAM Controller or 0 if not present * @r3: pm information */ +/* at91_pm_suspend_in_sram must be 8-byte aligned per the requirements of fncpy() */ + .align 3 ENTRY(at91_pm_suspend_in_sram) /* Save registers on stack */ stmfd sp!, {r4 - r12, lr} diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index 84bd26535ae9..8c53c55be1fe 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -35,6 +35,20 @@ config ARCH_BCM_CYGNUS BCM11300, BCM11320, BCM11350, BCM11360, BCM58300, BCM58302, BCM58303, BCM58305. +config ARCH_BCM_NSP + bool "Broadcom Northstar Plus SoC Support" if ARCH_MULTI_V7 + select ARCH_BCM_IPROC + select ARM_ERRATA_754322 + select ARM_ERRATA_775420 + help + Support for Broadcom Northstar Plus SoC. + Broadcom Northstar Plus family of SoCs are used for switching control + and management applications as well as residential router/gateway + applications. The SoC features dual core Cortex A9 ARM CPUs, + integrating several peripheral interfaces including multiple Gigabit + Ethernet PHYs, DDR3 memory, PCIE Gen-2, USB 2.0 and USB 3.0, serial and + NAND flash, SATA and several other IO controllers. + config ARCH_BCM_5301X bool "Broadcom BCM470X / BCM5301X ARM SoC" if ARCH_MULTI_V7 select ARCH_BCM_IPROC @@ -147,6 +161,7 @@ config ARCH_BRCMSTB select BCM7120_L2_IRQ select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE select ARCH_WANT_OPTIONAL_GPIOLIB + select SOC_BRCMSTB help Say Y if you intend to run the kernel on a Broadcom ARM-based STB chipset. diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile index 1780a3ff42f9..892261fec0ae 100644 --- a/arch/arm/mach-bcm/Makefile +++ b/arch/arm/mach-bcm/Makefile @@ -1,5 +1,5 @@ # -# Copyright (C) 2012-2014 Broadcom Corporation +# Copyright (C) 2012-2015 Broadcom Corporation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -13,6 +13,9 @@ # Cygnus obj-$(CONFIG_ARCH_BCM_CYGNUS) += bcm_cygnus.o +# Northstar Plus +obj-$(CONFIG_ARCH_BCM_NSP) += bcm_nsp.o + # BCM281XX obj-$(CONFIG_ARCH_BCM_281XX) += board_bcm281xx.o diff --git a/arch/arm/mach-bcm/bcm_nsp.c b/arch/arm/mach-bcm/bcm_nsp.c new file mode 100644 index 000000000000..a1101a3d318e --- /dev/null +++ b/arch/arm/mach-bcm/bcm_nsp.c @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <asm/mach/arch.h> + +static const char *const bcm_nsp_dt_compat[] __initconst = { + "brcm,nsp", + NULL, +}; + +DT_MACHINE_START(NSP_DT, "Broadcom Northstar Plus SoC") + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, + .dt_compat = bcm_nsp_dt_compat, +MACHINE_END diff --git a/arch/arm/mach-bcm/brcmstb.c b/arch/arm/mach-bcm/brcmstb.c index 3a60f7ee3f0c..99a67cfb7c0d 100644 --- a/arch/arm/mach-bcm/brcmstb.c +++ b/arch/arm/mach-bcm/brcmstb.c @@ -12,11 +12,19 @@ */ #include <linux/init.h> +#include <linux/irqchip.h> #include <linux/of_platform.h> +#include <linux/soc/brcmstb/brcmstb.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> +static void __init brcmstb_init_irq(void) +{ + irqchip_init(); + brcmstb_biuctrl_init(); +} + static const char *const brcmstb_match[] __initconst = { "brcm,bcm7445", "brcm,brcmstb", @@ -25,4 +33,5 @@ static const char *const brcmstb_match[] __initconst = { DT_MACHINE_START(BRCMSTB, "Broadcom STB (Flattened Device Tree)") .dt_compat = brcmstb_match, + .init_irq = brcmstb_init_irq, MACHINE_END diff --git a/arch/arm/mach-berlin/berlin.c b/arch/arm/mach-berlin/berlin.c index ac181c6797ee..25d73870ccca 100644 --- a/arch/arm/mach-berlin/berlin.c +++ b/arch/arm/mach-berlin/berlin.c @@ -18,6 +18,11 @@ #include <asm/hardware/cache-l2x0.h> #include <asm/mach/arch.h> +static void __init berlin_init_late(void) +{ + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); +} + static const char * const berlin_dt_compat[] = { "marvell,berlin", NULL, @@ -25,6 +30,7 @@ static const char * const berlin_dt_compat[] = { DT_MACHINE_START(BERLIN_DT, "Marvell Berlin") .dt_compat = berlin_dt_compat, + .init_late = berlin_init_late, /* * with DT probing for L2CCs, berlin_init_machine can be removed. * Note: 88DE3005 (Armada 1500-mini) uses pl310 l2cc diff --git a/arch/arm/mach-berlin/platsmp.c b/arch/arm/mach-berlin/platsmp.c index 34a3753e7356..405cd37e4fba 100644 --- a/arch/arm/mach-berlin/platsmp.c +++ b/arch/arm/mach-berlin/platsmp.c @@ -14,10 +14,16 @@ #include <linux/of_address.h> #include <asm/cacheflush.h> +#include <asm/cp15.h> #include <asm/smp_plat.h> #include <asm/smp_scu.h> -#define CPU_RESET 0x00 +/* + * There are two reset registers, one with self-clearing (SC) + * reset and one with non-self-clearing reset (NON_SC). + */ +#define CPU_RESET_SC 0x00 +#define CPU_RESET_NON_SC 0x20 #define RESET_VECT 0x00 #define SW_RESET_ADDR 0x94 @@ -30,9 +36,11 @@ static inline void berlin_perform_reset_cpu(unsigned int cpu) { u32 val; - val = readl(cpu_ctrl + CPU_RESET); + val = readl(cpu_ctrl + CPU_RESET_NON_SC); + val &= ~BIT(cpu_logical_map(cpu)); + writel(val, cpu_ctrl + CPU_RESET_NON_SC); val |= BIT(cpu_logical_map(cpu)); - writel(val, cpu_ctrl + CPU_RESET); + writel(val, cpu_ctrl + CPU_RESET_NON_SC); } static int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle) @@ -91,8 +99,32 @@ unmap_scu: iounmap(scu_base); } +#ifdef CONFIG_HOTPLUG_CPU +static void berlin_cpu_die(unsigned int cpu) +{ + v7_exit_coherency_flush(louis); + while (1) + cpu_do_idle(); +} + +static int berlin_cpu_kill(unsigned int cpu) +{ + u32 val; + + val = readl(cpu_ctrl + CPU_RESET_NON_SC); + val &= ~BIT(cpu_logical_map(cpu)); + writel(val, cpu_ctrl + CPU_RESET_NON_SC); + + return 1; +} +#endif + static struct smp_operations berlin_smp_ops __initdata = { .smp_prepare_cpus = berlin_smp_prepare_cpus, .smp_boot_secondary = berlin_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = berlin_cpu_die, + .cpu_kill = berlin_cpu_kill, +#endif }; CPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops); diff --git a/arch/arm/mach-digicolor/Kconfig b/arch/arm/mach-digicolor/Kconfig index 4f36d8d2bc57..fc65b0f1db48 100644 --- a/arch/arm/mach-digicolor/Kconfig +++ b/arch/arm/mach-digicolor/Kconfig @@ -1,7 +1,10 @@ config ARCH_DIGICOLOR bool "Conexant Digicolor SoC Support" depends on ARCH_MULTI_V7 + select ARCH_REQUIRE_GPIOLIB select CLKSRC_MMIO select DIGICOLOR_TIMER select GENERIC_IRQ_CHIP select MFD_SYSCON + select PINCTRL + select PINCTRL_DIGICOLOR diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index 21e4e8697a58..e2d53839fceb 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -131,6 +131,7 @@ void imx6q_pm_init(void); void imx6dl_pm_init(void); void imx6sl_pm_init(void); void imx6sx_pm_init(void); +void imx6ul_pm_init(void); #ifdef CONFIG_PM void imx51_pm_init(void); diff --git a/arch/arm/mach-imx/mach-imx6ul.c b/arch/arm/mach-imx/mach-imx6ul.c index 1b97fe133cef..acaf7056efa5 100644 --- a/arch/arm/mach-imx/mach-imx6ul.c +++ b/arch/arm/mach-imx/mach-imx6ul.c @@ -67,6 +67,7 @@ static void __init imx6ul_init_machine(void) of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); imx6ul_enet_init(); imx_anatop_init(); + imx6ul_pm_init(); } static void __init imx6ul_init_irq(void) @@ -74,6 +75,13 @@ static void __init imx6ul_init_irq(void) imx_init_revision_from_anatop(); imx_src_init(); irqchip_init(); + imx6_pm_ccm_init("fsl,imx6ul-ccm"); +} + +static void __init imx6ul_init_late(void) +{ + if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) + platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0); } static const char *imx6ul_dt_compat[] __initconst = { @@ -84,5 +92,6 @@ static const char *imx6ul_dt_compat[] __initconst = { DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)") .init_irq = imx6ul_init_irq, .init_machine = imx6ul_init_machine, + .init_late = imx6ul_init_late, .dt_compat = imx6ul_dt_compat, MACHINE_END diff --git a/arch/arm/mach-imx/mach-imx7d.c b/arch/arm/mach-imx/mach-imx7d.c index 62f3437257f1..b450f525a670 100644 --- a/arch/arm/mach-imx/mach-imx7d.c +++ b/arch/arm/mach-imx/mach-imx7d.c @@ -6,12 +6,85 @@ * published by the Free Software Foundation. */ #include <linux/irqchip.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx7-iomuxc-gpr.h> #include <linux/of_platform.h> +#include <linux/phy.h> +#include <linux/regmap.h> + #include <asm/mach/arch.h> #include <asm/mach/map.h> #include "common.h" +static int ar8031_phy_fixup(struct phy_device *dev) +{ + u16 val; + + /* Set RGMII IO voltage to 1.8V */ + phy_write(dev, 0x1d, 0x1f); + phy_write(dev, 0x1e, 0x8); + + /* disable phy AR8031 SmartEEE function. */ + phy_write(dev, 0xd, 0x3); + phy_write(dev, 0xe, 0x805d); + phy_write(dev, 0xd, 0x4003); + val = phy_read(dev, 0xe); + val &= ~(0x1 << 8); + phy_write(dev, 0xe, val); + + /* introduce tx clock delay */ + phy_write(dev, 0x1d, 0x5); + val = phy_read(dev, 0x1e); + val |= 0x0100; + phy_write(dev, 0x1e, val); + + return 0; +} + +static int bcm54220_phy_fixup(struct phy_device *dev) +{ + /* enable RXC skew select RGMII copper mode */ + phy_write(dev, 0x1e, 0x21); + phy_write(dev, 0x1f, 0x7ea8); + phy_write(dev, 0x1e, 0x2f); + phy_write(dev, 0x1f, 0x71b7); + + return 0; +} + +#define PHY_ID_AR8031 0x004dd074 +#define PHY_ID_BCM54220 0x600d8589 + +static void __init imx7d_enet_phy_init(void) +{ + if (IS_BUILTIN(CONFIG_PHYLIB)) { + phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffff, + ar8031_phy_fixup); + phy_register_fixup_for_uid(PHY_ID_BCM54220, 0xffffffff, + bcm54220_phy_fixup); + } +} + +static void __init imx7d_enet_clk_sel(void) +{ + struct regmap *gpr; + + gpr = syscon_regmap_lookup_by_compatible("fsl,imx7d-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_TX_CLK_SEL_MASK, 0); + regmap_update_bits(gpr, IOMUXC_GPR1, IMX7D_GPR1_ENET_CLK_DIR_MASK, 0); + } else { + pr_err("failed to find fsl,imx7d-iomux-gpr regmap\n"); + } +} + +static inline void imx7d_enet_init(void) +{ + imx7d_enet_phy_init(); + imx7d_enet_clk_sel(); +} + static void __init imx7d_init_machine(void) { struct device *parent; @@ -22,6 +95,7 @@ static void __init imx7d_init_machine(void) of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); imx_anatop_init(); + imx7d_enet_init(); } static void __init imx7d_init_irq(void) diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c index 8ff8fc0b261c..4470376af5f8 100644 --- a/arch/arm/mach-imx/pm-imx6.c +++ b/arch/arm/mach-imx/pm-imx6.c @@ -93,6 +93,7 @@ struct imx6_pm_socdata { const char *src_compat; const char *iomuxc_compat; const char *gpc_compat; + const char *pl310_compat; const u32 mmdc_io_num; const u32 *mmdc_io_offset; }; @@ -137,11 +138,19 @@ static const u32 imx6sx_mmdc_io_offset[] __initconst = { 0x330, 0x334, 0x338, 0x33c, /* SDQS0 ~ SDQS3 */ }; +static const u32 imx6ul_mmdc_io_offset[] __initconst = { + 0x244, 0x248, 0x24c, 0x250, /* DQM0, DQM1, RAS, CAS */ + 0x27c, 0x498, 0x4a4, 0x490, /* SDCLK0, GPR_B0DS-B1DS, GPR_ADDS */ + 0x280, 0x284, 0x260, 0x264, /* SDQS0~1, SODT0, SODT1 */ + 0x494, 0x4b0, /* MODE_CTL, MODE, */ +}; + static const struct imx6_pm_socdata imx6q_pm_data __initconst = { .mmdc_compat = "fsl,imx6q-mmdc", .src_compat = "fsl,imx6q-src", .iomuxc_compat = "fsl,imx6q-iomuxc", .gpc_compat = "fsl,imx6q-gpc", + .pl310_compat = "arm,pl310-cache", .mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset), .mmdc_io_offset = imx6q_mmdc_io_offset, }; @@ -151,6 +160,7 @@ static const struct imx6_pm_socdata imx6dl_pm_data __initconst = { .src_compat = "fsl,imx6q-src", .iomuxc_compat = "fsl,imx6dl-iomuxc", .gpc_compat = "fsl,imx6q-gpc", + .pl310_compat = "arm,pl310-cache", .mmdc_io_num = ARRAY_SIZE(imx6dl_mmdc_io_offset), .mmdc_io_offset = imx6dl_mmdc_io_offset, }; @@ -160,6 +170,7 @@ static const struct imx6_pm_socdata imx6sl_pm_data __initconst = { .src_compat = "fsl,imx6sl-src", .iomuxc_compat = "fsl,imx6sl-iomuxc", .gpc_compat = "fsl,imx6sl-gpc", + .pl310_compat = "arm,pl310-cache", .mmdc_io_num = ARRAY_SIZE(imx6sl_mmdc_io_offset), .mmdc_io_offset = imx6sl_mmdc_io_offset, }; @@ -169,10 +180,21 @@ static const struct imx6_pm_socdata imx6sx_pm_data __initconst = { .src_compat = "fsl,imx6sx-src", .iomuxc_compat = "fsl,imx6sx-iomuxc", .gpc_compat = "fsl,imx6sx-gpc", + .pl310_compat = "arm,pl310-cache", .mmdc_io_num = ARRAY_SIZE(imx6sx_mmdc_io_offset), .mmdc_io_offset = imx6sx_mmdc_io_offset, }; +static const struct imx6_pm_socdata imx6ul_pm_data __initconst = { + .mmdc_compat = "fsl,imx6ul-mmdc", + .src_compat = "fsl,imx6ul-src", + .iomuxc_compat = "fsl,imx6ul-iomuxc", + .gpc_compat = "fsl,imx6ul-gpc", + .pl310_compat = NULL, + .mmdc_io_num = ARRAY_SIZE(imx6ul_mmdc_io_offset), + .mmdc_io_offset = imx6ul_mmdc_io_offset, +}; + /* * This structure is for passing necessary data for low level ocram * suspend code(arch/arm/mach-imx/suspend-imx6.S), if this struct @@ -290,7 +312,7 @@ int imx6_set_lpm(enum mxc_cpu_pwr_mode mode) val |= BM_CLPCR_SBYOS; if (cpu_is_imx6sl()) val |= BM_CLPCR_BYPASS_PMIC_READY; - if (cpu_is_imx6sl() || cpu_is_imx6sx()) + if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul()) val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; else val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; @@ -330,6 +352,10 @@ static int imx6q_suspend_finish(unsigned long val) * as we need to float DDR IO. */ local_flush_tlb_all(); + /* check if need to flush internal L2 cache */ + if (!((struct imx6_cpu_pm_info *) + suspend_ocram_base)->l2_base.vbase) + flush_cache_all(); imx6_suspend_in_ocram_fn(suspend_ocram_base); } @@ -470,6 +496,7 @@ static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata) suspend_ocram_base = __arm_ioremap_exec(ocram_pbase, MX6Q_SUSPEND_OCRAM_SIZE, false); + memset(suspend_ocram_base, 0, sizeof(*pm_info)); pm_info = suspend_ocram_base; pm_info->pbase = ocram_pbase; pm_info->resume_addr = virt_to_phys(v7_cpu_resume); @@ -505,11 +532,13 @@ static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata) goto gpc_map_failed; } - ret = imx6_pm_get_base(&pm_info->l2_base, "arm,pl310-cache"); - if (ret) { - pr_warn("%s: failed to get pl310-cache base %d!\n", - __func__, ret); - goto pl310_cache_map_failed; + if (socdata->pl310_compat) { + ret = imx6_pm_get_base(&pm_info->l2_base, socdata->pl310_compat); + if (ret) { + pr_warn("%s: failed to get pl310-cache base %d!\n", + __func__, ret); + goto pl310_cache_map_failed; + } } pm_info->ddr_type = imx_mmdc_get_ddr_type(); @@ -610,3 +639,8 @@ void __init imx6sx_pm_init(void) { imx6_pm_common_init(&imx6sx_pm_data); } + +void __init imx6ul_pm_init(void) +{ + imx6_pm_common_init(&imx6ul_pm_data); +} diff --git a/arch/arm/mach-imx/suspend-imx6.S b/arch/arm/mach-imx/suspend-imx6.S index b99987b023fa..76ee2ceec8d5 100644 --- a/arch/arm/mach-imx/suspend-imx6.S +++ b/arch/arm/mach-imx/suspend-imx6.S @@ -79,12 +79,15 @@ /* sync L2 cache to drain L2's buffers to DRAM. */ #ifdef CONFIG_CACHE_L2X0 ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] + teq r11, #0 + beq 6f mov r6, #0x0 str r6, [r11, #L2X0_CACHE_SYNC] 1: ldr r6, [r11, #L2X0_CACHE_SYNC] ands r6, r6, #0x1 bne 1b +6: #endif .endm diff --git a/arch/arm/mach-mediatek/Makefile b/arch/arm/mach-mediatek/Makefile index 43e619f56172..21164605b83f 100644 --- a/arch/arm/mach-mediatek/Makefile +++ b/arch/arm/mach-mediatek/Makefile @@ -1 +1,4 @@ +ifeq ($(CONFIG_SMP),y) +obj-$(CONFIG_ARCH_MEDIATEK) += platsmp.o +endif obj-$(CONFIG_ARCH_MEDIATEK) += mediatek.o diff --git a/arch/arm/mach-mediatek/mediatek.c b/arch/arm/mach-mediatek/mediatek.c index a9549005097e..d019a080a559 100644 --- a/arch/arm/mach-mediatek/mediatek.c +++ b/arch/arm/mach-mediatek/mediatek.c @@ -16,6 +16,32 @@ */ #include <linux/init.h> #include <asm/mach/arch.h> +#include <linux/of.h> +#include <linux/clk-provider.h> +#include <linux/clocksource.h> + + +#define GPT6_CON_MT65xx 0x10008060 +#define GPT_ENABLE 0x31 + +static void __init mediatek_timer_init(void) +{ + void __iomem *gpt_base; + + if (of_machine_is_compatible("mediatek,mt6589") || + of_machine_is_compatible("mediatek,mt8135") || + of_machine_is_compatible("mediatek,mt8127")) { + /* turn on GPT6 which ungates arch timer clocks */ + gpt_base = ioremap(GPT6_CON_MT65xx, 0x04); + + /* enable clock and set to free-run */ + writel(GPT_ENABLE, gpt_base); + iounmap(gpt_base); + } + + of_clk_init(NULL); + clocksource_probe(); +}; static const char * const mediatek_board_dt_compat[] = { "mediatek,mt6589", @@ -27,4 +53,5 @@ static const char * const mediatek_board_dt_compat[] = { DT_MACHINE_START(MEDIATEK_DT, "Mediatek Cortex-A7 (Device Tree)") .dt_compat = mediatek_board_dt_compat, + .init_time = mediatek_timer_init, MACHINE_END diff --git a/arch/arm/mach-mediatek/platsmp.c b/arch/arm/mach-mediatek/platsmp.c new file mode 100644 index 000000000000..8141f3f8afed --- /dev/null +++ b/arch/arm/mach-mediatek/platsmp.c @@ -0,0 +1,141 @@ +/* + * arch/arm/mach-mediatek/platsmp.c + * + * Copyright (c) 2014 Mediatek Inc. + * Author: Shunli Wang <shunli.wang@mediatek.com> + * Yingjoe Chen <yingjoe.chen@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/io.h> +#include <linux/memblock.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/string.h> +#include <linux/threads.h> + +#define MTK_MAX_CPU 8 +#define MTK_SMP_REG_SIZE 0x1000 + +struct mtk_smp_boot_info { + unsigned long smp_base; + unsigned int jump_reg; + unsigned int core_keys[MTK_MAX_CPU - 1]; + unsigned int core_regs[MTK_MAX_CPU - 1]; +}; + +static const struct mtk_smp_boot_info mtk_mt8135_tz_boot = { + 0x80002000, 0x3fc, + { 0x534c4131, 0x4c415332, 0x41534c33 }, + { 0x3f8, 0x3f8, 0x3f8 }, +}; + +static const struct mtk_smp_boot_info mtk_mt6589_boot = { + 0x10002000, 0x34, + { 0x534c4131, 0x4c415332, 0x41534c33 }, + { 0x38, 0x3c, 0x40 }, +}; + +static const struct of_device_id mtk_tz_smp_boot_infos[] __initconst = { + { .compatible = "mediatek,mt8135", .data = &mtk_mt8135_tz_boot }, + { .compatible = "mediatek,mt8127", .data = &mtk_mt8135_tz_boot }, +}; + +static const struct of_device_id mtk_smp_boot_infos[] __initconst = { + { .compatible = "mediatek,mt6589", .data = &mtk_mt6589_boot }, +}; + +static void __iomem *mtk_smp_base; +static const struct mtk_smp_boot_info *mtk_smp_info; + +static int mtk_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + if (!mtk_smp_base) + return -EINVAL; + + if (!mtk_smp_info->core_keys[cpu-1]) + return -EINVAL; + + writel_relaxed(mtk_smp_info->core_keys[cpu-1], + mtk_smp_base + mtk_smp_info->core_regs[cpu-1]); + + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + return 0; +} + +static void __init __mtk_smp_prepare_cpus(unsigned int max_cpus, int trustzone) +{ + int i, num; + const struct of_device_id *infos; + + if (trustzone) { + num = ARRAY_SIZE(mtk_tz_smp_boot_infos); + infos = mtk_tz_smp_boot_infos; + } else { + num = ARRAY_SIZE(mtk_smp_boot_infos); + infos = mtk_smp_boot_infos; + } + + /* Find smp boot info for this SoC */ + for (i = 0; i < num; i++) { + if (of_machine_is_compatible(infos[i].compatible)) { + mtk_smp_info = infos[i].data; + break; + } + } + + if (!mtk_smp_info) { + pr_err("%s: Device is not supported\n", __func__); + return; + } + + if (trustzone) { + /* smp_base(trustzone-bootinfo) is reserved by device tree */ + mtk_smp_base = phys_to_virt(mtk_smp_info->smp_base); + } else { + mtk_smp_base = ioremap(mtk_smp_info->smp_base, MTK_SMP_REG_SIZE); + if (!mtk_smp_base) { + pr_err("%s: Can't remap %lx\n", __func__, + mtk_smp_info->smp_base); + return; + } + } + + /* + * write the address of slave startup address into the system-wide + * jump register + */ + writel_relaxed(virt_to_phys(secondary_startup_arm), + mtk_smp_base + mtk_smp_info->jump_reg); +} + +static void __init mtk_tz_smp_prepare_cpus(unsigned int max_cpus) +{ + __mtk_smp_prepare_cpus(max_cpus, 1); +} + +static void __init mtk_smp_prepare_cpus(unsigned int max_cpus) +{ + __mtk_smp_prepare_cpus(max_cpus, 0); +} + +static struct smp_operations mt81xx_tz_smp_ops __initdata = { + .smp_prepare_cpus = mtk_tz_smp_prepare_cpus, + .smp_boot_secondary = mtk_boot_secondary, +}; +CPU_METHOD_OF_DECLARE(mt81xx_tz_smp, "mediatek,mt81xx-tz-smp", &mt81xx_tz_smp_ops); + +static struct smp_operations mt6589_smp_ops __initdata = { + .smp_prepare_cpus = mtk_smp_prepare_cpus, + .smp_boot_secondary = mtk_boot_secondary, +}; +CPU_METHOD_OF_DECLARE(mt6589_smp, "mediatek,mt6589-smp", &mt6589_smp_ops); diff --git a/arch/arm/mach-meson/Kconfig b/arch/arm/mach-meson/Kconfig index 0743e2059645..5d56f86ae1a4 100644 --- a/arch/arm/mach-meson/Kconfig +++ b/arch/arm/mach-meson/Kconfig @@ -19,4 +19,9 @@ config MACH_MESON8 default ARCH_MESON select MESON6_TIMER +config MACH_MESON8B + bool "Amlogic Meson8b SoCs support" + default ARCH_MESON + select MESON6_TIMER + endif diff --git a/arch/arm/mach-meson/meson.c b/arch/arm/mach-meson/meson.c index 5d6affe6a694..4e2357178625 100644 --- a/arch/arm/mach-meson/meson.c +++ b/arch/arm/mach-meson/meson.c @@ -19,6 +19,7 @@ static const char * const meson_common_board_compat[] = { "amlogic,meson6", "amlogic,meson8", + "amlogic,meson8b", NULL, }; diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 44eedf331ae7..55348ee5a352 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -40,6 +40,7 @@ unsigned long coherency_phys_base; void __iomem *coherency_base; static void __iomem *coherency_cpu_base; +static void __iomem *cpu_config_base; /* Coherency fabric registers */ #define IO_SYNC_BARRIER_CTL_OFFSET 0x0 @@ -65,6 +66,31 @@ static const struct of_device_id of_coherency_table[] = { int ll_enable_coherency(void); void ll_add_cpu_to_smp_group(void); +#define CPU_CONFIG_SHARED_L2 BIT(16) + +/* + * Disable the "Shared L2 Present" bit in CPU Configuration register + * on Armada XP. + * + * The "Shared L2 Present" bit affects the "level of coherence" value + * in the clidr CP15 register. Cache operation functions such as + * "flush all" and "invalidate all" operate on all the cache levels + * that included in the defined level of coherence. When HW I/O + * coherency is used, this bit causes unnecessary flushes of the L2 + * cache. + */ +static void armada_xp_clear_shared_l2(void) +{ + u32 reg; + + if (!cpu_config_base) + return; + + reg = readl(cpu_config_base); + reg &= ~CPU_CONFIG_SHARED_L2; + writel(reg, cpu_config_base); +} + static int mvebu_hwcc_notifier(struct notifier_block *nb, unsigned long event, void *__dev) { @@ -85,9 +111,24 @@ static struct notifier_block mvebu_hwcc_pci_nb = { .notifier_call = mvebu_hwcc_notifier, }; +static int armada_xp_clear_shared_l2_notifier_func(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) + armada_xp_clear_shared_l2(); + + return NOTIFY_OK; +} + +static struct notifier_block armada_xp_clear_shared_l2_notifier = { + .notifier_call = armada_xp_clear_shared_l2_notifier_func, + .priority = 100, +}; + static void __init armada_370_coherency_init(struct device_node *np) { struct resource res; + struct device_node *cpu_config_np; of_address_to_resource(np, 0, &res); coherency_phys_base = res.start; @@ -100,6 +141,23 @@ static void __init armada_370_coherency_init(struct device_node *np) sync_cache_w(&coherency_phys_base); coherency_base = of_iomap(np, 0); coherency_cpu_base = of_iomap(np, 1); + + cpu_config_np = of_find_compatible_node(NULL, NULL, + "marvell,armada-xp-cpu-config"); + if (!cpu_config_np) + goto exit; + + cpu_config_base = of_iomap(cpu_config_np, 0); + if (!cpu_config_base) { + of_node_put(cpu_config_np); + goto exit; + } + + of_node_put(cpu_config_np); + + register_cpu_notifier(&armada_xp_clear_shared_l2_notifier); + +exit: set_cpu_coherent(); } @@ -204,6 +262,8 @@ int set_cpu_coherent(void) pr_warn("Coherency fabric is not initialized\n"); return 1; } + + armada_xp_clear_shared_l2(); ll_add_cpu_to_smp_group(); return ll_enable_coherency(); } diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 9658b5a11e38..ed8fda4cd055 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -379,6 +379,16 @@ static struct notifier_block mvebu_v7_cpu_pm_notifier = { static struct platform_device mvebu_v7_cpuidle_device; +static int broken_idle(struct device_node *np) +{ + if (of_property_read_bool(np, "broken-idle")) { + pr_warn("CPU idle is currently broken: disabling\n"); + return 1; + } + + return 0; +} + static __init int armada_370_cpuidle_init(void) { struct device_node *np; @@ -387,7 +397,9 @@ static __init int armada_370_cpuidle_init(void) np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); if (!np) return -ENODEV; - of_node_put(np); + + if (broken_idle(np)) + goto end; /* * On Armada 370, there is "a slow exit process from the deep @@ -406,6 +418,8 @@ static __init int armada_370_cpuidle_init(void) mvebu_v7_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; mvebu_v7_cpuidle_device.name = "cpuidle-armada-370"; +end: + of_node_put(np); return 0; } @@ -422,6 +436,10 @@ static __init int armada_38x_cpuidle_init(void) "marvell,armada-380-coherency-fabric"); if (!np) return -ENODEV; + + if (broken_idle(np)) + goto end; + of_node_put(np); np = of_find_compatible_node(NULL, NULL, @@ -430,7 +448,6 @@ static __init int armada_38x_cpuidle_init(void) return -ENODEV; mpsoc_base = of_iomap(np, 0); BUG_ON(!mpsoc_base); - of_node_put(np); /* Set up reset mask when powering down the cpus */ reg = readl(mpsoc_base + MPCORE_RESET_CTL); @@ -450,6 +467,8 @@ static __init int armada_38x_cpuidle_init(void) mvebu_v7_cpuidle_device.dev.platform_data = armada_38x_cpu_suspend; mvebu_v7_cpuidle_device.name = "cpuidle-armada-38x"; +end: + of_node_put(np); return 0; } @@ -460,12 +479,16 @@ static __init int armada_xp_cpuidle_init(void) np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); if (!np) return -ENODEV; - of_node_put(np); + + if (broken_idle(np)) + goto end; mvebu_cpu_resume = armada_370_xp_cpu_resume; mvebu_v7_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; mvebu_v7_cpuidle_device.name = "cpuidle-armada-xp"; +end: + of_node_put(np); return 0; } diff --git a/arch/arm/mach-orion5x/Kconfig b/arch/arm/mach-orion5x/Kconfig index 08d2be2ea41f..66f1c952c048 100644 --- a/arch/arm/mach-orion5x/Kconfig +++ b/arch/arm/mach-orion5x/Kconfig @@ -45,6 +45,7 @@ config MACH_KUROBOX_PRO config MACH_DNS323 bool "D-Link DNS-323" + select GENERIC_NET_UTILS select I2C_BOARDINFO help Say 'Y' here if you want your kernel to support the @@ -52,6 +53,7 @@ config MACH_DNS323 config MACH_TS209 bool "QNAP TS-109/TS-209" + select GENERIC_NET_UTILS help Say 'Y' here if you want your kernel to support the QNAP TS-109/TS-209 platform. @@ -93,6 +95,7 @@ config MACH_LINKSTATION_LS_HGL config MACH_TS409 bool "QNAP TS-409" + select GENERIC_NET_UTILS help Say 'Y' here if you want your kernel to support the QNAP TS-409 platform. diff --git a/arch/arm/mach-orion5x/dns323-setup.c b/arch/arm/mach-orion5x/dns323-setup.c index f267e58a8283..bc279a853075 100644 --- a/arch/arm/mach-orion5x/dns323-setup.c +++ b/arch/arm/mach-orion5x/dns323-setup.c @@ -173,42 +173,10 @@ static struct mv643xx_eth_platform_data dns323_eth_data = { .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; -/* dns323_parse_hex_*() taken from tsx09-common.c; should a common copy of these - * functions be kept somewhere? - */ -static int __init dns323_parse_hex_nibble(char n) -{ - if (n >= '0' && n <= '9') - return n - '0'; - - if (n >= 'A' && n <= 'F') - return n - 'A' + 10; - - if (n >= 'a' && n <= 'f') - return n - 'a' + 10; - - return -1; -} - -static int __init dns323_parse_hex_byte(const char *b) -{ - int hi; - int lo; - - hi = dns323_parse_hex_nibble(b[0]); - lo = dns323_parse_hex_nibble(b[1]); - - if (hi < 0 || lo < 0) - return -1; - - return (hi << 4) | lo; -} - static int __init dns323_read_mac_addr(void) { u_int8_t addr[6]; - int i; - char *mac_page; + void __iomem *mac_page; /* MAC address is stored as a regular ol' string in /dev/mtdblock4 * (0x007d0000-0x00800000) starting at offset 196480 (0x2ff80). @@ -217,23 +185,8 @@ static int __init dns323_read_mac_addr(void) if (!mac_page) return -ENOMEM; - /* Sanity check the string we're looking at */ - for (i = 0; i < 5; i++) { - if (*(mac_page + (i * 3) + 2) != ':') { - goto error_fail; - } - } - - for (i = 0; i < 6; i++) { - int byte; - - byte = dns323_parse_hex_byte(mac_page + (i * 3)); - if (byte < 0) { - goto error_fail; - } - - addr[i] = byte; - } + if (!mac_pton((__force const char *) mac_page, addr)) + goto error_fail; iounmap(mac_page); printk("DNS-323: Found ethernet MAC address: %pM\n", addr); diff --git a/arch/arm/mach-orion5x/tsx09-common.c b/arch/arm/mach-orion5x/tsx09-common.c index 24b2959719fa..d42e006597c7 100644 --- a/arch/arm/mach-orion5x/tsx09-common.c +++ b/arch/arm/mach-orion5x/tsx09-common.c @@ -53,53 +53,12 @@ struct mv643xx_eth_platform_data qnap_tsx09_eth_data = { .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; -static int __init qnap_tsx09_parse_hex_nibble(char n) -{ - if (n >= '0' && n <= '9') - return n - '0'; - - if (n >= 'A' && n <= 'F') - return n - 'A' + 10; - - if (n >= 'a' && n <= 'f') - return n - 'a' + 10; - - return -1; -} - -static int __init qnap_tsx09_parse_hex_byte(const char *b) -{ - int hi; - int lo; - - hi = qnap_tsx09_parse_hex_nibble(b[0]); - lo = qnap_tsx09_parse_hex_nibble(b[1]); - - if (hi < 0 || lo < 0) - return -1; - - return (hi << 4) | lo; -} - static int __init qnap_tsx09_check_mac_addr(const char *addr_str) { u_int8_t addr[6]; - int i; - for (i = 0; i < 6; i++) { - int byte; - - /* - * Enforce "xx:xx:xx:xx:xx:xx\n" format. - */ - if (addr_str[(i * 3) + 2] != ((i < 5) ? ':' : '\n')) - return -1; - - byte = qnap_tsx09_parse_hex_byte(addr_str + (i * 3)); - if (byte < 0) - return -1; - addr[i] = byte; - } + if (!mac_pton(addr_str, addr)) + return -1; printk(KERN_INFO "tsx09: found ethernet mac address %pM\n", addr); @@ -118,12 +77,12 @@ void __init qnap_tsx09_find_mac_addr(u32 mem_base, u32 size) unsigned long addr; for (addr = mem_base; addr < (mem_base + size); addr += 1024) { - char *nor_page; + void __iomem *nor_page; int ret = 0; nor_page = ioremap(addr, 1024); if (nor_page != NULL) { - ret = qnap_tsx09_check_mac_addr(nor_page); + ret = qnap_tsx09_check_mac_addr((__force const char *)nor_page); iounmap(nor_page); } diff --git a/arch/arm/mach-s3c24xx/mach-h1940.c b/arch/arm/mach-s3c24xx/mach-h1940.c index d40d4f5244c6..9f54300df4b3 100644 --- a/arch/arm/mach-s3c24xx/mach-h1940.c +++ b/arch/arm/mach-s3c24xx/mach-h1940.c @@ -25,6 +25,7 @@ #include <linux/gpio.h> #include <linux/input.h> #include <linux/gpio_keys.h> +#include <linux/pwm.h> #include <linux/pwm_backlight.h> #include <linux/i2c.h> #include <linux/leds.h> @@ -469,6 +470,11 @@ static struct s3c24xx_mci_pdata h1940_mmc_cfg __initdata = { .ocr_avail = MMC_VDD_32_33, }; +static struct pwm_lookup h1940_pwm_lookup[] = { + PWM_LOOKUP("samsung-pwm", 0, "pwm-backlight", NULL, 36296, + PWM_POLARITY_NORMAL), +}; + static int h1940_backlight_init(struct device *dev) { gpio_request(S3C2410_GPB(0), "Backlight"); @@ -503,11 +509,8 @@ static void h1940_backlight_exit(struct device *dev) static struct platform_pwm_backlight_data backlight_data = { - .pwm_id = 0, .max_brightness = 100, .dft_brightness = 50, - /* tcnt = 0x31 */ - .pwm_period_ns = 36296, .enable_gpio = -1, .init = h1940_backlight_init, .notify = h1940_backlight_notify, @@ -725,6 +728,7 @@ static void __init h1940_init(void) gpio_request(H1940_LATCH_SD_POWER, "SD power"); gpio_direction_output(H1940_LATCH_SD_POWER, 0); + pwm_add_table(h1940_pwm_lookup, ARRAY_SIZE(h1940_pwm_lookup)); platform_add_devices(h1940_devices, ARRAY_SIZE(h1940_devices)); gpio_request(S3C2410_GPA(1), "Red LED blink"); diff --git a/arch/arm/mach-s3c24xx/mach-rx1950.c b/arch/arm/mach-s3c24xx/mach-rx1950.c index 1d35ff375a01..774c982a7b7e 100644 --- a/arch/arm/mach-s3c24xx/mach-rx1950.c +++ b/arch/arm/mach-s3c24xx/mach-rx1950.c @@ -375,6 +375,11 @@ static struct s3c2410fb_mach_info rx1950_lcd_cfg = { }; +static struct pwm_lookup rx1950_pwm_lookup[] = { + PWM_LOOKUP("samsung-pwm", 0, "pwm-backlight.0", NULL, 48000, + PWM_POLARITY_NORMAL), +}; + static struct pwm_device *lcd_pwm; static void rx1950_lcd_power(int enable) @@ -520,10 +525,8 @@ static int rx1950_backlight_notify(struct device *dev, int brightness) } static struct platform_pwm_backlight_data rx1950_backlight_data = { - .pwm_id = 0, .max_brightness = 24, .dft_brightness = 4, - .pwm_period_ns = 48000, .enable_gpio = -1, .init = rx1950_backlight_init, .notify = rx1950_backlight_notify, @@ -792,6 +795,7 @@ static void __init rx1950_init_machine(void) gpio_direction_output(S3C2410_GPA(4), 0); gpio_direction_output(S3C2410_GPJ(6), 0); + pwm_add_table(rx1950_pwm_lookup, ARRAY_SIZE(rx1950_pwm_lookup)); platform_add_devices(rx1950_devices, ARRAY_SIZE(rx1950_devices)); i2c_register_board_info(0, rx1950_i2c_devices, diff --git a/arch/arm/mach-s3c64xx/dev-backlight.c b/arch/arm/mach-s3c64xx/dev-backlight.c index 38c323e68e3f..e62e789f9aee 100644 --- a/arch/arm/mach-s3c64xx/dev-backlight.c +++ b/arch/arm/mach-s3c64xx/dev-backlight.c @@ -69,7 +69,6 @@ static struct samsung_bl_drvdata samsung_dfl_bl_data __initdata = { .plat_data = { .max_brightness = 255, .dft_brightness = 255, - .pwm_period_ns = 78770, .enable_gpio = -1, .init = samsung_bl_init, .exit = samsung_bl_exit, @@ -111,7 +110,6 @@ void __init samsung_bl_set(struct samsung_bl_gpio_info *gpio_info, samsung_bl_data = &samsung_bl_drvdata->plat_data; /* Copy board specific data provided by user */ - samsung_bl_data->pwm_id = bl_data->pwm_id; samsung_bl_device->dev.parent = &samsung_device_pwm.dev; if (bl_data->max_brightness) @@ -120,8 +118,6 @@ void __init samsung_bl_set(struct samsung_bl_gpio_info *gpio_info, samsung_bl_data->dft_brightness = bl_data->dft_brightness; if (bl_data->lth_brightness) samsung_bl_data->lth_brightness = bl_data->lth_brightness; - if (bl_data->pwm_period_ns) - samsung_bl_data->pwm_period_ns = bl_data->pwm_period_ns; if (bl_data->enable_gpio >= 0) samsung_bl_data->enable_gpio = bl_data->enable_gpio; if (bl_data->init) diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 14bd9ae3f476..f776adcdaee8 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -25,6 +25,7 @@ #include <linux/mmc/host.h> #include <linux/regulator/machine.h> #include <linux/regulator/fixed.h> +#include <linux/pwm.h> #include <linux/pwm_backlight.h> #include <linux/dm9000.h> #include <linux/gpio_keys.h> @@ -108,11 +109,14 @@ static struct s3c2410_uartcfg crag6410_uartcfgs[] __initdata = { }, }; +static struct pwm_lookup crag6410_pwm_lookup[] = { + PWM_LOOKUP("samsung-pwm", 0, "pwm-backlight", NULL, 100000, + PWM_POLARITY_NORMAL), +}; + static struct platform_pwm_backlight_data crag6410_backlight_data = { - .pwm_id = 0, .max_brightness = 1000, .dft_brightness = 600, - .pwm_period_ns = 100000, /* about 1kHz */ .enable_gpio = -1, }; @@ -843,6 +847,7 @@ static void __init crag6410_machine_init(void) samsung_keypad_set_platdata(&crag6410_keypad_data); s3c64xx_spi0_set_platdata(NULL, 0, 2); + pwm_add_table(crag6410_pwm_lookup, ARRAY_SIZE(crag6410_pwm_lookup)); platform_add_devices(crag6410_devices, ARRAY_SIZE(crag6410_devices)); gpio_led_register_device(-1, &gpio_leds_pdata); diff --git a/arch/arm/mach-s3c64xx/mach-hmt.c b/arch/arm/mach-s3c64xx/mach-hmt.c index e4b087c58ee6..816b39d1e6d1 100644 --- a/arch/arm/mach-s3c64xx/mach-hmt.c +++ b/arch/arm/mach-s3c64xx/mach-hmt.c @@ -19,6 +19,7 @@ #include <linux/gpio.h> #include <linux/delay.h> #include <linux/leds.h> +#include <linux/pwm.h> #include <linux/pwm_backlight.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -73,6 +74,11 @@ static struct s3c2410_uartcfg hmt_uartcfgs[] __initdata = { }, }; +static struct pwm_lookup hmt_pwm_lookup[] = { + PWM_LOOKUP("samsung-pwm", 1, "pwm-backlight.0", NULL, + 1000000000 / (100 * 256 * 20), PWM_POLARITY_NORMAL), +}; + static int hmt_bl_init(struct device *dev) { int ret; @@ -110,10 +116,8 @@ static void hmt_bl_exit(struct device *dev) } static struct platform_pwm_backlight_data hmt_backlight_data = { - .pwm_id = 1, .max_brightness = 100 * 256, .dft_brightness = 40 * 256, - .pwm_period_ns = 1000000000 / (100 * 256 * 20), .enable_gpio = -1, .init = hmt_bl_init, .notify = hmt_bl_notify, @@ -268,6 +272,7 @@ static void __init hmt_machine_init(void) gpio_request(S3C64XX_GPF(13), "usb power"); gpio_direction_output(S3C64XX_GPF(13), 1); + pwm_add_table(hmt_pwm_lookup, ARRAY_SIZE(hmt_pwm_lookup)); platform_add_devices(hmt_devices, ARRAY_SIZE(hmt_devices)); } diff --git a/arch/arm/mach-s3c64xx/mach-smartq.c b/arch/arm/mach-s3c64xx/mach-smartq.c index 719843dca510..acdfb5fac40f 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq.c +++ b/arch/arm/mach-s3c64xx/mach-smartq.c @@ -14,6 +14,7 @@ #include <linux/gpio.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/pwm.h> #include <linux/pwm_backlight.h> #include <linux/serial_core.h> #include <linux/serial_s3c.h> @@ -139,6 +140,11 @@ static struct platform_device smartq_usb_otg_vbus_dev = { .dev.platform_data = &smartq_usb_otg_vbus_pdata, }; +static struct pwm_lookup smartq_pwm_lookup[] = { + PWM_LOOKUP("samsung-pwm", 1, "pwm-backlight.0", NULL, + 1000000000 / (1000 * 20), PWM_POLARITY_NORMAL), +}; + static int smartq_bl_init(struct device *dev) { s3c_gpio_cfgpin(S3C64XX_GPF(15), S3C_GPIO_SFN(2)); @@ -147,10 +153,8 @@ static int smartq_bl_init(struct device *dev) } static struct platform_pwm_backlight_data smartq_backlight_data = { - .pwm_id = 1, .max_brightness = 1000, .dft_brightness = 600, - .pwm_period_ns = 1000000000 / (1000 * 20), .enable_gpio = -1, .init = smartq_bl_init, }; @@ -396,5 +400,6 @@ void __init smartq_machine_init(void) WARN_ON(smartq_usb_host_init()); WARN_ON(smartq_wifi_init()); + pwm_add_table(smartq_pwm_lookup, ARRAY_SIZE(smartq_pwm_lookup)); platform_add_devices(smartq_devices, ARRAY_SIZE(smartq_devices)); } diff --git a/arch/arm/mach-s3c64xx/mach-smdk6410.c b/arch/arm/mach-s3c64xx/mach-smdk6410.c index 286c9bd676e1..30fd27853072 100644 --- a/arch/arm/mach-s3c64xx/mach-smdk6410.c +++ b/arch/arm/mach-s3c64xx/mach-smdk6410.c @@ -30,6 +30,7 @@ #include <linux/smsc911x.h> #include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> +#include <linux/pwm.h> #include <linux/pwm_backlight.h> #include <linux/platform_data/s3c-hsotg.h> @@ -623,8 +624,12 @@ static struct samsung_bl_gpio_info smdk6410_bl_gpio_info = { .func = S3C_GPIO_SFN(2), }; +static struct pwm_lookup smdk6410_pwm_lookup[] = { + PWM_LOOKUP("samsung-pwm", 1, "pwm-backlight.0", NULL, 78770, + PWM_POLARITY_NORMAL), +}; + static struct platform_pwm_backlight_data smdk6410_bl_data = { - .pwm_id = 1, .enable_gpio = -1, }; @@ -695,6 +700,7 @@ static void __init smdk6410_machine_init(void) platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices)); + pwm_add_table(smdk6410_pwm_lookup, ARRAY_SIZE(smdk6410_pwm_lookup)); samsung_bl_set(&smdk6410_bl_gpio_info, &smdk6410_bl_data); } diff --git a/arch/arm/mach-shmobile/pm-rmobile.c b/arch/arm/mach-shmobile/pm-rmobile.c index 424155b0cfe9..46d0a1ddce75 100644 --- a/arch/arm/mach-shmobile/pm-rmobile.c +++ b/arch/arm/mach-shmobile/pm-rmobile.c @@ -12,6 +12,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ +#include <linux/clk/shmobile.h> #include <linux/console.h> #include <linux/delay.h> #include <linux/of.h> @@ -124,36 +125,6 @@ static bool rmobile_pd_active_wakeup(struct device *dev) return true; } -static int rmobile_pd_attach_dev(struct generic_pm_domain *domain, - struct device *dev) -{ - int error; - - error = pm_clk_create(dev); - if (error) { - dev_err(dev, "pm_clk_create failed %d\n", error); - return error; - } - - error = pm_clk_add(dev, NULL); - if (error) { - dev_err(dev, "pm_clk_add failed %d\n", error); - goto fail; - } - - return 0; - -fail: - pm_clk_destroy(dev); - return error; -} - -static void rmobile_pd_detach_dev(struct generic_pm_domain *domain, - struct device *dev) -{ - pm_clk_destroy(dev); -} - static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) { struct generic_pm_domain *genpd = &rmobile_pd->genpd; @@ -164,8 +135,8 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) genpd->dev_ops.active_wakeup = rmobile_pd_active_wakeup; genpd->power_off = rmobile_pd_power_down; genpd->power_on = rmobile_pd_power_up; - genpd->attach_dev = rmobile_pd_attach_dev; - genpd->detach_dev = rmobile_pd_detach_dev; + genpd->attach_dev = cpg_mstp_attach_dev; + genpd->detach_dev = cpg_mstp_detach_dev; __rmobile_pd_power_up(rmobile_pd, false); } diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c index 223c9e99380d..c2be98f38e73 100644 --- a/arch/arm/mach-sunxi/sunxi.c +++ b/arch/arm/mach-sunxi/sunxi.c @@ -26,10 +26,11 @@ static const char * const sunxi_board_dt_compat[] = { "allwinner,sun4i-a10", "allwinner,sun5i-a10s", "allwinner,sun5i-a13", + "allwinner,sun5i-r8", NULL, }; -DT_MACHINE_START(SUNXI_DT, "Allwinner A1X (Device Tree)") +DT_MACHINE_START(SUNXI_DT, "Allwinner sun4i/sun5i Families") .dt_compat = sunxi_board_dt_compat, .init_late = sunxi_dt_cpufreq_init, MACHINE_END diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c index fbe74c6806f3..49d1110cff53 100644 --- a/arch/arm/mach-tegra/board-paz00.c +++ b/arch/arm/mach-tegra/board-paz00.c @@ -39,8 +39,8 @@ static struct platform_device wifi_rfkill_device = { static struct gpiod_lookup_table wifi_gpio_lookup = { .dev_id = "rfkill_gpio", .table = { - GPIO_LOOKUP_IDX("tegra-gpio", 25, NULL, 0, 0), - GPIO_LOOKUP_IDX("tegra-gpio", 85, NULL, 1, 0), + GPIO_LOOKUP("tegra-gpio", 25, "reset", 0), + GPIO_LOOKUP("tegra-gpio", 85, "shutdown", 0), { }, }, }; diff --git a/arch/arm/mach-uniphier/Makefile b/arch/arm/mach-uniphier/Makefile index 60bd2265f753..1233f9b610bc 100644 --- a/arch/arm/mach-uniphier/Makefile +++ b/arch/arm/mach-uniphier/Makefile @@ -1,2 +1,2 @@ obj-y := uniphier.o -obj-$(CONFIG_SMP) += platsmp.o +obj-$(CONFIG_SMP) += platsmp.o headsmp.o diff --git a/arch/arm/mach-uniphier/headsmp.S b/arch/arm/mach-uniphier/headsmp.S new file mode 100644 index 000000000000..c819dff84546 --- /dev/null +++ b/arch/arm/mach-uniphier/headsmp.S @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/cp15.h> + +ENTRY(uniphier_smp_trampoline) +ARM_BE8(setend be) @ ensure we are in BE8 mode + mrc p15, 0, r0, c0, c0, 5 @ MPIDR (Multiprocessor Affinity Reg) + and r2, r0, #0x3 @ CPU ID + ldr r1, uniphier_smp_trampoline_jump + ldr r3, uniphier_smp_trampoline_poll_addr + mrc p15, 0, r0, c1, c0, 0 @ SCTLR (System Control Register) + orr r0, r0, #CR_I @ Enable ICache + bic r0, r0, #(CR_C | CR_M) @ Disable MMU and Dcache + mcr p15, 0, r0, c1, c0, 0 + b 1f @ cache the following 5 instructions +0: wfe +1: ldr r0, [r3] + cmp r0, r2 + bxeq r1 @ branch to secondary_startup + b 0b + .globl uniphier_smp_trampoline_jump +uniphier_smp_trampoline_jump: + .word 0 @ set virt_to_phys(secondary_startup) + .globl uniphier_smp_trampoline_poll_addr +uniphier_smp_trampoline_poll_addr: + .word 0 @ set CPU ID to be kicked to this reg + .globl uniphier_smp_trampoline_end +uniphier_smp_trampoline_end: +ENDPROC(uniphier_smp_trampoline) diff --git a/arch/arm/mach-uniphier/platsmp.c b/arch/arm/mach-uniphier/platsmp.c index 4b784f721135..f0577664611c 100644 --- a/arch/arm/mach-uniphier/platsmp.c +++ b/arch/arm/mach-uniphier/platsmp.c @@ -12,73 +12,198 @@ * GNU General Public License for more details. */ -#include <linux/sizes.h> -#include <linux/compiler.h> +#define pr_fmt(fmt) "uniphier: " fmt + #include <linux/init.h> #include <linux/io.h> -#include <linux/regmap.h> -#include <linux/mfd/syscon.h> +#include <linux/ioport.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/sizes.h> +#include <asm/cacheflush.h> +#include <asm/hardware/cache-uniphier.h> +#include <asm/pgtable.h> #include <asm/smp.h> #include <asm/smp_scu.h> -static struct regmap *sbcm_regmap; +/* + * The secondary CPUs check this register from the boot ROM for the jump + * destination. After that, it can be reused as a scratch register. + */ +#define UNIPHIER_SBC_ROM_BOOT_RSV2 0x1208 -static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus) +static void __iomem *uniphier_smp_rom_boot_rsv2; +static unsigned int uniphier_smp_max_cpus; + +extern char uniphier_smp_trampoline; +extern char uniphier_smp_trampoline_jump; +extern char uniphier_smp_trampoline_poll_addr; +extern char uniphier_smp_trampoline_end; + +/* + * Copy trampoline code to the tail of the 1st section of the page table used + * in the boot ROM. This area is directly accessible by the secondary CPUs + * for all the UniPhier SoCs. + */ +static const phys_addr_t uniphier_smp_trampoline_dest_end = SECTION_SIZE; +static phys_addr_t uniphier_smp_trampoline_dest; + +static int __init uniphier_smp_copy_trampoline(phys_addr_t poll_addr) { - static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 }; - unsigned long scu_base_phys = 0; - void __iomem *scu_base; + size_t trmp_size; + static void __iomem *trmp_base; - sbcm_regmap = syscon_regmap_lookup_by_compatible( - "socionext,uniphier-system-bus-controller-misc"); - if (IS_ERR(sbcm_regmap)) { - pr_err("failed to regmap system-bus-controller-misc\n"); - goto err; + if (!uniphier_cache_l2_is_enabled()) { + pr_warn("outer cache is needed for SMP, but not enabled\n"); + return -ENODEV; } + uniphier_cache_l2_set_locked_ways(1); + + outer_flush_all(); + + trmp_size = &uniphier_smp_trampoline_end - &uniphier_smp_trampoline; + uniphier_smp_trampoline_dest = uniphier_smp_trampoline_dest_end - + trmp_size; + + uniphier_cache_l2_touch_range(uniphier_smp_trampoline_dest, + uniphier_smp_trampoline_dest_end); + + trmp_base = ioremap_cache(uniphier_smp_trampoline_dest, trmp_size); + if (!trmp_base) { + pr_err("failed to map trampoline destination area\n"); + return -ENOMEM; + } + + memcpy(trmp_base, &uniphier_smp_trampoline, trmp_size); + + writel(virt_to_phys(secondary_startup), + trmp_base + (&uniphier_smp_trampoline_jump - + &uniphier_smp_trampoline)); + + writel(poll_addr, trmp_base + (&uniphier_smp_trampoline_poll_addr - + &uniphier_smp_trampoline)); + + flush_cache_all(); /* flush out trampoline code to outer cache */ + + iounmap(trmp_base); + + return 0; +} + +static int __init uniphier_smp_prepare_trampoline(unsigned int max_cpus) +{ + struct device_node *np; + struct resource res; + phys_addr_t rom_rsv2_phys; + int ret; + + np = of_find_compatible_node(NULL, NULL, + "socionext,uniphier-system-bus-controller"); + ret = of_address_to_resource(np, 1, &res); + if (ret) { + pr_err("failed to get resource of system-bus-controller\n"); + return ret; + } + + rom_rsv2_phys = res.start + UNIPHIER_SBC_ROM_BOOT_RSV2; + + ret = uniphier_smp_copy_trampoline(rom_rsv2_phys); + if (ret) + return ret; + + uniphier_smp_rom_boot_rsv2 = ioremap(rom_rsv2_phys, sizeof(SZ_4)); + if (!uniphier_smp_rom_boot_rsv2) { + pr_err("failed to map ROM_BOOT_RSV2 register\n"); + return -ENOMEM; + } + + writel(uniphier_smp_trampoline_dest, uniphier_smp_rom_boot_rsv2); + asm("sev"); /* Bring up all secondary CPUs to the trampoline code */ + + uniphier_smp_max_cpus = max_cpus; /* save for later use */ + + return 0; +} + +static void __init uniphier_smp_unprepare_trampoline(void) +{ + iounmap(uniphier_smp_rom_boot_rsv2); + + if (uniphier_smp_trampoline_dest) + outer_inv_range(uniphier_smp_trampoline_dest, + uniphier_smp_trampoline_dest_end); + + uniphier_cache_l2_set_locked_ways(0); +} + +static int __init uniphier_smp_enable_scu(void) +{ + unsigned long scu_base_phys = 0; + void __iomem *scu_base; + if (scu_a9_has_base()) scu_base_phys = scu_a9_get_base(); if (!scu_base_phys) { pr_err("failed to get scu base\n"); - goto err; + return -ENODEV; } scu_base = ioremap(scu_base_phys, SZ_128); if (!scu_base) { - pr_err("failed to remap scu base (0x%08lx)\n", scu_base_phys); - goto err; + pr_err("failed to map scu base\n"); + return -ENOMEM; } scu_enable(scu_base); iounmap(scu_base); + return 0; +} + +static void __init uniphier_smp_prepare_cpus(unsigned int max_cpus) +{ + static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 }; + int ret; + + ret = uniphier_smp_prepare_trampoline(max_cpus); + if (ret) + goto err; + + ret = uniphier_smp_enable_scu(); + if (ret) + goto err; + return; err: pr_warn("disabling SMP\n"); init_cpu_present(&only_cpu_0); - sbcm_regmap = NULL; + uniphier_smp_unprepare_trampoline(); } -static int uniphier_boot_secondary(unsigned int cpu, - struct task_struct *idle) +static int __init uniphier_smp_boot_secondary(unsigned int cpu, + struct task_struct *idle) { - int ret; + if (WARN_ON_ONCE(!uniphier_smp_rom_boot_rsv2)) + return -EFAULT; - if (!sbcm_regmap) - return -ENODEV; + writel(cpu, uniphier_smp_rom_boot_rsv2); + readl(uniphier_smp_rom_boot_rsv2); /* relax */ - ret = regmap_write(sbcm_regmap, 0x1208, - virt_to_phys(secondary_startup)); - if (!ret) - asm("sev"); /* wake up secondary CPU */ + asm("sev"); /* wake up secondary CPUs sleeping in the trampoline */ + + if (cpu == uniphier_smp_max_cpus - 1) { + /* clean up resources if this is the last CPU */ + uniphier_smp_unprepare_trampoline(); + } - return ret; + return 0; } -struct smp_operations uniphier_smp_ops __initdata = { +static struct smp_operations uniphier_smp_ops __initdata = { .smp_prepare_cpus = uniphier_smp_prepare_cpus, - .smp_boot_secondary = uniphier_boot_secondary, + .smp_boot_secondary = uniphier_smp_boot_secondary, }; CPU_METHOD_OF_DECLARE(uniphier_smp, "socionext,uniphier-smp", &uniphier_smp_ops); diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index c21941349b3e..41218867a9a6 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -974,6 +974,16 @@ config CACHE_TAUROS2 This option enables the Tauros2 L2 cache controller (as found on PJ1/PJ4). +config CACHE_UNIPHIER + bool "Enable the UniPhier outer cache controller" + depends on ARCH_UNIPHIER + default y + select OUTER_CACHE + select OUTER_CACHE_SYNC + help + This option enables the UniPhier outer cache (system cache) + controller. + config CACHE_XSC3L2 bool "Enable the L2 cache on XScale3" depends on CPU_XSC3 diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 57c8df500e8c..7f76d96ce546 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -103,3 +103,4 @@ obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o l2c-l2x0-resume.o obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o obj-$(CONFIG_CACHE_TAUROS2) += cache-tauros2.o +obj-$(CONFIG_CACHE_UNIPHIER) += cache-uniphier.o diff --git a/arch/arm/mm/cache-uniphier.c b/arch/arm/mm/cache-uniphier.c new file mode 100644 index 000000000000..0502ba17a3ab --- /dev/null +++ b/arch/arm/mm/cache-uniphier.c @@ -0,0 +1,555 @@ +/* + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "uniphier: " fmt + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/log2.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <asm/hardware/cache-uniphier.h> +#include <asm/outercache.h> + +/* control registers */ +#define UNIPHIER_SSCC 0x0 /* Control Register */ +#define UNIPHIER_SSCC_BST BIT(20) /* UCWG burst read */ +#define UNIPHIER_SSCC_ACT BIT(19) /* Inst-Data separate */ +#define UNIPHIER_SSCC_WTG BIT(18) /* WT gathering on */ +#define UNIPHIER_SSCC_PRD BIT(17) /* enable pre-fetch */ +#define UNIPHIER_SSCC_ON BIT(0) /* enable cache */ +#define UNIPHIER_SSCLPDAWCR 0x30 /* Unified/Data Active Way Control */ +#define UNIPHIER_SSCLPIAWCR 0x34 /* Instruction Active Way Control */ + +/* revision registers */ +#define UNIPHIER_SSCID 0x0 /* ID Register */ + +/* operation registers */ +#define UNIPHIER_SSCOPE 0x244 /* Cache Operation Primitive Entry */ +#define UNIPHIER_SSCOPE_CM_INV 0x0 /* invalidate */ +#define UNIPHIER_SSCOPE_CM_CLEAN 0x1 /* clean */ +#define UNIPHIER_SSCOPE_CM_FLUSH 0x2 /* flush */ +#define UNIPHIER_SSCOPE_CM_SYNC 0x8 /* sync (drain bufs) */ +#define UNIPHIER_SSCOPE_CM_FLUSH_PREFETCH 0x9 /* flush p-fetch buf */ +#define UNIPHIER_SSCOQM 0x248 /* Cache Operation Queue Mode */ +#define UNIPHIER_SSCOQM_TID_MASK (0x3 << 21) +#define UNIPHIER_SSCOQM_TID_LRU_DATA (0x0 << 21) +#define UNIPHIER_SSCOQM_TID_LRU_INST (0x1 << 21) +#define UNIPHIER_SSCOQM_TID_WAY (0x2 << 21) +#define UNIPHIER_SSCOQM_S_MASK (0x3 << 17) +#define UNIPHIER_SSCOQM_S_RANGE (0x0 << 17) +#define UNIPHIER_SSCOQM_S_ALL (0x1 << 17) +#define UNIPHIER_SSCOQM_S_WAY (0x2 << 17) +#define UNIPHIER_SSCOQM_CE BIT(15) /* notify completion */ +#define UNIPHIER_SSCOQM_CM_INV 0x0 /* invalidate */ +#define UNIPHIER_SSCOQM_CM_CLEAN 0x1 /* clean */ +#define UNIPHIER_SSCOQM_CM_FLUSH 0x2 /* flush */ +#define UNIPHIER_SSCOQM_CM_PREFETCH 0x3 /* prefetch to cache */ +#define UNIPHIER_SSCOQM_CM_PREFETCH_BUF 0x4 /* prefetch to pf-buf */ +#define UNIPHIER_SSCOQM_CM_TOUCH 0x5 /* touch */ +#define UNIPHIER_SSCOQM_CM_TOUCH_ZERO 0x6 /* touch to zero */ +#define UNIPHIER_SSCOQM_CM_TOUCH_DIRTY 0x7 /* touch with dirty */ +#define UNIPHIER_SSCOQAD 0x24c /* Cache Operation Queue Address */ +#define UNIPHIER_SSCOQSZ 0x250 /* Cache Operation Queue Size */ +#define UNIPHIER_SSCOQMASK 0x254 /* Cache Operation Queue Address Mask */ +#define UNIPHIER_SSCOQWN 0x258 /* Cache Operation Queue Way Number */ +#define UNIPHIER_SSCOPPQSEF 0x25c /* Cache Operation Queue Set Complete*/ +#define UNIPHIER_SSCOPPQSEF_FE BIT(1) +#define UNIPHIER_SSCOPPQSEF_OE BIT(0) +#define UNIPHIER_SSCOLPQS 0x260 /* Cache Operation Queue Status */ +#define UNIPHIER_SSCOLPQS_EF BIT(2) +#define UNIPHIER_SSCOLPQS_EST BIT(1) +#define UNIPHIER_SSCOLPQS_QST BIT(0) + +/* Is the touch/pre-fetch destination specified by ways? */ +#define UNIPHIER_SSCOQM_TID_IS_WAY(op) \ + ((op & UNIPHIER_SSCOQM_TID_MASK) == UNIPHIER_SSCOQM_TID_WAY) +/* Is the operation region specified by address range? */ +#define UNIPHIER_SSCOQM_S_IS_RANGE(op) \ + ((op & UNIPHIER_SSCOQM_S_MASK) == UNIPHIER_SSCOQM_S_RANGE) + +/** + * uniphier_cache_data - UniPhier outer cache specific data + * + * @ctrl_base: virtual base address of control registers + * @rev_base: virtual base address of revision registers + * @op_base: virtual base address of operation registers + * @way_present_mask: each bit specifies if the way is present + * @way_locked_mask: each bit specifies if the way is locked + * @nsets: number of associativity sets + * @line_size: line size in bytes + * @range_op_max_size: max size that can be handled by a single range operation + * @list: list node to include this level in the whole cache hierarchy + */ +struct uniphier_cache_data { + void __iomem *ctrl_base; + void __iomem *rev_base; + void __iomem *op_base; + u32 way_present_mask; + u32 way_locked_mask; + u32 nsets; + u32 line_size; + u32 range_op_max_size; + struct list_head list; +}; + +/* + * List of the whole outer cache hierarchy. This list is only modified during + * the early boot stage, so no mutex is taken for the access to the list. + */ +static LIST_HEAD(uniphier_cache_list); + +/** + * __uniphier_cache_sync - perform a sync point for a particular cache level + * + * @data: cache controller specific data + */ +static void __uniphier_cache_sync(struct uniphier_cache_data *data) +{ + /* This sequence need not be atomic. Do not disable IRQ. */ + writel_relaxed(UNIPHIER_SSCOPE_CM_SYNC, + data->op_base + UNIPHIER_SSCOPE); + /* need a read back to confirm */ + readl_relaxed(data->op_base + UNIPHIER_SSCOPE); +} + +/** + * __uniphier_cache_maint_common - run a queue operation for a particular level + * + * @data: cache controller specific data + * @start: start address of range operation (don't care for "all" operation) + * @size: data size of range operation (don't care for "all" operation) + * @operation: flags to specify the desired cache operation + */ +static void __uniphier_cache_maint_common(struct uniphier_cache_data *data, + unsigned long start, + unsigned long size, + u32 operation) +{ + unsigned long flags; + + /* + * No spin lock is necessary here because: + * + * [1] This outer cache controller is able to accept maintenance + * operations from multiple CPUs at a time in an SMP system; if a + * maintenance operation is under way and another operation is issued, + * the new one is stored in the queue. The controller performs one + * operation after another. If the queue is full, the status register, + * UNIPHIER_SSCOPPQSEF, indicates that the queue registration has + * failed. The status registers, UNIPHIER_{SSCOPPQSEF, SSCOLPQS}, have + * different instances for each CPU, i.e. each CPU can track the status + * of the maintenance operations triggered by itself. + * + * [2] The cache command registers, UNIPHIER_{SSCOQM, SSCOQAD, SSCOQSZ, + * SSCOQWN}, are shared between multiple CPUs, but the hardware still + * guarantees the registration sequence is atomic; the write access to + * them are arbitrated by the hardware. The first accessor to the + * register, UNIPHIER_SSCOQM, holds the access right and it is released + * by reading the status register, UNIPHIER_SSCOPPQSEF. While one CPU + * is holding the access right, other CPUs fail to register operations. + * One CPU should not hold the access right for a long time, so local + * IRQs should be disabled while the following sequence. + */ + local_irq_save(flags); + + /* clear the complete notification flag */ + writel_relaxed(UNIPHIER_SSCOLPQS_EF, data->op_base + UNIPHIER_SSCOLPQS); + + do { + /* set cache operation */ + writel_relaxed(UNIPHIER_SSCOQM_CE | operation, + data->op_base + UNIPHIER_SSCOQM); + + /* set address range if needed */ + if (likely(UNIPHIER_SSCOQM_S_IS_RANGE(operation))) { + writel_relaxed(start, data->op_base + UNIPHIER_SSCOQAD); + writel_relaxed(size, data->op_base + UNIPHIER_SSCOQSZ); + } + + /* set target ways if needed */ + if (unlikely(UNIPHIER_SSCOQM_TID_IS_WAY(operation))) + writel_relaxed(data->way_locked_mask, + data->op_base + UNIPHIER_SSCOQWN); + } while (unlikely(readl_relaxed(data->op_base + UNIPHIER_SSCOPPQSEF) & + (UNIPHIER_SSCOPPQSEF_FE | UNIPHIER_SSCOPPQSEF_OE))); + + /* wait until the operation is completed */ + while (likely(readl_relaxed(data->op_base + UNIPHIER_SSCOLPQS) != + UNIPHIER_SSCOLPQS_EF)) + cpu_relax(); + + local_irq_restore(flags); +} + +static void __uniphier_cache_maint_all(struct uniphier_cache_data *data, + u32 operation) +{ + __uniphier_cache_maint_common(data, 0, 0, + UNIPHIER_SSCOQM_S_ALL | operation); + + __uniphier_cache_sync(data); +} + +static void __uniphier_cache_maint_range(struct uniphier_cache_data *data, + unsigned long start, unsigned long end, + u32 operation) +{ + unsigned long size; + + /* + * If the start address is not aligned, + * perform a cache operation for the first cache-line + */ + start = start & ~(data->line_size - 1); + + size = end - start; + + if (unlikely(size >= (unsigned long)(-data->line_size))) { + /* this means cache operation for all range */ + __uniphier_cache_maint_all(data, operation); + return; + } + + /* + * If the end address is not aligned, + * perform a cache operation for the last cache-line + */ + size = ALIGN(size, data->line_size); + + while (size) { + unsigned long chunk_size = min_t(unsigned long, size, + data->range_op_max_size); + + __uniphier_cache_maint_common(data, start, chunk_size, + UNIPHIER_SSCOQM_S_RANGE | operation); + + start += chunk_size; + size -= chunk_size; + } + + __uniphier_cache_sync(data); +} + +static void __uniphier_cache_enable(struct uniphier_cache_data *data, bool on) +{ + u32 val = 0; + + if (on) + val = UNIPHIER_SSCC_WTG | UNIPHIER_SSCC_PRD | UNIPHIER_SSCC_ON; + + writel_relaxed(val, data->ctrl_base + UNIPHIER_SSCC); +} + +static void __init __uniphier_cache_set_locked_ways( + struct uniphier_cache_data *data, + u32 way_mask) +{ + data->way_locked_mask = way_mask & data->way_present_mask; + + writel_relaxed(~data->way_locked_mask & data->way_present_mask, + data->ctrl_base + UNIPHIER_SSCLPDAWCR); +} + +static void uniphier_cache_maint_range(unsigned long start, unsigned long end, + u32 operation) +{ + struct uniphier_cache_data *data; + + list_for_each_entry(data, &uniphier_cache_list, list) + __uniphier_cache_maint_range(data, start, end, operation); +} + +static void uniphier_cache_maint_all(u32 operation) +{ + struct uniphier_cache_data *data; + + list_for_each_entry(data, &uniphier_cache_list, list) + __uniphier_cache_maint_all(data, operation); +} + +static void uniphier_cache_inv_range(unsigned long start, unsigned long end) +{ + uniphier_cache_maint_range(start, end, UNIPHIER_SSCOQM_CM_INV); +} + +static void uniphier_cache_clean_range(unsigned long start, unsigned long end) +{ + uniphier_cache_maint_range(start, end, UNIPHIER_SSCOQM_CM_CLEAN); +} + +static void uniphier_cache_flush_range(unsigned long start, unsigned long end) +{ + uniphier_cache_maint_range(start, end, UNIPHIER_SSCOQM_CM_FLUSH); +} + +static void __init uniphier_cache_inv_all(void) +{ + uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_INV); +} + +static void uniphier_cache_flush_all(void) +{ + uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_FLUSH); +} + +static void uniphier_cache_disable(void) +{ + struct uniphier_cache_data *data; + + list_for_each_entry_reverse(data, &uniphier_cache_list, list) + __uniphier_cache_enable(data, false); + + uniphier_cache_flush_all(); +} + +static void __init uniphier_cache_enable(void) +{ + struct uniphier_cache_data *data; + + uniphier_cache_inv_all(); + + list_for_each_entry(data, &uniphier_cache_list, list) { + __uniphier_cache_enable(data, true); + __uniphier_cache_set_locked_ways(data, 0); + } +} + +static void uniphier_cache_sync(void) +{ + struct uniphier_cache_data *data; + + list_for_each_entry(data, &uniphier_cache_list, list) + __uniphier_cache_sync(data); +} + +int __init uniphier_cache_l2_is_enabled(void) +{ + struct uniphier_cache_data *data; + + data = list_first_entry_or_null(&uniphier_cache_list, + struct uniphier_cache_data, list); + if (!data) + return 0; + + return !!(readl_relaxed(data->ctrl_base + UNIPHIER_SSCC) & + UNIPHIER_SSCC_ON); +} + +void __init uniphier_cache_l2_touch_range(unsigned long start, + unsigned long end) +{ + struct uniphier_cache_data *data; + + data = list_first_entry_or_null(&uniphier_cache_list, + struct uniphier_cache_data, list); + if (data) + __uniphier_cache_maint_range(data, start, end, + UNIPHIER_SSCOQM_TID_WAY | + UNIPHIER_SSCOQM_CM_TOUCH); +} + +void __init uniphier_cache_l2_set_locked_ways(u32 way_mask) +{ + struct uniphier_cache_data *data; + + data = list_first_entry_or_null(&uniphier_cache_list, + struct uniphier_cache_data, list); + if (data) + __uniphier_cache_set_locked_ways(data, way_mask); +} + +static const struct of_device_id uniphier_cache_match[] __initconst = { + { + .compatible = "socionext,uniphier-system-cache", + }, + { /* sentinel */ } +}; + +static struct device_node * __init uniphier_cache_get_next_level_node( + struct device_node *np) +{ + u32 phandle; + + if (of_property_read_u32(np, "next-level-cache", &phandle)) + return NULL; + + return of_find_node_by_phandle(phandle); +} + +static int __init __uniphier_cache_init(struct device_node *np, + unsigned int *cache_level) +{ + struct uniphier_cache_data *data; + u32 level, cache_size; + struct device_node *next_np; + int ret = 0; + + if (!of_match_node(uniphier_cache_match, np)) { + pr_err("L%d: not compatible with uniphier cache\n", + *cache_level); + return -EINVAL; + } + + if (of_property_read_u32(np, "cache-level", &level)) { + pr_err("L%d: cache-level is not specified\n", *cache_level); + return -EINVAL; + } + + if (level != *cache_level) { + pr_err("L%d: cache-level is unexpected value %d\n", + *cache_level, level); + return -EINVAL; + } + + if (!of_property_read_bool(np, "cache-unified")) { + pr_err("L%d: cache-unified is not specified\n", *cache_level); + return -EINVAL; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (of_property_read_u32(np, "cache-line-size", &data->line_size) || + !is_power_of_2(data->line_size)) { + pr_err("L%d: cache-line-size is unspecified or invalid\n", + *cache_level); + ret = -EINVAL; + goto err; + } + + if (of_property_read_u32(np, "cache-sets", &data->nsets) || + !is_power_of_2(data->nsets)) { + pr_err("L%d: cache-sets is unspecified or invalid\n", + *cache_level); + ret = -EINVAL; + goto err; + } + + if (of_property_read_u32(np, "cache-size", &cache_size) || + cache_size == 0 || cache_size % (data->nsets * data->line_size)) { + pr_err("L%d: cache-size is unspecified or invalid\n", + *cache_level); + ret = -EINVAL; + goto err; + } + + data->way_present_mask = + ((u32)1 << cache_size / data->nsets / data->line_size) - 1; + + data->ctrl_base = of_iomap(np, 0); + if (!data->ctrl_base) { + pr_err("L%d: failed to map control register\n", *cache_level); + ret = -ENOMEM; + goto err; + } + + data->rev_base = of_iomap(np, 1); + if (!data->rev_base) { + pr_err("L%d: failed to map revision register\n", *cache_level); + ret = -ENOMEM; + goto err; + } + + data->op_base = of_iomap(np, 2); + if (!data->op_base) { + pr_err("L%d: failed to map operation register\n", *cache_level); + ret = -ENOMEM; + goto err; + } + + if (*cache_level == 2) { + u32 revision = readl(data->rev_base + UNIPHIER_SSCID); + /* + * The size of range operation is limited to (1 << 22) or less + * for PH-sLD8 or older SoCs. + */ + if (revision <= 0x16) + data->range_op_max_size = (u32)1 << 22; + } + + data->range_op_max_size -= data->line_size; + + INIT_LIST_HEAD(&data->list); + list_add_tail(&data->list, &uniphier_cache_list); /* no mutex */ + + /* + * OK, this level has been successfully initialized. Look for the next + * level cache. Do not roll back even if the initialization of the + * next level cache fails because we want to continue with available + * cache levels. + */ + next_np = uniphier_cache_get_next_level_node(np); + if (next_np) { + (*cache_level)++; + ret = __uniphier_cache_init(next_np, cache_level); + } + of_node_put(next_np); + + return ret; +err: + iounmap(data->op_base); + iounmap(data->rev_base); + iounmap(data->ctrl_base); + kfree(data); + + return ret; +} + +int __init uniphier_cache_init(void) +{ + struct device_node *np = NULL; + unsigned int cache_level; + int ret = 0; + + /* look for level 2 cache */ + while ((np = of_find_matching_node(np, uniphier_cache_match))) + if (!of_property_read_u32(np, "cache-level", &cache_level) && + cache_level == 2) + break; + + if (!np) + return -ENODEV; + + ret = __uniphier_cache_init(np, &cache_level); + of_node_put(np); + + if (ret) { + /* + * Error out iif L2 initialization fails. Continue with any + * error on L3 or outer because they are optional. + */ + if (cache_level == 2) { + pr_err("failed to initialize L2 cache\n"); + return ret; + } + + cache_level--; + ret = 0; + } + + outer_cache.inv_range = uniphier_cache_inv_range; + outer_cache.clean_range = uniphier_cache_clean_range; + outer_cache.flush_range = uniphier_cache_flush_range; + outer_cache.flush_all = uniphier_cache_flush_all; + outer_cache.disable = uniphier_cache_disable; + outer_cache.sync = uniphier_cache_sync; + + uniphier_cache_enable(); + + pr_info("enabled outer cache (cache level: %d)\n", cache_level); + + return ret; +} |