Commit 5df22a61 authored by Olof Johansson's avatar Olof Johansson

Merge tag 'mvebu-soc-3.16' of git://git.infradead.org/linux-mvebu into next/soc

Merge "ARM: mvebu: SoC changes for v3.16" from Jason Cooper:

mvebu SoC changes for v3.16

 - Armada 375/38x coherency support
 - Armada 375/38x SMP support
 - mvebu PMSU and CPU reset support
 - Armada 370/XP cpuidle support
 - kirkwood remove platform init of audio device
 - small fixes and cleanup for new SoC (375/38x)

Note:
 - due to complex deps, cpuidle changes Acked by appropriate maintainer for
   going though arm-soc tree.

* tag 'mvebu-soc-3.16' of git://git.infradead.org/linux-mvebu: (46 commits)
  ARM: mvebu: Fix pmsu compilation when ARMv6 is selected
  ARM: mvebu: conditionalize Armada 375 coherency workaround
  ARM: mvebu: conditionalize Armada 375 SMP workaround
  ARM: mvebu: add Armada 375 A0 revision definition
  ARM: mvebu: initialize mvebu-soc-id earlier
  ARM: mvebu: fix thermal quirk SoC revision check
  ARM: Kirkwood: t5325: Remove platform device to instantiate audio
  ARM: Kirkwood: Remove platform driver for codec
  ARM: mvebu: Add thermal quirk for the Armada 375 DB board
  ARM: mvebu: Select HAVE_ARM_TWD only if SMP is enabled
  ARM: mvebu: fix the name of the parameter used in mvebu_get_soc_id
  ARM: mvebu: remove unnecessary ifdef around l2x0_of_init
  ARM: mvebu: register the cpuidle driver for the Armada XP SoCs
  cpuidle: mvebu: Add initial CPU idle support for Armada 370/XP SoC
  ARM: mvebu: Register notifier callback for the cpuidle transition
  ARM: mvebu: refine which files are build in mach-mvebu
  ARM: mvebu: Add the PMSU related part of the cpu idle functions
  ARM: mvebu: Allow to power down L2 cache controller in idle mode
  ARM: mvebu: Low level function to disable HW coherency support
  ARM: mvebu: Split low level functions to manipulate HW coherency
  ...
Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents c15c1199 9f0affcf
Power Management Service Unit(PMSU) Power Management Service Unit(PMSU)
----------------------------------- -----------------------------------
Available on Marvell SOCs: Armada 370 and Armada XP Available on Marvell SOCs: Armada 370, Armada 38x and Armada XP
Required properties: Required properties:
- compatible: "marvell,armada-370-xp-pmsu" - compatible: should be one of:
- "marvell,armada-370-pmsu" for Armada 370 or Armada XP
- "marvell,armada-380-pmsu" for Armada 38x
- "marvell,armada-370-xp-pmsu" was used for Armada 370/XP but is now
deprecated and will be removed
- reg: Should contain PMSU registers location and length. First pair - reg: Should contain PMSU registers location and length.
for the per-CPU SW Reset Control registers, second pair for the
Power Management Service Unit.
Example: Example:
armada-370-xp-pmsu@d0022000 { armada-370-xp-pmsu@22000 {
compatible = "marvell,armada-370-xp-pmsu"; compatible = "marvell,armada-370-pmsu";
reg = <0xd0022100 0x430>, reg = <0x22000 0x1000>;
<0xd0020800 0x20>;
}; };
Marvell Armada CPU reset controller
===================================
Required properties:
- compatible: Should be "marvell,armada-370-cpu-reset".
- reg: should be register base and length as documented in the
datasheet for the CPU reset registers
cpurst: cpurst@20800 {
compatible = "marvell,armada-370-cpu-reset";
reg = <0x20800 0x20>;
};
Coherency fabric Coherency fabric
---------------- ----------------
Available on Marvell SOCs: Armada 370 and Armada XP Available on Marvell SOCs: Armada 370, Armada 375, Armada 38x and Armada XP
Required properties: Required properties:
- compatible: "marvell,coherency-fabric" - compatible: the possible values are:
* "marvell,coherency-fabric", to be used for the coherency fabric of
the Armada 370 and Armada XP.
* "marvell,armada-375-coherency-fabric", for the Armada 375 coherency
fabric.
* "marvell,armada-380-coherency-fabric", for the Armada 38x coherency
fabric.
- reg: Should contain coherency fabric registers location and - reg: Should contain coherency fabric registers location and
length. First pair for the coherency fabric registers, second pair length.
for the per-CPU fabric registers registers.
* For "marvell,coherency-fabric", the first pair for the coherency
fabric registers, second pair for the per-CPU fabric registers.
Example: * For "marvell,armada-375-coherency-fabric", only one pair is needed
for the per-CPU fabric registers.
* For "marvell,armada-380-coherency-fabric", only one pair is needed
for the per-CPU fabric registers.
Examples:
coherency-fabric@d0020200 { coherency-fabric@d0020200 {
compatible = "marvell,coherency-fabric"; compatible = "marvell,coherency-fabric";
...@@ -19,3 +36,8 @@ coherency-fabric@d0020200 { ...@@ -19,3 +36,8 @@ coherency-fabric@d0020200 {
}; };
coherency-fabric@21810 {
compatible = "marvell,armada-375-coherency-fabric";
reg = <0x21810 0x1c>;
};
...@@ -185,6 +185,9 @@ nodes to be present and contain the properties described below. ...@@ -185,6 +185,9 @@ nodes to be present and contain the properties described below.
"qcom,gcc-msm8660" "qcom,gcc-msm8660"
"qcom,kpss-acc-v1" "qcom,kpss-acc-v1"
"qcom,kpss-acc-v2" "qcom,kpss-acc-v2"
"marvell,armada-375-smp"
"marvell,armada-380-smp"
"marvell,armada-xp-smp"
- cpu-release-addr - cpu-release-addr
Usage: required for systems that have an "enable-method" Usage: required for systems that have an "enable-method"
......
...@@ -195,7 +195,7 @@ static void __init kirkwood_dt_init(void) ...@@ -195,7 +195,7 @@ static void __init kirkwood_dt_init(void)
{ {
kirkwood_disable_mbus_error_propagation(); kirkwood_disable_mbus_error_propagation();
BUG_ON(mvebu_mbus_dt_init()); BUG_ON(mvebu_mbus_dt_init(false));
#ifdef CONFIG_CACHE_FEROCEON_L2 #ifdef CONFIG_CACHE_FEROCEON_L2
feroceon_of_init(); feroceon_of_init();
......
...@@ -6,6 +6,7 @@ config ARCH_MVEBU ...@@ -6,6 +6,7 @@ config ARCH_MVEBU
select IRQ_DOMAIN select IRQ_DOMAIN
select PINCTRL select PINCTRL
select PLAT_ORION select PLAT_ORION
select SOC_BUS
select MVEBU_MBUS select MVEBU_MBUS
select ZONE_DMA if ARM_LPAE select ZONE_DMA if ARM_LPAE
select ARCH_REQUIRE_GPIOLIB select ARCH_REQUIRE_GPIOLIB
...@@ -39,6 +40,9 @@ config MACH_ARMADA_375 ...@@ -39,6 +40,9 @@ config MACH_ARMADA_375
select ARM_GIC select ARM_GIC
select ARMADA_375_CLK select ARMADA_375_CLK
select CPU_V7 select CPU_V7
select HAVE_ARM_SCU
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select MACH_MVEBU_V7 select MACH_MVEBU_V7
select PINCTRL_ARMADA_375 select PINCTRL_ARMADA_375
help help
...@@ -52,6 +56,9 @@ config MACH_ARMADA_38X ...@@ -52,6 +56,9 @@ config MACH_ARMADA_38X
select ARM_GIC select ARM_GIC
select ARMADA_38X_CLK select ARMADA_38X_CLK
select CPU_V7 select CPU_V7
select HAVE_ARM_SCU
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select MACH_MVEBU_V7 select MACH_MVEBU_V7
select PINCTRL_ARMADA_38X select PINCTRL_ARMADA_38X
help help
...@@ -97,13 +104,6 @@ config MACH_KIRKWOOD ...@@ -97,13 +104,6 @@ config MACH_KIRKWOOD
Say 'Y' here if you want your kernel to support boards based Say 'Y' here if you want your kernel to support boards based
on the Marvell Kirkwood device tree. on the Marvell Kirkwood device tree.
config MACH_T5325
bool "HP T5325 thin client"
depends on MACH_KIRKWOOD
help
Say 'Y' here if you want your kernel to support the
HP T5325 Thin client
endmenu endmenu
endif endif
...@@ -2,12 +2,15 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ ...@@ -2,12 +2,15 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \
-I$(srctree)/arch/arm/plat-orion/include -I$(srctree)/arch/arm/plat-orion/include
AFLAGS_coherency_ll.o := -Wa,-march=armv7-a AFLAGS_coherency_ll.o := -Wa,-march=armv7-a
CFLAGS_pmsu.o := -march=armv7-a
obj-y += system-controller.o mvebu-soc-id.o obj-y += system-controller.o mvebu-soc-id.o
obj-$(CONFIG_MACH_MVEBU_V7) += board-v7.o
ifeq ($(CONFIG_MACH_MVEBU_V7),y)
obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
endif
obj-$(CONFIG_MACH_DOVE) += dove.o obj-$(CONFIG_MACH_DOVE) += dove.o
obj-$(CONFIG_ARCH_MVEBU) += coherency.o coherency_ll.o pmsu.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o
obj-$(CONFIG_MACH_T5325) += board-t5325.o
...@@ -20,8 +20,6 @@ ...@@ -20,8 +20,6 @@
#define ARMADA_XP_MAX_CPUS 4 #define ARMADA_XP_MAX_CPUS 4
void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq);
void armada_xp_mpic_smp_cpu_init(void);
void armada_xp_secondary_startup(void); void armada_xp_secondary_startup(void);
extern struct smp_operations armada_xp_smp_ops; extern struct smp_operations armada_xp_smp_ops;
#endif #endif
......
/*
* HP T5325 Board Setup
*
* Copyright (C) 2014
*
* Andrew Lunn <andrew@lunn.ch>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <sound/alc5623.h>
#include "board.h"
static struct platform_device hp_t5325_audio_device = {
.name = "t5325-audio",
.id = -1,
};
static struct alc5623_platform_data alc5621_data = {
.add_ctrl = 0x3700,
.jack_det_ctrl = 0x4810,
};
static struct i2c_board_info i2c_board_info[] __initdata = {
{
I2C_BOARD_INFO("alc5621", 0x1a),
.platform_data = &alc5621_data,
},
};
void __init t5325_init(void)
{
i2c_register_board_info(0, i2c_board_info, ARRAY_SIZE(i2c_board_info));
platform_device_register(&hp_t5325_audio_device);
}
...@@ -27,11 +27,29 @@ ...@@ -27,11 +27,29 @@
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/mach/time.h> #include <asm/mach/time.h>
#include <asm/smp_scu.h>
#include "armada-370-xp.h" #include "armada-370-xp.h"
#include "common.h" #include "common.h"
#include "coherency.h" #include "coherency.h"
#include "mvebu-soc-id.h" #include "mvebu-soc-id.h"
/*
* Enables the SCU when available. Obviously, this is only useful on
* Cortex-A based SOCs, not on PJ4B based ones.
*/
static void __init mvebu_scu_enable(void)
{
void __iomem *scu_base;
struct device_node *np =
of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
if (np) {
scu_base = of_iomap(np, 0);
scu_enable(scu_base);
of_node_put(np);
}
}
/* /*
* Early versions of Armada 375 SoC have a bug where the BootROM * Early versions of Armada 375 SoC have a bug where the BootROM
* leaves an external data abort pending. The kernel is hit by this * leaves an external data abort pending. The kernel is hit by this
...@@ -57,11 +75,10 @@ static void __init mvebu_timer_and_clk_init(void) ...@@ -57,11 +75,10 @@ static void __init mvebu_timer_and_clk_init(void)
{ {
of_clk_init(NULL); of_clk_init(NULL);
clocksource_of_init(); clocksource_of_init();
mvebu_scu_enable();
coherency_init(); coherency_init();
BUG_ON(mvebu_mbus_dt_init()); BUG_ON(mvebu_mbus_dt_init(coherency_available()));
#ifdef CONFIG_CACHE_L2X0
l2x0_of_init(0, ~0UL); l2x0_of_init(0, ~0UL);
#endif
if (of_machine_is_compatible("marvell,armada375")) if (of_machine_is_compatible("marvell,armada375"))
hook_fault_code(16 + 6, armada_375_external_abort_wa, SIGBUS, 0, hook_fault_code(16 + 6, armada_375_external_abort_wa, SIGBUS, 0,
...@@ -78,7 +95,7 @@ static void __init i2c_quirk(void) ...@@ -78,7 +95,7 @@ static void __init i2c_quirk(void)
* mechanism. We can exit only if we are sure that we can * mechanism. We can exit only if we are sure that we can
* get the SoC revision and it is more recent than A0. * get the SoC revision and it is more recent than A0.
*/ */
if (mvebu_get_soc_id(&rev, &dev) == 0 && dev > MV78XX0_A0_REV) if (mvebu_get_soc_id(&dev, &rev) == 0 && rev > MV78XX0_A0_REV)
return; return;
for_each_compatible_node(np, NULL, "marvell,mv78230-i2c") { for_each_compatible_node(np, NULL, "marvell,mv78230-i2c") {
...@@ -96,10 +113,66 @@ static void __init i2c_quirk(void) ...@@ -96,10 +113,66 @@ static void __init i2c_quirk(void)
return; return;
} }
#define A375_Z1_THERMAL_FIXUP_OFFSET 0xc
static void __init thermal_quirk(void)
{
struct device_node *np;
u32 dev, rev;
if (mvebu_get_soc_id(&dev, &rev) == 0 && rev > ARMADA_375_Z1_REV)
return;
for_each_compatible_node(np, NULL, "marvell,armada375-thermal") {
struct property *prop;
__be32 newval, *newprop, *oldprop;
int len;
/*
* The register offset is at a wrong location. This quirk
* creates a new reg property as a clone of the previous
* one and corrects the offset.
*/
oldprop = (__be32 *)of_get_property(np, "reg", &len);
if (!oldprop)
continue;
/* Create a duplicate of the 'reg' property */
prop = kzalloc(sizeof(*prop), GFP_KERNEL);
prop->length = len;
prop->name = kstrdup("reg", GFP_KERNEL);
prop->value = kzalloc(len, GFP_KERNEL);
memcpy(prop->value, oldprop, len);
/* Fixup the register offset of the second entry */
oldprop += 2;
newprop = (__be32 *)prop->value + 2;
newval = cpu_to_be32(be32_to_cpu(*oldprop) -
A375_Z1_THERMAL_FIXUP_OFFSET);
*newprop = newval;
of_update_property(np, prop);
/*
* The thermal controller needs some quirk too, so let's change
* the compatible string to reflect this.
*/
prop = kzalloc(sizeof(*prop), GFP_KERNEL);
prop->name = kstrdup("compatible", GFP_KERNEL);
prop->length = sizeof("marvell,armada375-z1-thermal");
prop->value = kstrdup("marvell,armada375-z1-thermal",
GFP_KERNEL);
of_update_property(np, prop);
}
return;
}
static void __init mvebu_dt_init(void) static void __init mvebu_dt_init(void)
{ {
if (of_machine_is_compatible("plathome,openblocks-ax3-4")) if (of_machine_is_compatible("plathome,openblocks-ax3-4"))
i2c_quirk(); i2c_quirk();
if (of_machine_is_compatible("marvell,a375-db"))
thermal_quirk();
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
} }
...@@ -123,6 +196,7 @@ static const char * const armada_375_dt_compat[] = { ...@@ -123,6 +196,7 @@ static const char * const armada_375_dt_compat[] = {
DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)") DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)")
.init_time = mvebu_timer_and_clk_init, .init_time = mvebu_timer_and_clk_init,
.init_machine = mvebu_dt_init,
.restart = mvebu_restart, .restart = mvebu_restart,
.dt_compat = armada_375_dt_compat, .dt_compat = armada_375_dt_compat,
MACHINE_END MACHINE_END
......
...@@ -13,10 +13,4 @@ ...@@ -13,10 +13,4 @@
#ifndef __ARCH_MVEBU_BOARD_H #ifndef __ARCH_MVEBU_BOARD_H
#define __ARCH_MVEBU_BOARD_H #define __ARCH_MVEBU_BOARD_H
#ifdef CONFIG_MACH_T5325
void t5325_init(void);
#else
static inline void t5325_init(void) {};
#endif
#endif #endif
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
* supplies basic routines for configuring and controlling hardware coherency * supplies basic routines for configuring and controlling hardware coherency
*/ */
#define pr_fmt(fmt) "mvebu-coherency: " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/of_address.h> #include <linux/of_address.h>
...@@ -24,13 +26,17 @@ ...@@ -24,13 +26,17 @@
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mbus.h>
#include <linux/clk.h>
#include <asm/smp_plat.h> #include <asm/smp_plat.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include "armada-370-xp.h" #include "armada-370-xp.h"
#include "coherency.h" #include "coherency.h"
#include "mvebu-soc-id.h"
unsigned long coherency_phys_base; unsigned long coherency_phys_base;
static void __iomem *coherency_base; void __iomem *coherency_base;
static void __iomem *coherency_cpu_base; static void __iomem *coherency_cpu_base;
/* Coherency fabric registers */ /* Coherency fabric registers */
...@@ -38,27 +44,190 @@ static void __iomem *coherency_cpu_base; ...@@ -38,27 +44,190 @@ static void __iomem *coherency_cpu_base;
#define IO_SYNC_BARRIER_CTL_OFFSET 0x0 #define IO_SYNC_BARRIER_CTL_OFFSET 0x0
enum {
COHERENCY_FABRIC_TYPE_NONE,
COHERENCY_FABRIC_TYPE_ARMADA_370_XP,
COHERENCY_FABRIC_TYPE_ARMADA_375,
COHERENCY_FABRIC_TYPE_ARMADA_380,
};
static struct of_device_id of_coherency_table[] = { static struct of_device_id of_coherency_table[] = {
{.compatible = "marvell,coherency-fabric"}, {.compatible = "marvell,coherency-fabric",
.data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_370_XP },
{.compatible = "marvell,armada-375-coherency-fabric",
.data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_375 },
{.compatible = "marvell,armada-380-coherency-fabric",
.data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_380 },
{ /* end of list */ }, { /* end of list */ },
}; };
/* Function defined in coherency_ll.S */ /* Functions defined in coherency_ll.S */
int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id); int ll_enable_coherency(void);
void ll_add_cpu_to_smp_group(void);
int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) int set_cpu_coherent(void)
{ {
if (!coherency_base) { if (!coherency_base) {
pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id); pr_warn("Can't make current CPU cache coherent.\n");
pr_warn("Coherency fabric is not initialized\n"); pr_warn("Coherency fabric is not initialized\n");
return 1; return 1;
} }
return ll_set_cpu_coherent(coherency_base, hw_cpu_id); ll_add_cpu_to_smp_group();
return ll_enable_coherency();
}
/*
* The below code implements the I/O coherency workaround on Armada
* 375. This workaround consists in using the two channels of the
* first XOR engine to trigger a XOR transaction that serves as the
* I/O coherency barrier.
*/
static void __iomem *xor_base, *xor_high_base;
static dma_addr_t coherency_wa_buf_phys[CONFIG_NR_CPUS];
static void *coherency_wa_buf[CONFIG_NR_CPUS];
static bool coherency_wa_enabled;
#define XOR_CONFIG(chan) (0x10 + (chan * 4))
#define XOR_ACTIVATION(chan) (0x20 + (chan * 4))
#define WINDOW_BAR_ENABLE(chan) (0x240 + ((chan) << 2))
#define WINDOW_BASE(w) (0x250 + ((w) << 2))
#define WINDOW_SIZE(w) (0x270 + ((w) << 2))
#define WINDOW_REMAP_HIGH(w) (0x290 + ((w) << 2))
#define WINDOW_OVERRIDE_CTRL(chan) (0x2A0 + ((chan) << 2))
#define XOR_DEST_POINTER(chan) (0x2B0 + (chan * 4))
#define XOR_BLOCK_SIZE(chan) (0x2C0 + (chan * 4))
#define XOR_INIT_VALUE_LOW 0x2E0
#define XOR_INIT_VALUE_HIGH 0x2E4
static inline void mvebu_hwcc_armada375_sync_io_barrier_wa(void)
{
int idx = smp_processor_id();
/* Write '1' to the first word of the buffer */
writel(0x1, coherency_wa_buf[idx]);
/* Wait until the engine is idle */
while ((readl(xor_base + XOR_ACTIVATION(idx)) >> 4) & 0x3)
;
dmb();
/* Trigger channel */
writel(0x1, xor_base + XOR_ACTIVATION(idx));
/* Poll the data until it is cleared by the XOR transaction */
while (readl(coherency_wa_buf[idx]))
;
}
static void __init armada_375_coherency_init_wa(void)
{
const struct mbus_dram_target_info *dram;
struct device_node *xor_node;
struct property *xor_status;
struct clk *xor_clk;
u32 win_enable = 0;
int i;
pr_warn("enabling coherency workaround for Armada 375 Z1, one XOR engine disabled\n");
/*
* Since the workaround uses one XOR engine, we grab a
* reference to its Device Tree node first.
*/
xor_node = of_find_compatible_node(NULL, NULL, "marvell,orion-xor");
BUG_ON(!xor_node);
/*
* Then we mark it as disabled so that the real XOR driver
* will not use it.
*/
xor_status = kzalloc(sizeof(struct property), GFP_KERNEL);
BUG_ON(!xor_status);
xor_status->value = kstrdup("disabled", GFP_KERNEL);
BUG_ON(!xor_status->value);
xor_status->length = 8;
xor_status->name = kstrdup("status", GFP_KERNEL);
BUG_ON(!xor_status->name);
of_update_property(xor_node, xor_status);
/*
* And we remap the registers, get the clock, and do the
* initial configuration of the XOR engine.
*/
xor_base = of_iomap(xor_node, 0);
xor_high_base = of_iomap(xor_node, 1);
xor_clk = of_clk_get_by_name(xor_node, NULL);
BUG_ON(!xor_clk);
clk_prepare_enable(xor_clk);
dram = mv_mbus_dram_info();
for (i = 0; i < 8; i++) {
writel(0, xor_base + WINDOW_BASE(i));
writel(0, xor_base + WINDOW_SIZE(i));
if (i < 4)
writel(0, xor_base + WINDOW_REMAP_HIGH(i));
}
for (i = 0; i < dram->num_cs; i++) {
const struct mbus_dram_window *cs = dram->cs + i;
writel((cs->base & 0xffff0000) |
(cs->mbus_attr << 8) |
dram->mbus_dram_target_id, xor_base + WINDOW_BASE(i));
writel((cs->size - 1) & 0xffff0000, xor_base + WINDOW_SIZE(i));
win_enable |= (1 << i);
win_enable |= 3 << (16 + (2 * i));
}
writel(win_enable, xor_base + WINDOW_BAR_ENABLE(0));
writel(win_enable, xor_base + WINDOW_BAR_ENABLE(1));
writel(0, xor_base + WINDOW_OVERRIDE_CTRL(0));
writel(0, xor_base + WINDOW_OVERRIDE_CTRL(1));
for (i = 0; i < CONFIG_NR_CPUS; i++) {
coherency_wa_buf[i] = kzalloc(PAGE_SIZE, GFP_KERNEL);
BUG_ON(!coherency_wa_buf[i]);
/*
* We can't use the DMA mapping API, since we don't
* have a valid 'struct device' pointer
*/
coherency_wa_buf_phys[i] =
virt_to_phys(coherency_wa_buf[i]);
BUG_ON(!coherency_wa_buf_phys[i]);
/*
* Configure the XOR engine for memset operation, with
* a 128 bytes block size
*/
writel(0x444, xor_base + XOR_CONFIG(i));
writel(128, xor_base + XOR_BLOCK_SIZE(i));
writel(coherency_wa_buf_phys[i],
xor_base + XOR_DEST_POINTER(i));
}
writel(0x0, xor_base + XOR_INIT_VALUE_LOW);
writel(0x0, xor_base + XOR_INIT_VALUE_HIGH);
coherency_wa_enabled = true;
} }
static inline void mvebu_hwcc_sync_io_barrier(void) static inline void mvebu_hwcc_sync_io_barrier(void)
{ {
if (coherency_wa_enabled) {
mvebu_hwcc_armada375_sync_io_barrier_wa();
return;
}
writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET);
while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1);
} }
...@@ -121,42 +290,93 @@ static struct notifier_block mvebu_hwcc_platform_nb = { ...@@ -121,42 +290,93 @@ static struct notifier_block mvebu_hwcc_platform_nb = {
.notifier_call = mvebu_hwcc_platform_notifier, .notifier_call = mvebu_hwcc_platform_notifier,
}; };
int __init coherency_init(void) static void __init armada_370_coherency_init(struct device_node *np)
{
struct resource res;
of_address_to_resource(np, 0, &res);
coherency_phys_base = res.start;
/*
* Ensure secondary CPUs will see the updated value,
* which they read before they join the coherency
* fabric, and therefore before they are coherent with
* the boot CPU cache.
*/
sync_cache_w(&coherency_phys_base);
coherency_base = of_iomap(np, 0);
coherency_cpu_base = of_iomap(np, 1);
set_cpu_coherent();
}
static void __init armada_375_380_coherency_init(struct device_node *np)
{
coherency_cpu_base = of_iomap(np, 0);
}
static int coherency_type(void)
{ {
struct device_node *np; struct device_node *np;
const struct of_device_id *match;
np = of_find_matching_node(NULL, of_coherency_table); np = of_find_matching_node_and_match(NULL, of_coherency_table, &match);
if (np) { if (np) {
struct resource res; int type = (int) match->data;
pr_info("Initializing Coherency fabric\n");
of_address_to_resource(np, 0, &res); /* Armada 370/XP coherency works in both UP and SMP */
coherency_phys_base = res.start; if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
/* return type;
* Ensure secondary CPUs will see the updated value,
* which they read before they join the coherency /* Armada 375 coherency works only on SMP */
* fabric, and therefore before they are coherent with else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 && is_smp())
* the boot CPU cache. return type;
*/
sync_cache_w(&coherency_phys_base); /* Armada 380 coherency works only on SMP */
coherency_base = of_iomap(np, 0); else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380 && is_smp())
coherency_cpu_base = of_iomap(np, 1); return type;
set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0);
of_node_put(np);
} }
return 0; return COHERENCY_FABRIC_TYPE_NONE;
} }
static int __init coherency_late_init(void) int coherency_available(void)
{
return coherency_type() != COHERENCY_FABRIC_TYPE_NONE;
}
int __init coherency_init(void)
{ {
int type = coherency_type();
struct device_node *np; struct device_node *np;
np = of_find_matching_node(NULL, of_coherency_table); np = of_find_matching_node(NULL, of_coherency_table);
if (np) {
bus_register_notifier(&platform_bus_type, if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
&mvebu_hwcc_platform_nb); armada_370_coherency_init(np);
of_node_put(np); else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 ||
type == COHERENCY_FABRIC_TYPE_ARMADA_380)
armada_375_380_coherency_init(np);
return 0;
}
static int __init coherency_late_init(void)
{
int type = coherency_type();
if (type == COHERENCY_FABRIC_TYPE_NONE)
return 0;
if (type == COHERENCY_FABRIC_TYPE_ARMADA_375) {
u32 dev, rev;
if (mvebu_get_soc_id(&dev, &rev) == 0 &&
rev == ARMADA_375_Z1_REV)
armada_375_coherency_init_wa();
} }
bus_register_notifier(&platform_bus_type,
&mvebu_hwcc_platform_nb);
return 0; return 0;
} }
......
...@@ -15,8 +15,9 @@ ...@@ -15,8 +15,9 @@
#define __MACH_370_XP_COHERENCY_H #define __MACH_370_XP_COHERENCY_H
extern unsigned long coherency_phys_base; extern unsigned long coherency_phys_base;
int set_cpu_coherent(void);
int set_cpu_coherent(unsigned int cpu_id, int smp_group_id);
int coherency_init(void); int coherency_init(void);
int coherency_available(void);
#endif /* __MACH_370_XP_COHERENCY_H */ #endif /* __MACH_370_XP_COHERENCY_H */
...@@ -21,38 +21,108 @@ ...@@ -21,38 +21,108 @@
#define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4 #define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4
#include <asm/assembler.h> #include <asm/assembler.h>
#include <asm/cp15.h>
.text .text
/* /* Returns with the coherency address in r1 (r0 is untouched)*/
* r0: Coherency fabric base register address ENTRY(ll_get_coherency_base)
* r1: HW CPU id mrc p15, 0, r1, c1, c0, 0
*/ tst r1, #CR_M @ Check MMU bit enabled
ENTRY(ll_set_cpu_coherent) bne 1f
/* Create bit by cpu index */
mov r3, #(1 << 24)
lsl r1, r3, r1
ARM_BE8(rev r1, r1)
/* Add CPU to SMP group - Atomic */ /* use physical address of the coherency register */
add r3, r0, #ARMADA_XP_CFB_CTL_REG_OFFSET adr r1, 3f
ldr r3, [r1]
ldr r1, [r1, r3]
b 2f
1: 1:
ldrex r2, [r3] /* use virtual address of the coherency register */
orr r2, r2, r1 ldr r1, =coherency_base
strex r0, r2, [r3] ldr r1, [r1]
cmp r0, #0 2:
bne 1b mov pc, lr
ENDPROC(ll_get_coherency_base)
/* Enable coherency on CPU - Atomic */
add r3, r3, #ARMADA_XP_CFB_CFG_REG_OFFSET /* Returns with the CPU ID in r3 (r0 is untouched)*/
ENTRY(ll_get_cpuid)
mrc 15, 0, r3, cr0, cr0, 5
and r3, r3, #15
mov r2, #(1 << 24)
lsl r3, r2, r3
ARM_BE8(rev r1, r1)
mov pc, lr
ENDPROC(ll_get_cpuid)
/* ll_add_cpu_to_smp_group, ll_enable_coherency and
* ll_disable_coherency use strex/ldrex whereas MMU can be off. The
* Armada XP SoC has an exclusive monitor that can track transactions
* to Device and/or SO and as such also when MMU is disabled the
* exclusive transactions will be functional
*/
ENTRY(ll_add_cpu_to_smp_group)
/*
* r0 being untouched in ll_get_coherency_base and
* ll_get_cpuid, we can use it to save lr modifing it with the
* following bl
*/
mov r0, lr
bl ll_get_coherency_base
bl ll_get_cpuid
mov lr, r0
add r0, r1, #ARMADA_XP_CFB_CFG_REG_OFFSET
1: 1:
ldrex r2, [r3] ldrex r2, [r0]
orr r2, r2, r1 orr r2, r2, r3
strex r0, r2, [r3] strex r1, r2, [r0]
cmp r0, #0 cmp r1, #0
bne 1b bne 1b
mov pc, lr
ENDPROC(ll_add_cpu_to_smp_group)
ENTRY(ll_enable_coherency)
/*
* r0 being untouched in ll_get_coherency_base and
* ll_get_cpuid, we can use it to save lr modifing it with the
* following bl
*/
mov r0, lr
bl ll_get_coherency_base
bl ll_get_cpuid
mov lr, r0
add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET
1:
ldrex r2, [r0]
orr r2, r2, r3
strex r1, r2, [r0]
cmp r1, #0
bne 1b
dsb dsb
mov r0, #0 mov r0, #0
mov pc, lr mov pc, lr
ENDPROC(ll_set_cpu_coherent) ENDPROC(ll_enable_coherency)
ENTRY(ll_disable_coherency)
/*
* r0 being untouched in ll_get_coherency_base and
* ll_get_cpuid, we can use it to save lr modifing it with the
* following bl
*/
mov r0, lr
bl ll_get_coherency_base
bl ll_get_cpuid
mov lr, r0
add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET
1:
ldrex r2, [r0]
bic r2, r2, r3
strex r1, r2, [r0]
cmp r1, #0
bne 1b
dsb
mov pc, lr
ENDPROC(ll_disable_coherency)
.align 2
3:
.long coherency_phys_base - .
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
#include <linux/reboot.h> #include <linux/reboot.h>
void mvebu_restart(enum reboot_mode mode, const char *cmd); void mvebu_restart(enum reboot_mode mode, const char *cmd);
int mvebu_cpu_reset_deassert(int cpu);
void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr);
void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr);
void armada_xp_cpu_die(unsigned int cpu); void armada_xp_cpu_die(unsigned int cpu);
......
/*
* Copyright (C) 2014 Marvell
*
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#define pr_fmt(fmt) "mvebu-cpureset: " fmt
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/resource.h>
#include "armada-370-xp.h"
static void __iomem *cpu_reset_base;
static size_t cpu_reset_size;
#define CPU_RESET_OFFSET(cpu) (cpu * 0x8)
#define CPU_RESET_ASSERT BIT(0)
int mvebu_cpu_reset_deassert(int cpu)
{
u32 reg;
if (!cpu_reset_base)
return -ENODEV;
if (CPU_RESET_OFFSET(cpu) >= cpu_reset_size)
return -EINVAL;
reg = readl(cpu_reset_base + CPU_RESET_OFFSET(cpu));
reg &= ~CPU_RESET_ASSERT;
writel(reg, cpu_reset_base + CPU_RESET_OFFSET(cpu));
return 0;
}
static int mvebu_cpu_reset_map(struct device_node *np, int res_idx)
{
struct resource res;
if (of_address_to_resource(np, res_idx, &res)) {
pr_err("unable to get resource\n");
return -ENOENT;
}
if (!request_mem_region(res.start, resource_size(&res),
np->full_name)) {
pr_err("unable to request region\n");
return -EBUSY;
}
cpu_reset_base = ioremap(res.start, resource_size(&res));
if (!cpu_reset_base) {
pr_err("unable to map registers\n");
release_mem_region(res.start, resource_size(&res));
return -ENOMEM;
}
cpu_reset_size = resource_size(&res);
return 0;
}
int __init mvebu_cpu_reset_init(void)
{
struct device_node *np;
int res_idx;
int ret;
np = of_find_compatible_node(NULL, NULL,
"marvell,armada-370-cpu-reset");
if (np) {
res_idx = 0;
} else {
/*
* This code is kept for backward compatibility with
* old Device Trees.
*/
np = of_find_compatible_node(NULL, NULL,
"marvell,armada-370-xp-pmsu");
if (np) {
pr_warn(FW_WARN "deprecated pmsu binding\n");
res_idx = 1;
}
}
/* No reset node found */
if (!np)
return -ENODEV;
ret = mvebu_cpu_reset_map(np, res_idx);
of_node_put(np);
return ret;
}
early_initcall(mvebu_cpu_reset_init);
...@@ -23,7 +23,7 @@ static void __init dove_init(void) ...@@ -23,7 +23,7 @@ static void __init dove_init(void)
#ifdef CONFIG_CACHE_TAUROS2 #ifdef CONFIG_CACHE_TAUROS2
tauros2_init(0); tauros2_init(0);
#endif #endif
BUG_ON(mvebu_mbus_dt_init()); BUG_ON(mvebu_mbus_dt_init(false));
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
} }
......
/*
* SMP support: Entry point for secondary CPUs of Marvell EBU
* Cortex-A9 based SOCs (Armada 375 and Armada 38x).
*
* Copyright (C) 2014 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/linkage.h>
#include <linux/init.h>
__CPUINIT
#define CPU_RESUME_ADDR_REG 0xf10182d4
.global armada_375_smp_cpu1_enable_code_start
.global armada_375_smp_cpu1_enable_code_end
armada_375_smp_cpu1_enable_code_start:
ldr r0, [pc, #4]
ldr r1, [r0]
mov pc, r1
.word CPU_RESUME_ADDR_REG
armada_375_smp_cpu1_enable_code_end:
ENTRY(mvebu_cortex_a9_secondary_startup)
bl v7_invalidate_l1
b secondary_startup
ENDPROC(mvebu_cortex_a9_secondary_startup)
...@@ -31,21 +31,10 @@ ...@@ -31,21 +31,10 @@
ENTRY(armada_xp_secondary_startup) ENTRY(armada_xp_secondary_startup)
ARM_BE8(setend be ) @ go BE8 if entered LE ARM_BE8(setend be ) @ go BE8 if entered LE
/* Get coherency fabric base physical address */ bl ll_add_cpu_to_smp_group
adr r0, 1f
ldr r1, [r0]
ldr r0, [r0, r1]
/* Read CPU id */ bl ll_enable_coherency
mrc p15, 0, r1, c0, c0, 5
and r1, r1, #0xF
/* Add CPU to coherency fabric */
bl ll_set_cpu_coherent
b secondary_startup b secondary_startup
ENDPROC(armada_xp_secondary_startup) ENDPROC(armada_xp_secondary_startup)
.align 2
1:
.long coherency_phys_base - .
...@@ -169,7 +169,7 @@ static void __init kirkwood_dt_init(void) ...@@ -169,7 +169,7 @@ static void __init kirkwood_dt_init(void)
{ {
kirkwood_disable_mbus_error_propagation(); kirkwood_disable_mbus_error_propagation();
BUG_ON(mvebu_mbus_dt_init()); BUG_ON(mvebu_mbus_dt_init(false));
#ifdef CONFIG_CACHE_FEROCEON_L2 #ifdef CONFIG_CACHE_FEROCEON_L2
feroceon_of_init(); feroceon_of_init();
...@@ -180,9 +180,6 @@ static void __init kirkwood_dt_init(void) ...@@ -180,9 +180,6 @@ static void __init kirkwood_dt_init(void)
kirkwood_pm_init(); kirkwood_pm_init();
kirkwood_dt_eth_fixup(); kirkwood_dt_eth_fixup();
if (of_machine_is_compatible("hp,t5325"))
t5325_init();
of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL); of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL);
} }
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include "mvebu-soc-id.h" #include "mvebu-soc-id.h"
#define PCIE_DEV_ID_OFF 0x0 #define PCIE_DEV_ID_OFF 0x0
...@@ -116,5 +118,33 @@ static int __init mvebu_soc_id_init(void) ...@@ -116,5 +118,33 @@ static int __init mvebu_soc_id_init(void)
return ret; return ret;
} }
core_initcall(mvebu_soc_id_init); early_initcall(mvebu_soc_id_init);
static int __init mvebu_soc_device(void)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
/* Also protects against running on non-mvebu systems */
if (!is_id_valid)
return 0;
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
return -ENOMEM;
soc_dev_attr->family = kasprintf(GFP_KERNEL, "Marvell");
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", soc_rev);
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%X", soc_dev_id);
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
kfree(soc_dev_attr->family);
kfree(soc_dev_attr->revision);
kfree(soc_dev_attr->soc_id);
kfree(soc_dev_attr);
}
return 0;
}
postcore_initcall(mvebu_soc_device);
...@@ -20,6 +20,10 @@ ...@@ -20,6 +20,10 @@
#define MV78XX0_A0_REV 0x1 #define MV78XX0_A0_REV 0x1
#define MV78XX0_B0_REV 0x2 #define MV78XX0_B0_REV 0x2
/* Armada 375 */
#define ARMADA_375_Z1_REV 0x0
#define ARMADA_375_A0_REV 0x3
#ifdef CONFIG_ARCH_MVEBU #ifdef CONFIG_ARCH_MVEBU
int mvebu_get_soc_id(u32 *dev, u32 *rev); int mvebu_get_soc_id(u32 *dev, u32 *rev);
#else #else
......
/*
* Symmetric Multi Processing (SMP) support for Marvell EBU Cortex-A9
* based SOCs (Armada 375/38x).
*
* Copyright (C) 2014 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/smp.h>
#include <linux/mbus.h>
#include <asm/smp_scu.h>
#include <asm/smp_plat.h>
#include "common.h"
#include "mvebu-soc-id.h"
#include "pmsu.h"
#define CRYPT0_ENG_ID 41
#define CRYPT0_ENG_ATTR 0x1
#define SRAM_PHYS_BASE 0xFFFF0000
#define BOOTROM_BASE 0xFFF00000
#define BOOTROM_SIZE 0x100000
extern unsigned char armada_375_smp_cpu1_enable_code_end;
extern unsigned char armada_375_smp_cpu1_enable_code_start;
void armada_375_smp_cpu1_enable_wa(void)
{
void __iomem *sram_virt_base;
mvebu_mbus_del_window(BOOTROM_BASE, BOOTROM_SIZE);
mvebu_mbus_add_window_by_id(CRYPT0_ENG_ID, CRYPT0_ENG_ATTR,
SRAM_PHYS_BASE, SZ_64K);
sram_virt_base = ioremap(SRAM_PHYS_BASE, SZ_64K);
memcpy(sram_virt_base, &armada_375_smp_cpu1_enable_code_start,
&armada_375_smp_cpu1_enable_code_end
- &armada_375_smp_cpu1_enable_code_start);
}
extern void mvebu_cortex_a9_secondary_startup(void);
static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu,
struct task_struct *idle)
{
int ret, hw_cpu;
pr_info("Booting CPU %d\n", cpu);
/*
* Write the address of secondary startup into the system-wide
* flags register. The boot monitor waits until it receives a
* soft interrupt, and then the secondary CPU branches to this
* address.
*/
hw_cpu = cpu_logical_map(cpu);
if (of_machine_is_compatible("marvell,armada375")) {
u32 dev, rev;
if (mvebu_get_soc_id(&dev, &rev) == 0 &&
rev == ARMADA_375_Z1_REV)
armada_375_smp_cpu1_enable_wa();
mvebu_system_controller_set_cpu_boot_addr(mvebu_cortex_a9_secondary_startup);
}
else {
mvebu_pmsu_set_cpu_boot_addr(hw_cpu,
mvebu_cortex_a9_secondary_startup);
}
smp_wmb();
ret = mvebu_cpu_reset_deassert(hw_cpu);
if (ret) {
pr_err("Could not start the secondary CPU: %d\n", ret);
return ret;
}
arch_send_wakeup_ipi_mask(cpumask_of(cpu));
return 0;
}
static struct smp_operations mvebu_cortex_a9_smp_ops __initdata = {
.smp_boot_secondary = mvebu_cortex_a9_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = armada_xp_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp",
&mvebu_cortex_a9_smp_ops);
CPU_METHOD_OF_DECLARE(mvebu_armada_380_smp, "marvell,armada-380-smp",
&mvebu_cortex_a9_smp_ops);
...@@ -70,16 +70,19 @@ static void __init set_secondary_cpus_clock(void) ...@@ -70,16 +70,19 @@ static void __init set_secondary_cpus_clock(void)
} }
} }
static void armada_xp_secondary_init(unsigned int cpu)
{
armada_xp_mpic_smp_cpu_init();
}
static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle)
{ {
int ret, hw_cpu;
pr_info("Booting CPU %d\n", cpu); pr_info("Booting CPU %d\n", cpu);
armada_xp_boot_cpu(cpu, armada_xp_secondary_startup); hw_cpu = cpu_logical_map(cpu);
mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup);
ret = mvebu_cpu_reset_deassert(hw_cpu);
if (ret) {
pr_warn("unable to boot CPU: %d\n", ret);
return ret;
}
return 0; return 0;
} }
...@@ -90,8 +93,6 @@ static void __init armada_xp_smp_init_cpus(void) ...@@ -90,8 +93,6 @@ static void __init armada_xp_smp_init_cpus(void)
if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS) if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS)
panic("Invalid number of CPUs in DT\n"); panic("Invalid number of CPUs in DT\n");
set_smp_cross_call(armada_mpic_send_doorbell);
} }
static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
...@@ -102,7 +103,7 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) ...@@ -102,7 +103,7 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
set_secondary_cpus_clock(); set_secondary_cpus_clock();
flush_cache_all(); flush_cache_all();
set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); set_cpu_coherent();
/* /*
* In order to boot the secondary CPUs we need to ensure * In order to boot the secondary CPUs we need to ensure
...@@ -124,9 +125,11 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) ...@@ -124,9 +125,11 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
struct smp_operations armada_xp_smp_ops __initdata = { struct smp_operations armada_xp_smp_ops __initdata = {
.smp_init_cpus = armada_xp_smp_init_cpus, .smp_init_cpus = armada_xp_smp_init_cpus,
.smp_prepare_cpus = armada_xp_smp_prepare_cpus, .smp_prepare_cpus = armada_xp_smp_prepare_cpus,
.smp_secondary_init = armada_xp_secondary_init,
.smp_boot_secondary = armada_xp_boot_secondary, .smp_boot_secondary = armada_xp_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
.cpu_die = armada_xp_cpu_die, .cpu_die = armada_xp_cpu_die,
#endif #endif
}; };
CPU_METHOD_OF_DECLARE(armada_xp_smp, "marvell,armada-xp-smp",
&armada_xp_smp_ops);
...@@ -16,62 +16,283 @@ ...@@ -16,62 +16,283 @@
* other SOC units * other SOC units
*/ */
#define pr_fmt(fmt) "mvebu-pmsu: " fmt
#include <linux/cpu_pm.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/resource.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_plat.h> #include <asm/smp_plat.h>
#include "pmsu.h" #include <asm/suspend.h>
#include <asm/tlbflush.h>
#include "common.h"
static void __iomem *pmsu_mp_base; static void __iomem *pmsu_mp_base;
static void __iomem *pmsu_reset_base;
#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x24) #define PMSU_BASE_OFFSET 0x100
#define PMSU_RESET_CTL_OFFSET(cpu) (cpu * 0x8) #define PMSU_REG_SIZE 0x1000
/* PMSU MP registers */
#define PMSU_CONTROL_AND_CONFIG(cpu) ((cpu * 0x100) + 0x104)
#define PMSU_CONTROL_AND_CONFIG_DFS_REQ BIT(18)
#define PMSU_CONTROL_AND_CONFIG_PWDDN_REQ BIT(16)
#define PMSU_CONTROL_AND_CONFIG_L2_PWDDN BIT(20)
#define PMSU_CPU_POWER_DOWN_CONTROL(cpu) ((cpu * 0x100) + 0x108)
#define PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP BIT(0)
#define PMSU_STATUS_AND_MASK(cpu) ((cpu * 0x100) + 0x10c)
#define PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT BIT(16)
#define PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT BIT(17)
#define PMSU_STATUS_AND_MASK_IRQ_WAKEUP BIT(20)
#define PMSU_STATUS_AND_MASK_FIQ_WAKEUP BIT(21)
#define PMSU_STATUS_AND_MASK_DBG_WAKEUP BIT(22)
#define PMSU_STATUS_AND_MASK_IRQ_MASK BIT(24)
#define PMSU_STATUS_AND_MASK_FIQ_MASK BIT(25)
#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124)
/* PMSU fabric registers */
#define L2C_NFABRIC_PM_CTL 0x4
#define L2C_NFABRIC_PM_CTL_PWR_DOWN BIT(20)
extern void ll_disable_coherency(void);
extern void ll_enable_coherency(void);
static struct platform_device armada_xp_cpuidle_device = {
.name = "cpuidle-armada-370-xp",
};
static struct of_device_id of_pmsu_table[] = { static struct of_device_id of_pmsu_table[] = {
{.compatible = "marvell,armada-370-xp-pmsu"}, { .compatible = "marvell,armada-370-pmsu", },
{ .compatible = "marvell,armada-370-xp-pmsu", },
{ .compatible = "marvell,armada-380-pmsu", },
{ /* end of list */ }, { /* end of list */ },
}; };
#ifdef CONFIG_SMP void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr)
{ {
int reg, hw_cpu; writel(virt_to_phys(boot_addr), pmsu_mp_base +
PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu));
}
static int __init armada_370_xp_pmsu_init(void)
{
struct device_node *np;
struct resource res;
int ret = 0;
np = of_find_matching_node(NULL, of_pmsu_table);
if (!np)
return 0;
pr_info("Initializing Power Management Service Unit\n");
if (!pmsu_mp_base || !pmsu_reset_base) { if (of_address_to_resource(np, 0, &res)) {
pr_warn("Can't boot CPU. PMSU is uninitialized\n"); pr_err("unable to get resource\n");
return 1; ret = -ENOENT;
goto out;
} }
hw_cpu = cpu_logical_map(cpu_id); if (of_device_is_compatible(np, "marvell,armada-370-xp-pmsu")) {
pr_warn(FW_WARN "deprecated pmsu binding\n");
res.start = res.start - PMSU_BASE_OFFSET;
res.end = res.start + PMSU_REG_SIZE - 1;
}
writel(virt_to_phys(boot_addr), pmsu_mp_base + if (!request_mem_region(res.start, resource_size(&res),
PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); np->full_name)) {
pr_err("unable to request region\n");
ret = -EBUSY;
goto out;
}
pmsu_mp_base = ioremap(res.start, resource_size(&res));
if (!pmsu_mp_base) {
pr_err("unable to map registers\n");
release_mem_region(res.start, resource_size(&res));
ret = -ENOMEM;
goto out;
}
out:
of_node_put(np);
return ret;
}
static void armada_370_xp_pmsu_enable_l2_powerdown_onidle(void)
{
u32 reg;
if (pmsu_mp_base == NULL)
return;
/* Enable L2 & Fabric powerdown in Deep-Idle mode - Fabric */
reg = readl(pmsu_mp_base + L2C_NFABRIC_PM_CTL);
reg |= L2C_NFABRIC_PM_CTL_PWR_DOWN;
writel(reg, pmsu_mp_base + L2C_NFABRIC_PM_CTL);
}
static void armada_370_xp_cpu_resume(void)
{
asm volatile("bl ll_add_cpu_to_smp_group\n\t"
"bl ll_enable_coherency\n\t"
"b cpu_resume\n\t");
}
/* No locking is needed because we only access per-CPU registers */
void armada_370_xp_pmsu_idle_prepare(bool deepidle)
{
unsigned int hw_cpu = cpu_logical_map(smp_processor_id());
u32 reg;
if (pmsu_mp_base == NULL)
return;
/* Release CPU from reset by clearing reset bit*/ /*
reg = readl(pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); * Adjust the PMSU configuration to wait for WFI signal, enable
reg &= (~0x1); * IRQ and FIQ as wakeup events, set wait for snoop queue empty
writel(reg, pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); * indication and mask IRQ and FIQ from CPU
*/
reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu));
reg |= PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT |
PMSU_STATUS_AND_MASK_IRQ_WAKEUP |
PMSU_STATUS_AND_MASK_FIQ_WAKEUP |
PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT |
PMSU_STATUS_AND_MASK_IRQ_MASK |
PMSU_STATUS_AND_MASK_FIQ_MASK;
writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu));
reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu));
/* ask HW to power down the L2 Cache if needed */
if (deepidle)
reg |= PMSU_CONTROL_AND_CONFIG_L2_PWDDN;
/* request power down */
reg |= PMSU_CONTROL_AND_CONFIG_PWDDN_REQ;
writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu));
/* Disable snoop disable by HW - SW is taking care of it */
reg = readl(pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu));
reg |= PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP;
writel(reg, pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu));
}
static noinline int do_armada_370_xp_cpu_suspend(unsigned long deepidle)
{
armada_370_xp_pmsu_idle_prepare(deepidle);
v7_exit_coherency_flush(all);
ll_disable_coherency();
dsb();
wfi();
/* If we are here, wfi failed. As processors run out of
* coherency for some time, tlbs might be stale, so flush them
*/
local_flush_tlb_all();
ll_enable_coherency();
/* Test the CR_C bit and set it if it was cleared */
asm volatile(
"mrc p15, 0, %0, c1, c0, 0 \n\t"
"tst %0, #(1 << 2) \n\t"
"orreq %0, %0, #(1 << 2) \n\t"
"mcreq p15, 0, %0, c1, c0, 0 \n\t"
"isb "
: : "r" (0));
pr_warn("Failed to suspend the system\n");
return 0; return 0;
} }
#endif
static int __init armada_370_xp_pmsu_init(void) static int armada_370_xp_cpu_suspend(unsigned long deepidle)
{
return cpu_suspend(deepidle, do_armada_370_xp_cpu_suspend);
}
/* No locking is needed because we only access per-CPU registers */
static noinline void armada_370_xp_pmsu_idle_restore(void)
{
unsigned int hw_cpu = cpu_logical_map(smp_processor_id());
u32 reg;
if (pmsu_mp_base == NULL)
return;
/* cancel ask HW to power down the L2 Cache if possible */
reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu));
reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN;
writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu));
/* cancel Enable wakeup events and mask interrupts */
reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu));
reg &= ~(PMSU_STATUS_AND_MASK_IRQ_WAKEUP | PMSU_STATUS_AND_MASK_FIQ_WAKEUP);
reg &= ~PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT;
reg &= ~PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT;
reg &= ~(PMSU_STATUS_AND_MASK_IRQ_MASK | PMSU_STATUS_AND_MASK_FIQ_MASK);
writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu));
}
static int armada_370_xp_cpu_pm_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
if (action == CPU_PM_ENTER) {
unsigned int hw_cpu = cpu_logical_map(smp_processor_id());
mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_370_xp_cpu_resume);
} else if (action == CPU_PM_EXIT) {
armada_370_xp_pmsu_idle_restore();
}
return NOTIFY_OK;
}
static struct notifier_block armada_370_xp_cpu_pm_notifier = {
.notifier_call = armada_370_xp_cpu_pm_notify,
};
int __init armada_370_xp_cpu_pm_init(void)
{ {
struct device_node *np; struct device_node *np;
/*
* Check that all the requirements are available to enable
* cpuidle. So far, it is only supported on Armada XP, cpuidle
* needs the coherency fabric and the PMSU enabled
*/
if (!of_machine_is_compatible("marvell,armadaxp"))
return 0;
np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric");
if (!np)
return 0;
of_node_put(np);
np = of_find_matching_node(NULL, of_pmsu_table); np = of_find_matching_node(NULL, of_pmsu_table);
if (np) { if (!np)
pr_info("Initializing Power Management Service Unit\n"); return 0;
pmsu_mp_base = of_iomap(np, 0); of_node_put(np);
pmsu_reset_base = of_iomap(np, 1);
of_node_put(np); armada_370_xp_pmsu_enable_l2_powerdown_onidle();
} armada_xp_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend;
platform_device_register(&armada_xp_cpuidle_device);
cpu_pm_register_notifier(&armada_370_xp_cpu_pm_notifier);
return 0; return 0;
} }
arch_initcall(armada_370_xp_cpu_pm_init);
early_initcall(armada_370_xp_pmsu_init); early_initcall(armada_370_xp_pmsu_init);
...@@ -37,6 +37,8 @@ struct mvebu_system_controller { ...@@ -37,6 +37,8 @@ struct mvebu_system_controller {
u32 rstoutn_mask_reset_out_en; u32 rstoutn_mask_reset_out_en;
u32 system_soft_reset; u32 system_soft_reset;
u32 resume_boot_addr;
}; };
static struct mvebu_system_controller *mvebu_sc; static struct mvebu_system_controller *mvebu_sc;
...@@ -52,6 +54,7 @@ static const struct mvebu_system_controller armada_375_system_controller = { ...@@ -52,6 +54,7 @@ static const struct mvebu_system_controller armada_375_system_controller = {
.system_soft_reset_offset = 0x58, .system_soft_reset_offset = 0x58,
.rstoutn_mask_reset_out_en = 0x1, .rstoutn_mask_reset_out_en = 0x1,
.system_soft_reset = 0x1, .system_soft_reset = 0x1,
.resume_boot_addr = 0xd4,
}; };
static const struct mvebu_system_controller orion_system_controller = { static const struct mvebu_system_controller orion_system_controller = {
...@@ -98,6 +101,16 @@ void mvebu_restart(enum reboot_mode mode, const char *cmd) ...@@ -98,6 +101,16 @@ void mvebu_restart(enum reboot_mode mode, const char *cmd)
; ;
} }
#ifdef CONFIG_SMP
void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr)
{
BUG_ON(system_controller_base == NULL);
BUG_ON(mvebu_sc->resume_boot_addr == 0);
writel(virt_to_phys(boot_addr), system_controller_base +
mvebu_sc->resume_boot_addr);
}
#endif
static int __init mvebu_system_controller_init(void) static int __init mvebu_system_controller_init(void)
{ {
const struct of_device_id *match; const struct of_device_id *match;
...@@ -114,4 +127,4 @@ static int __init mvebu_system_controller_init(void) ...@@ -114,4 +127,4 @@ static int __init mvebu_system_controller_init(void)
return 0; return 0;
} }
arch_initcall(mvebu_system_controller_init); early_initcall(mvebu_system_controller_init);
...@@ -694,7 +694,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, ...@@ -694,7 +694,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
phys_addr_t sdramwins_phys_base, phys_addr_t sdramwins_phys_base,
size_t sdramwins_size) size_t sdramwins_size)
{ {
struct device_node *np;
int win; int win;
mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size); mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size);
...@@ -707,12 +706,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, ...@@ -707,12 +706,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
return -ENOMEM; return -ENOMEM;
} }
np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric");
if (np) {
mbus->hw_io_coherency = 1;
of_node_put(np);
}
for (win = 0; win < mbus->soc->num_wins; win++) for (win = 0; win < mbus->soc->num_wins; win++)
mvebu_mbus_disable_window(mbus, win); mvebu_mbus_disable_window(mbus, win);
...@@ -882,7 +875,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, ...@@ -882,7 +875,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np,
} }
} }
int __init mvebu_mbus_dt_init(void) int __init mvebu_mbus_dt_init(bool is_coherent)
{ {
struct resource mbuswins_res, sdramwins_res; struct resource mbuswins_res, sdramwins_res;
struct device_node *np, *controller; struct device_node *np, *controller;
...@@ -920,6 +913,8 @@ int __init mvebu_mbus_dt_init(void) ...@@ -920,6 +913,8 @@ int __init mvebu_mbus_dt_init(void)
return -EINVAL; return -EINVAL;
} }
mbus_state.hw_io_coherency = is_coherent;
/* Get optional pcie-{mem,io}-aperture properties */ /* Get optional pcie-{mem,io}-aperture properties */
mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture, mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture,
&mbus_state.pcie_io_aperture); &mbus_state.pcie_io_aperture);
......
# #
# ARM CPU Idle drivers # ARM CPU Idle drivers
# #
config ARM_ARMADA_370_XP_CPUIDLE
bool "CPU Idle Driver for Armada 370/XP family processors"
depends on ARCH_MVEBU
help
Select this to enable cpuidle on Armada 370/XP processors.
config ARM_BIG_LITTLE_CPUIDLE config ARM_BIG_LITTLE_CPUIDLE
bool "Support for ARM big.LITTLE processors" bool "Support for ARM big.LITTLE processors"
......
...@@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o ...@@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
################################################################################## ##################################################################################
# ARM SoC drivers # ARM SoC drivers
obj-$(CONFIG_ARM_ARMADA_370_XP_CPUIDLE) += cpuidle-armada-370-xp.o
obj-$(CONFIG_ARM_BIG_LITTLE_CPUIDLE) += cpuidle-big_little.o obj-$(CONFIG_ARM_BIG_LITTLE_CPUIDLE) += cpuidle-big_little.o
obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o
obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o
......
/*
* Marvell Armada 370 and Armada XP SoC cpuidle driver
*
* Copyright (C) 2014 Marvell
*
* Nadav Haklai <nadavh@marvell.com>
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* Maintainer: Gregory CLEMENT <gregory.clement@free-electrons.com>
*/
#include <linux/cpu_pm.h>
#include <linux/cpuidle.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/suspend.h>
#include <linux/platform_device.h>
#include <asm/cpuidle.h>
#define ARMADA_370_XP_MAX_STATES 3
#define ARMADA_370_XP_FLAG_DEEP_IDLE 0x10000
static int (*armada_370_xp_cpu_suspend)(int);
static int armada_370_xp_enter_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
int ret;
bool deepidle = false;
cpu_pm_enter();
if (drv->states[index].flags & ARMADA_370_XP_FLAG_DEEP_IDLE)
deepidle = true;
ret = armada_370_xp_cpu_suspend(deepidle);
if (ret)
return ret;
cpu_pm_exit();
return index;
}
static struct cpuidle_driver armada_370_xp_idle_driver = {
.name = "armada_370_xp_idle",
.states[0] = ARM_CPUIDLE_WFI_STATE,
.states[1] = {
.enter = armada_370_xp_enter_idle,
.exit_latency = 10,
.power_usage = 50,
.target_residency = 100,
.flags = CPUIDLE_FLAG_TIME_VALID,
.name = "MV CPU IDLE",
.desc = "CPU power down",
},
.states[2] = {
.enter = armada_370_xp_enter_idle,
.exit_latency = 100,
.power_usage = 5,
.target_residency = 1000,
.flags = CPUIDLE_FLAG_TIME_VALID |
ARMADA_370_XP_FLAG_DEEP_IDLE,
.name = "MV CPU DEEP IDLE",
.desc = "CPU and L2 Fabric power down",
},
.state_count = ARMADA_370_XP_MAX_STATES,
};
static int armada_370_xp_cpuidle_probe(struct platform_device *pdev)
{
armada_370_xp_cpu_suspend = (void *)(pdev->dev.platform_data);
return cpuidle_register(&armada_370_xp_idle_driver, NULL);
}
static struct platform_driver armada_370_xp_cpuidle_plat_driver = {
.driver = {
.name = "cpuidle-armada-370-xp",
.owner = THIS_MODULE,
},
.probe = armada_370_xp_cpuidle_probe,
};
module_platform_driver(armada_370_xp_cpuidle_plat_driver);
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
MODULE_DESCRIPTION("Armada 370/XP cpu idle driver");
MODULE_LICENSE("GPL");
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
#include <linux/cpu.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
...@@ -310,7 +311,8 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, ...@@ -310,7 +311,8 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
} }
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) static void armada_mpic_send_doorbell(const struct cpumask *mask,
unsigned int irq)
{ {
int cpu; int cpu;
unsigned long map = 0; unsigned long map = 0;
...@@ -330,7 +332,7 @@ void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) ...@@ -330,7 +332,7 @@ void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq)
ARMADA_370_XP_SW_TRIG_INT_OFFS); ARMADA_370_XP_SW_TRIG_INT_OFFS);
} }
void armada_xp_mpic_smp_cpu_init(void) static void armada_xp_mpic_smp_cpu_init(void)
{ {
/* Clear pending IPIs */ /* Clear pending IPIs */
writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
...@@ -342,6 +344,20 @@ void armada_xp_mpic_smp_cpu_init(void) ...@@ -342,6 +344,20 @@ void armada_xp_mpic_smp_cpu_init(void)
/* Unmask IPI interrupt */ /* Unmask IPI interrupt */
writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
} }
static int armada_xp_mpic_secondary_init(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
armada_xp_mpic_smp_cpu_init();
return NOTIFY_OK;
}
static struct notifier_block armada_370_xp_mpic_cpu_notifier = {
.notifier_call = armada_xp_mpic_secondary_init,
.priority = 100,
};
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
...@@ -497,6 +513,10 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, ...@@ -497,6 +513,10 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
if (parent_irq <= 0) { if (parent_irq <= 0) {
irq_set_default_host(armada_370_xp_mpic_domain); irq_set_default_host(armada_370_xp_mpic_domain);
set_handle_irq(armada_370_xp_handle_irq); set_handle_irq(armada_370_xp_handle_irq);
#ifdef CONFIG_SMP
set_smp_cross_call(armada_mpic_send_doorbell);
register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier);
#endif
} else { } else {
irq_set_chained_handler(parent_irq, irq_set_chained_handler(parent_irq,
armada_370_xp_mpic_handle_cascade_irq); armada_370_xp_mpic_handle_cascade_irq);
......
...@@ -42,7 +42,7 @@ __exception_irq_entry orion_handle_irq(struct pt_regs *regs) ...@@ -42,7 +42,7 @@ __exception_irq_entry orion_handle_irq(struct pt_regs *regs)
u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) & u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) &
gc->mask_cache; gc->mask_cache;
while (stat) { while (stat) {
u32 hwirq = ffs(stat) - 1; u32 hwirq = __fls(stat);
u32 irq = irq_find_mapping(orion_irq_domain, u32 irq = irq_find_mapping(orion_irq_domain,
gc->irq_base + hwirq); gc->irq_base + hwirq);
handle_IRQ(irq, regs); handle_IRQ(irq, regs);
...@@ -117,7 +117,7 @@ static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc) ...@@ -117,7 +117,7 @@ static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc)
gc->mask_cache; gc->mask_cache;
while (stat) { while (stat) {
u32 hwirq = ffs(stat) - 1; u32 hwirq = __fls(stat);
generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq));
stat &= ~(1 << hwirq); stat &= ~(1 << hwirq);
......
...@@ -73,6 +73,6 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size); ...@@ -73,6 +73,6 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size);
int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base, int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base,
size_t mbus_size, phys_addr_t sdram_phys_base, size_t mbus_size, phys_addr_t sdram_phys_base,
size_t sdram_size); size_t sdram_size);
int mvebu_mbus_dt_init(void); int mvebu_mbus_dt_init(bool is_coherent);
#endif /* __LINUX_MBUS_H */ #endif /* __LINUX_MBUS_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment