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.
 
 Depends:
  - tags/irqchip-mvebu-3.16 in the mvebu/irqchip branch (tglx already pulled) for:
 
     d7df84b3ce irqchip: irq-armada-370-xp: Use cpu notifier to initialize secondary CPUs
     ef37d337e1 irqchip: irq-armada-370-xp: Do the set_smp_cross_call() in the driver
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJTbOGvAAoJEP45WPkGe8Zn53UP/jn5IEn7T/wMKt+m+K4HTPH4
 tLbimH60wKtBDK+R978GqaKibgzqjBy/CeGcV9ip7NCOAVZTXBi3bZW/bHHw9azu
 pCUAUBdX6uAt6Ua+SxtyrI3nqI5g8QrQhg7Loa08r34buL07qRIbR7TwR13cr5pV
 w5svXJeb4bdnvPmMlpzMYFAfvYhUxf5S0aALh1hLzeMsfEq4pfBH581UZQliPAus
 8MVE/eeiam+6wI2mpNyxedaevLJ875SDZo8n7r4yytGvoexIfegvd9GIwKRLe06z
 Owqn05PkU0Zo+X4FSQBWZ81DcRNKP+D3gWJkN7pTVRWNjNVGJtZTkQX4Cyo6JZiX
 0Qz9APp8ZzrnzG4uhYdq0vwlgiMgd5KoxzMF8Wbid3JW+NEMST6QnNqDmF1R86s6
 K4f/DDJtQU9fonicNM8yPlGYvBCBO7Jbb5hoc5QaxTTHSv0hFJVwWtoejwtnhmJA
 wcvTu+oGKmF4nM63zV2P2YbWF0FahGS4ssm2VWk9OsZuXzG4AAV3QGP85qw+3QuV
 ry/GjqT81ExC04KnXsanFz7nw2a74DU1UVZgpkyKEJbS0zcNvr+6u9AIA1zmoF5P
 Nmjwjn4nsC9Y2YuabSGMkM11gCUjFjIG+fA6E5/m5rBwH8UtgPHyLKIt/2nXm6DG
 LVNzCvQpYeh1NTA6Mro6
 =wNp0
 -----END PGP SIGNATURE-----

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: Olof Johansson <olof@lixom.net>
This commit is contained in:
Olof Johansson 2014-05-19 21:59:55 -07:00
commit 5df22a6148
33 changed files with 1181 additions and 209 deletions

View File

@ -1,20 +1,21 @@
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:
- 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
for the per-CPU SW Reset Control registers, second pair for the
Power Management Service Unit.
- reg: Should contain PMSU registers location and length.
Example:
armada-370-xp-pmsu@d0022000 {
compatible = "marvell,armada-370-xp-pmsu";
reg = <0xd0022100 0x430>,
<0xd0020800 0x20>;
armada-370-xp-pmsu@22000 {
compatible = "marvell,armada-370-pmsu";
reg = <0x22000 0x1000>;
};

View File

@ -0,0 +1,14 @@
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>;
};

View File

@ -1,16 +1,33 @@
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:
- 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
length. First pair for the coherency fabric registers, second pair
for the per-CPU fabric registers registers.
length.
Example:
* For "marvell,coherency-fabric", the first pair for the coherency
fabric registers, second pair for the per-CPU fabric registers.
* 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 {
compatible = "marvell,coherency-fabric";
@ -19,3 +36,8 @@ coherency-fabric@d0020200 {
};
coherency-fabric@21810 {
compatible = "marvell,armada-375-coherency-fabric";
reg = <0x21810 0x1c>;
};

View File

@ -185,6 +185,9 @@ nodes to be present and contain the properties described below.
"qcom,gcc-msm8660"
"qcom,kpss-acc-v1"
"qcom,kpss-acc-v2"
"marvell,armada-375-smp"
"marvell,armada-380-smp"
"marvell,armada-xp-smp"
- cpu-release-addr
Usage: required for systems that have an "enable-method"

View File

@ -195,7 +195,7 @@ static void __init kirkwood_dt_init(void)
{
kirkwood_disable_mbus_error_propagation();
BUG_ON(mvebu_mbus_dt_init());
BUG_ON(mvebu_mbus_dt_init(false));
#ifdef CONFIG_CACHE_FEROCEON_L2
feroceon_of_init();

View File

@ -6,6 +6,7 @@ config ARCH_MVEBU
select IRQ_DOMAIN
select PINCTRL
select PLAT_ORION
select SOC_BUS
select MVEBU_MBUS
select ZONE_DMA if ARM_LPAE
select ARCH_REQUIRE_GPIOLIB
@ -39,6 +40,9 @@ config MACH_ARMADA_375
select ARM_GIC
select ARMADA_375_CLK
select CPU_V7
select HAVE_ARM_SCU
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select MACH_MVEBU_V7
select PINCTRL_ARMADA_375
help
@ -52,6 +56,9 @@ config MACH_ARMADA_38X
select ARM_GIC
select ARMADA_38X_CLK
select CPU_V7
select HAVE_ARM_SCU
select HAVE_ARM_TWD if SMP
select HAVE_SMP
select MACH_MVEBU_V7
select PINCTRL_ARMADA_38X
help
@ -97,13 +104,6 @@ config MACH_KIRKWOOD
Say 'Y' here if you want your kernel to support boards based
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
endif

View File

@ -2,12 +2,15 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \
-I$(srctree)/arch/arm/plat-orion/include
AFLAGS_coherency_ll.o := -Wa,-march=armv7-a
CFLAGS_pmsu.o := -march=armv7-a
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_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_T5325) += board-t5325.o

View File

@ -20,8 +20,6 @@
#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);
extern struct smp_operations armada_xp_smp_ops;
#endif

View File

@ -1,41 +0,0 @@
/*
* 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);
}

View File

@ -27,11 +27,29 @@
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/smp_scu.h>
#include "armada-370-xp.h"
#include "common.h"
#include "coherency.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
* 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)
{
of_clk_init(NULL);
clocksource_of_init();
mvebu_scu_enable();
coherency_init();
BUG_ON(mvebu_mbus_dt_init());
#ifdef CONFIG_CACHE_L2X0
BUG_ON(mvebu_mbus_dt_init(coherency_available()));
l2x0_of_init(0, ~0UL);
#endif
if (of_machine_is_compatible("marvell,armada375"))
hook_fault_code(16 + 6, armada_375_external_abort_wa, SIGBUS, 0,
@ -78,7 +95,7 @@ static void __init i2c_quirk(void)
* mechanism. We can exit only if we are sure that we can
* 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;
for_each_compatible_node(np, NULL, "marvell,mv78230-i2c") {
@ -96,10 +113,66 @@ static void __init i2c_quirk(void)
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)
{
if (of_machine_is_compatible("plathome,openblocks-ax3-4"))
i2c_quirk();
if (of_machine_is_compatible("marvell,a375-db"))
thermal_quirk();
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
@ -123,6 +196,7 @@ static const char * const armada_375_dt_compat[] = {
DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)")
.init_time = mvebu_timer_and_clk_init,
.init_machine = mvebu_dt_init,
.restart = mvebu_restart,
.dt_compat = armada_375_dt_compat,
MACHINE_END

View File

@ -13,10 +13,4 @@
#ifndef __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

View File

@ -17,6 +17,8 @@
* supplies basic routines for configuring and controlling hardware coherency
*/
#define pr_fmt(fmt) "mvebu-coherency: " fmt
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_address.h>
@ -24,13 +26,17 @@
#include <linux/smp.h>
#include <linux/dma-mapping.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/cacheflush.h>
#include "armada-370-xp.h"
#include "coherency.h"
#include "mvebu-soc-id.h"
unsigned long coherency_phys_base;
static void __iomem *coherency_base;
void __iomem *coherency_base;
static void __iomem *coherency_cpu_base;
/* Coherency fabric registers */
@ -38,27 +44,190 @@ static void __iomem *coherency_cpu_base;
#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[] = {
{.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 */ },
};
/* Function defined in coherency_ll.S */
int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id);
/* Functions defined in coherency_ll.S */
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) {
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");
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)
{
if (coherency_wa_enabled) {
mvebu_hwcc_armada375_sync_io_barrier_wa();
return;
}
writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET);
while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1);
}
@ -121,42 +290,93 @@ static struct notifier_block mvebu_hwcc_platform_nb = {
.notifier_call = mvebu_hwcc_platform_notifier,
};
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;
const struct of_device_id *match;
np = of_find_matching_node_and_match(NULL, of_coherency_table, &match);
if (np) {
int type = (int) match->data;
/* Armada 370/XP coherency works in both UP and SMP */
if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
return type;
/* Armada 375 coherency works only on SMP */
else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 && is_smp())
return type;
/* Armada 380 coherency works only on SMP */
else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380 && is_smp())
return type;
}
return COHERENCY_FABRIC_TYPE_NONE;
}
int coherency_available(void)
{
return coherency_type() != COHERENCY_FABRIC_TYPE_NONE;
}
int __init coherency_init(void)
{
int type = coherency_type();
struct device_node *np;
np = of_find_matching_node(NULL, of_coherency_table);
if (np) {
struct resource res;
pr_info("Initializing Coherency fabric\n");
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(cpu_logical_map(smp_processor_id()), 0);
of_node_put(np);
}
if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP)
armada_370_coherency_init(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)
{
struct device_node *np;
int type = coherency_type();
np = of_find_matching_node(NULL, of_coherency_table);
if (np) {
bus_register_notifier(&platform_bus_type,
&mvebu_hwcc_platform_nb);
of_node_put(np);
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;
}

View File

@ -15,8 +15,9 @@
#define __MACH_370_XP_COHERENCY_H
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_available(void);
#endif /* __MACH_370_XP_COHERENCY_H */

View File

@ -21,38 +21,108 @@
#define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4
#include <asm/assembler.h>
#include <asm/cp15.h>
.text
/*
* r0: Coherency fabric base register address
* r1: HW CPU id
*/
ENTRY(ll_set_cpu_coherent)
/* Create bit by cpu index */
mov r3, #(1 << 24)
lsl r1, r3, r1
/* Returns with the coherency address in r1 (r0 is untouched)*/
ENTRY(ll_get_coherency_base)
mrc p15, 0, r1, c1, c0, 0
tst r1, #CR_M @ Check MMU bit enabled
bne 1f
/* use physical address of the coherency register */
adr r1, 3f
ldr r3, [r1]
ldr r1, [r1, r3]
b 2f
1:
/* use virtual address of the coherency register */
ldr r1, =coherency_base
ldr r1, [r1]
2:
mov pc, lr
ENDPROC(ll_get_coherency_base)
/* 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)
/* Add CPU to SMP group - Atomic */
add r3, r0, #ARMADA_XP_CFB_CTL_REG_OFFSET
/* 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:
ldrex r2, [r3]
orr r2, r2, r1
strex r0, r2, [r3]
cmp r0, #0
bne 1b
ldrex r2, [r0]
orr r2, r2, r3
strex r1, r2, [r0]
cmp r1, #0
bne 1b
mov pc, lr
ENDPROC(ll_add_cpu_to_smp_group)
/* Enable coherency on CPU - Atomic */
add r3, r3, #ARMADA_XP_CFB_CFG_REG_OFFSET
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, [r3]
orr r2, r2, r1
strex r0, r2, [r3]
cmp r0, #0
bne 1b
ldrex r2, [r0]
orr r2, r2, r3
strex r1, r2, [r0]
cmp r1, #0
bne 1b
dsb
mov r0, #0
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 - .

View File

@ -18,6 +18,9 @@
#include <linux/reboot.h>
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);

View File

@ -0,0 +1,103 @@
/*
* 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);

View File

@ -23,7 +23,7 @@ static void __init dove_init(void)
#ifdef CONFIG_CACHE_TAUROS2
tauros2_init(0);
#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);
}

View File

@ -0,0 +1,34 @@
/*
* 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)

View File

@ -31,21 +31,10 @@
ENTRY(armada_xp_secondary_startup)
ARM_BE8(setend be ) @ go BE8 if entered LE
/* Get coherency fabric base physical address */
adr r0, 1f
ldr r1, [r0]
ldr r0, [r0, r1]
bl ll_add_cpu_to_smp_group
/* Read CPU id */
mrc p15, 0, r1, c0, c0, 5
and r1, r1, #0xF
bl ll_enable_coherency
/* Add CPU to coherency fabric */
bl ll_set_cpu_coherent
b secondary_startup
ENDPROC(armada_xp_secondary_startup)
.align 2
1:
.long coherency_phys_base - .

View File

@ -169,7 +169,7 @@ static void __init kirkwood_dt_init(void)
{
kirkwood_disable_mbus_error_propagation();
BUG_ON(mvebu_mbus_dt_init());
BUG_ON(mvebu_mbus_dt_init(false));
#ifdef CONFIG_CACHE_FEROCEON_L2
feroceon_of_init();
@ -180,9 +180,6 @@ static void __init kirkwood_dt_init(void)
kirkwood_pm_init();
kirkwood_dt_eth_fixup();
if (of_machine_is_compatible("hp,t5325"))
t5325_init();
of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL);
}

View File

@ -23,6 +23,8 @@
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include "mvebu-soc-id.h"
#define PCIE_DEV_ID_OFF 0x0
@ -116,5 +118,33 @@ clk_err:
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);

View File

@ -20,6 +20,10 @@
#define MV78XX0_A0_REV 0x1
#define MV78XX0_B0_REV 0x2
/* Armada 375 */
#define ARMADA_375_Z1_REV 0x0
#define ARMADA_375_A0_REV 0x3
#ifdef CONFIG_ARCH_MVEBU
int mvebu_get_soc_id(u32 *dev, u32 *rev);
#else

View File

@ -0,0 +1,102 @@
/*
* 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);

View File

@ -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)
{
int ret, hw_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;
}
@ -90,8 +93,6 @@ static void __init armada_xp_smp_init_cpus(void)
if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS)
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)
@ -102,7 +103,7 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
set_secondary_cpus_clock();
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
@ -124,9 +125,11 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
struct smp_operations armada_xp_smp_ops __initdata = {
.smp_init_cpus = armada_xp_smp_init_cpus,
.smp_prepare_cpus = armada_xp_smp_prepare_cpus,
.smp_secondary_init = armada_xp_secondary_init,
.smp_boot_secondary = armada_xp_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = armada_xp_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(armada_xp_smp, "marvell,armada-xp-smp",
&armada_xp_smp_ops);

View File

@ -16,62 +16,283 @@
* other SOC units
*/
#define pr_fmt(fmt) "mvebu-pmsu: " fmt
#include <linux/cpu_pm.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/smp.h>
#include <linux/resource.h>
#include <asm/cacheflush.h>
#include <asm/cp15.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_reset_base;
#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x24)
#define PMSU_RESET_CTL_OFFSET(cpu) (cpu * 0x8)
#define PMSU_BASE_OFFSET 0x100
#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[] = {
{.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 */ },
};
#ifdef CONFIG_SMP
int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr)
void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
{
int reg, hw_cpu;
if (!pmsu_mp_base || !pmsu_reset_base) {
pr_warn("Can't boot CPU. PMSU is uninitialized\n");
return 1;
}
hw_cpu = cpu_logical_map(cpu_id);
writel(virt_to_phys(boot_addr), pmsu_mp_base +
PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu));
/* Release CPU from reset by clearing reset bit*/
reg = readl(pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu));
reg &= (~0x1);
writel(reg, pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu));
return 0;
PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu));
}
#endif
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) {
pr_info("Initializing Power Management Service Unit\n");
pmsu_mp_base = of_iomap(np, 0);
pmsu_reset_base = of_iomap(np, 1);
of_node_put(np);
if (!np)
return 0;
pr_info("Initializing Power Management Service Unit\n");
if (of_address_to_resource(np, 0, &res)) {
pr_err("unable to get resource\n");
ret = -ENOENT;
goto out;
}
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;
}
if (!request_mem_region(res.start, resource_size(&res),
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;
/*
* Adjust the PMSU configuration to wait for WFI signal, enable
* IRQ and FIQ as wakeup events, set wait for snoop queue empty
* 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;
}
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;
/*
* 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);
if (!np)
return 0;
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;
}
arch_initcall(armada_370_xp_cpu_pm_init);
early_initcall(armada_370_xp_pmsu_init);

View File

@ -37,6 +37,8 @@ struct mvebu_system_controller {
u32 rstoutn_mask_reset_out_en;
u32 system_soft_reset;
u32 resume_boot_addr;
};
static struct mvebu_system_controller *mvebu_sc;
@ -52,6 +54,7 @@ static const struct mvebu_system_controller armada_375_system_controller = {
.system_soft_reset_offset = 0x58,
.rstoutn_mask_reset_out_en = 0x1,
.system_soft_reset = 0x1,
.resume_boot_addr = 0xd4,
};
static const struct mvebu_system_controller orion_system_controller = {
@ -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)
{
const struct of_device_id *match;
@ -114,4 +127,4 @@ static int __init mvebu_system_controller_init(void)
return 0;
}
arch_initcall(mvebu_system_controller_init);
early_initcall(mvebu_system_controller_init);

View File

@ -694,7 +694,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
phys_addr_t sdramwins_phys_base,
size_t sdramwins_size)
{
struct device_node *np;
int win;
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,
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++)
mvebu_mbus_disable_window(mbus, win);
@ -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 device_node *np, *controller;
@ -920,6 +913,8 @@ int __init mvebu_mbus_dt_init(void)
return -EINVAL;
}
mbus_state.hw_io_coherency = is_coherent;
/* Get optional pcie-{mem,io}-aperture properties */
mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture,
&mbus_state.pcie_io_aperture);

View File

@ -1,6 +1,11 @@
#
# 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
bool "Support for ARM big.LITTLE processors"

View File

@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
##################################################################################
# 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_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o
obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o

View File

@ -0,0 +1,93 @@
/*
* 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");

View File

@ -19,6 +19,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/cpu.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@ -310,7 +311,8 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
}
#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;
unsigned long map = 0;
@ -330,7 +332,7 @@ void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq)
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 */
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)
/* Unmask IPI interrupt */
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 */
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,
if (parent_irq <= 0) {
irq_set_default_host(armada_370_xp_mpic_domain);
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 {
irq_set_chained_handler(parent_irq,
armada_370_xp_mpic_handle_cascade_irq);

View File

@ -42,7 +42,7 @@ __exception_irq_entry orion_handle_irq(struct pt_regs *regs)
u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) &
gc->mask_cache;
while (stat) {
u32 hwirq = ffs(stat) - 1;
u32 hwirq = __fls(stat);
u32 irq = irq_find_mapping(orion_irq_domain,
gc->irq_base + hwirq);
handle_IRQ(irq, regs);
@ -117,7 +117,7 @@ static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc)
gc->mask_cache;
while (stat) {
u32 hwirq = ffs(stat) - 1;
u32 hwirq = __fls(stat);
generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq));
stat &= ~(1 << hwirq);

View File

@ -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,
size_t mbus_size, phys_addr_t sdram_phys_base,
size_t sdram_size);
int mvebu_mbus_dt_init(void);
int mvebu_mbus_dt_init(bool is_coherent);
#endif /* __LINUX_MBUS_H */