* [PATCHv2 1/5] clk: mvebu: support for 98DX3236 SoC
From: Chris Packham @ 2017-01-05 3:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105033641.6212-1-chris.packham@alliedtelesis.co.nz>
The 98DX3236, 98DX3336, 98DX4521 and variants have a different TCLK from
the Armada XP (200MHz vs 250MHz). The CPU core clock is fixed at 800MHz.
The clock gating options are a subset of those on the Armada XP.
The core clock divider is different to the Armada XP also.
Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
Changes in v2:
- Update devicetree binding documentation for new compatible string
.../devicetree/bindings/clock/mvebu-cpu-clock.txt | 1 +
drivers/clk/mvebu/Makefile | 2 +-
drivers/clk/mvebu/armada-xp.c | 42 +++++
drivers/clk/mvebu/clk-cpu.c | 33 +++-
drivers/clk/mvebu/mv98dx3236-corediv.c | 207 +++++++++++++++++++++
5 files changed, 281 insertions(+), 4 deletions(-)
create mode 100644 drivers/clk/mvebu/mv98dx3236-corediv.c
diff --git a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt
index 99c214660bdc..7f28506eaee7 100644
--- a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt
+++ b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt
@@ -3,6 +3,7 @@ Device Tree Clock bindings for cpu clock of Marvell EBU platforms
Required properties:
- compatible : shall be one of the following:
"marvell,armada-xp-cpu-clock" - cpu clocks for Armada XP
+ "marvell,mv98dx3236-cpu-clock" - cpu clocks for 98DX3236 SoC
- reg : Address and length of the clock complex register set, followed
by address and length of the PMU DFS registers
- #clock-cells : should be set to 1.
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile
index d9ae97fb43c4..6a3681e3d6db 100644
--- a/drivers/clk/mvebu/Makefile
+++ b/drivers/clk/mvebu/Makefile
@@ -9,7 +9,7 @@ obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o
obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-xtal.o
obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-tbg.o
obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-periph.o
-obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o
+obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o mv98dx3236-corediv.o
obj-$(CONFIG_ARMADA_AP806_SYSCON) += ap806-system-controller.o
obj-$(CONFIG_ARMADA_CP110_SYSCON) += cp110-system-controller.o
obj-$(CONFIG_DOVE_CLK) += dove.o dove-divider.o
diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c
index b3094315a3c0..0413bf8284e0 100644
--- a/drivers/clk/mvebu/armada-xp.c
+++ b/drivers/clk/mvebu/armada-xp.c
@@ -52,6 +52,12 @@ static u32 __init axp_get_tclk_freq(void __iomem *sar)
return 250000000;
}
+/* MV98DX3236 TCLK frequency is fixed to 200MHz */
+static u32 __init mv98dx3236_get_tclk_freq(void __iomem *sar)
+{
+ return 200000000;
+}
+
static const u32 axp_cpu_freqs[] __initconst = {
1000000000,
1066000000,
@@ -89,6 +95,12 @@ static u32 __init axp_get_cpu_freq(void __iomem *sar)
return cpu_freq;
}
+/* MV98DX3236 CLK frequency is fixed to 800MHz */
+static u32 __init mv98dx3236_get_cpu_freq(void __iomem *sar)
+{
+ return 800000000;
+}
+
static const int axp_nbclk_ratios[32][2] __initconst = {
{0, 1}, {1, 2}, {2, 2}, {2, 2},
{1, 2}, {1, 2}, {1, 1}, {2, 3},
@@ -158,6 +170,14 @@ static const struct coreclk_soc_desc axp_coreclks = {
.num_ratios = ARRAY_SIZE(axp_coreclk_ratios),
};
+static const struct coreclk_soc_desc mv98dx3236_coreclks = {
+ .get_tclk_freq = mv98dx3236_get_tclk_freq,
+ .get_cpu_freq = mv98dx3236_get_cpu_freq,
+ .get_clk_ratio = NULL,
+ .ratios = NULL,
+ .num_ratios = 0,
+};
+
/*
* Clock Gating Control
*/
@@ -195,6 +215,15 @@ static const struct clk_gating_soc_desc axp_gating_desc[] __initconst = {
{ }
};
+static const struct clk_gating_soc_desc mv98dx3236_gating_desc[] __initconst = {
+ { "ge1", NULL, 3, 0 },
+ { "ge0", NULL, 4, 0 },
+ { "pex00", NULL, 5, 0 },
+ { "sdio", NULL, 17, 0 },
+ { "xor0", NULL, 22, 0 },
+ { }
+};
+
static void __init axp_clk_init(struct device_node *np)
{
struct device_node *cgnp =
@@ -206,3 +235,16 @@ static void __init axp_clk_init(struct device_node *np)
mvebu_clk_gating_setup(cgnp, axp_gating_desc);
}
CLK_OF_DECLARE(axp_clk, "marvell,armada-xp-core-clock", axp_clk_init);
+
+static void __init mv98dx3236_clk_init(struct device_node *np)
+{
+ struct device_node *cgnp =
+ of_find_compatible_node(NULL, NULL, "marvell,armada-xp-gating-clock");
+
+ mvebu_coreclk_setup(np, &mv98dx3236_coreclks);
+
+ if (cgnp)
+ mvebu_clk_gating_setup(cgnp, mv98dx3236_gating_desc);
+}
+CLK_OF_DECLARE(mv98dx3236_clk, "marvell,mv98dx3236-core-clock",
+ mv98dx3236_clk_init);
diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c
index 5837eb8a212f..29f295e7a36b 100644
--- a/drivers/clk/mvebu/clk-cpu.c
+++ b/drivers/clk/mvebu/clk-cpu.c
@@ -165,7 +165,9 @@ static const struct clk_ops cpu_ops = {
.set_rate = clk_cpu_set_rate,
};
-static void __init of_cpu_clk_setup(struct device_node *node)
+/* Add parameter to allow this to support different clock operations. */
+static void __init _of_cpu_clk_setup(struct device_node *node,
+ const struct clk_ops *cpu_clk_ops)
{
struct cpu_clk *cpuclk;
void __iomem *clock_complex_base = of_iomap(node, 0);
@@ -218,7 +220,7 @@ static void __init of_cpu_clk_setup(struct device_node *node)
cpuclk[cpu].hw.init = &init;
init.name = cpuclk[cpu].clk_name;
- init.ops = &cpu_ops;
+ init.ops = cpu_clk_ops;
init.flags = 0;
init.parent_names = &cpuclk[cpu].parent_name;
init.num_parents = 1;
@@ -243,5 +245,30 @@ static void __init of_cpu_clk_setup(struct device_node *node)
iounmap(clock_complex_base);
}
+/* Use this function to call the generic setup with the correct
+ * clock operation
+ */
+static void __init of_cpu_clk_setup(struct device_node *node)
+{
+ _of_cpu_clk_setup(node, &cpu_ops);
+}
+
CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock",
- of_cpu_clk_setup);
+ of_cpu_clk_setup);
+
+/* Define the clock and operations for the mv98dx3236 - it cannot perform
+ * any operations.
+ */
+static const struct clk_ops mv98dx3236_cpu_ops = {
+ .recalc_rate = NULL,
+ .round_rate = NULL,
+ .set_rate = NULL,
+};
+
+static void __init of_mv98dx3236_cpu_clk_setup(struct device_node *node)
+{
+ _of_cpu_clk_setup(node, &mv98dx3236_cpu_ops);
+}
+
+CLK_OF_DECLARE(mv98dx3236_cpu_clock, "marvell,mv98dx3236-cpu-clock",
+ of_mv98dx3236_cpu_clk_setup);
diff --git a/drivers/clk/mvebu/mv98dx3236-corediv.c b/drivers/clk/mvebu/mv98dx3236-corediv.c
new file mode 100644
index 000000000000..3060764a8e5d
--- /dev/null
+++ b/drivers/clk/mvebu/mv98dx3236-corediv.c
@@ -0,0 +1,207 @@
+/*
+ * MV98DX3236 Core divider clock
+ *
+ * Copyright (C) 2015 Allied Telesis Labs
+ *
+ * Based on armada-xp-corediv.c
+ * Copyright (C) 2015 Marvell
+ *
+ * John Thompson <john.thompson@alliedtelesis.co.nz>
+ *
+ * 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/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "common.h"
+
+#define CORE_CLK_DIV_RATIO_MASK 0xff
+
+#define CLK_DIV_RATIO_NAND_MASK 0x0f
+#define CLK_DIV_RATIO_NAND_OFFSET 6
+#define CLK_DIV_RATIO_NAND_FORCE_RELOAD_BIT 26
+
+#define RATIO_RELOAD_BIT BIT(10)
+#define RATIO_REG_OFFSET 0x08
+
+/*
+ * This structure represents one core divider clock for the clock
+ * framework, and is dynamically allocated for each core divider clock
+ * existing in the current SoC.
+ */
+struct clk_corediv {
+ struct clk_hw hw;
+ void __iomem *reg;
+ spinlock_t lock;
+};
+
+static struct clk_onecell_data clk_data;
+
+
+#define to_corediv_clk(p) container_of(p, struct clk_corediv, hw)
+
+static int mv98dx3236_corediv_is_enabled(struct clk_hw *hwclk)
+{
+ /* Core divider is always active */
+ return 1;
+}
+
+static int mv98dx3236_corediv_enable(struct clk_hw *hwclk)
+{
+ /* always succeeds */
+ return 0;
+}
+
+static void mv98dx3236_corediv_disable(struct clk_hw *hwclk)
+{
+ /* can't be disabled so is left alone */
+}
+
+static unsigned long mv98dx3236_corediv_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct clk_corediv *corediv = to_corediv_clk(hwclk);
+ u32 reg, div;
+
+ reg = readl(corediv->reg + RATIO_REG_OFFSET);
+ div = (reg >> CLK_DIV_RATIO_NAND_OFFSET) & CLK_DIV_RATIO_NAND_MASK;
+ return parent_rate / div;
+}
+
+static long mv98dx3236_corediv_round_rate(struct clk_hw *hwclk,
+ unsigned long rate, unsigned long *parent_rate)
+{
+ /* Valid ratio are 1:4, 1:5, 1:6 and 1:8 */
+ u32 div;
+
+ div = *parent_rate / rate;
+ if (div < 4)
+ div = 4;
+ else if (div > 6)
+ div = 8;
+
+ return *parent_rate / div;
+}
+
+static int mv98dx3236_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_corediv *corediv = to_corediv_clk(hwclk);
+ unsigned long flags = 0;
+ u32 reg, div;
+
+ div = parent_rate / rate;
+
+ spin_lock_irqsave(&corediv->lock, flags);
+
+ /* Write new divider to the divider ratio register */
+ reg = readl(corediv->reg + RATIO_REG_OFFSET);
+ reg &= ~(CLK_DIV_RATIO_NAND_MASK << CLK_DIV_RATIO_NAND_OFFSET);
+ reg |= (div & CLK_DIV_RATIO_NAND_MASK) << CLK_DIV_RATIO_NAND_OFFSET;
+ writel(reg, corediv->reg + RATIO_REG_OFFSET);
+
+ /* Set reload-force for this clock */
+ reg = readl(corediv->reg) | BIT(CLK_DIV_RATIO_NAND_FORCE_RELOAD_BIT);
+ writel(reg, corediv->reg);
+
+ /* Now trigger the clock update */
+ reg = readl(corediv->reg + RATIO_REG_OFFSET) | RATIO_RELOAD_BIT;
+ writel(reg, corediv->reg + RATIO_REG_OFFSET);
+
+ /*
+ * Wait for clocks to settle down, and then clear all the
+ * ratios request and the reload request.
+ */
+ udelay(1000);
+ reg &= ~(CORE_CLK_DIV_RATIO_MASK | RATIO_RELOAD_BIT);
+ writel(reg, corediv->reg + RATIO_REG_OFFSET);
+ udelay(1000);
+
+ spin_unlock_irqrestore(&corediv->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops ops = {
+ .enable = mv98dx3236_corediv_enable,
+ .disable = mv98dx3236_corediv_disable,
+ .is_enabled = mv98dx3236_corediv_is_enabled,
+ .recalc_rate = mv98dx3236_corediv_recalc_rate,
+ .round_rate = mv98dx3236_corediv_round_rate,
+ .set_rate = mv98dx3236_corediv_set_rate,
+};
+
+static void __init mv98dx3236_corediv_clk_init(struct device_node *node)
+{
+ struct clk_init_data init;
+ struct clk_corediv *corediv;
+ struct clk **clks;
+ void __iomem *base;
+ const __be32 *off;
+ const char *parent_name;
+ const char *clk_name;
+ int len;
+ struct device_node *dfx_node;
+
+ dfx_node = of_parse_phandle(node, "base", 0);
+ if (WARN_ON(!dfx_node))
+ return;
+
+ off = of_get_property(node, "reg", &len);
+ if (WARN_ON(!off))
+ return;
+
+ base = of_iomap(dfx_node, 0);
+ if (WARN_ON(!base))
+ return;
+
+ of_node_put(dfx_node);
+
+ parent_name = of_clk_get_parent_name(node, 0);
+
+ clk_data.clk_num = 1;
+
+ /* clks holds the clock array */
+ clks = kcalloc(clk_data.clk_num, sizeof(struct clk *),
+ GFP_KERNEL);
+ if (WARN_ON(!clks))
+ goto err_unmap;
+ /* corediv holds the clock specific array */
+ corediv = kcalloc(clk_data.clk_num, sizeof(struct clk_corediv),
+ GFP_KERNEL);
+ if (WARN_ON(!corediv))
+ goto err_free_clks;
+
+ spin_lock_init(&corediv->lock);
+
+ of_property_read_string_index(node, "clock-output-names",
+ 0, &clk_name);
+
+ init.num_parents = 1;
+ init.parent_names = &parent_name;
+ init.name = clk_name;
+ init.ops = &ops;
+ init.flags = 0;
+
+ corediv[0].reg = (void *)((int)base + be32_to_cpu(*off));
+ corediv[0].hw.init = &init;
+
+ clks[0] = clk_register(NULL, &corediv[0].hw);
+ WARN_ON(IS_ERR(clks[0]));
+
+ clk_data.clks = clks;
+ of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data);
+ return;
+
+err_free_clks:
+ kfree(clks);
+err_unmap:
+ iounmap(base);
+}
+
+CLK_OF_DECLARE(mv98dx3236_corediv_clk, "marvell,mv98dx3236-corediv-clock",
+ mv98dx3236_corediv_clk_init);
--
2.11.0.24.ge6920cf
^ permalink raw reply related
* [PATCHv2 2/5] arm: mvebu: support for SMP on 98DX3336 SoC
From: Chris Packham @ 2017-01-05 3:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105033641.6212-1-chris.packham@alliedtelesis.co.nz>
Compared to the armada-xp the 98DX3336 uses different registers to set
the boot address for the secondary CPU so a new enable-method is needed.
This will only work if the machine definition doesn't define an overall
smp_ops because there is not currently a way of overriding this from the
device tree if it is set in the machine definition.
Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
Changes in v2:
- Document new enable-method value
- Correct some references from 98DX4521 to 98DX3236
Documentation/devicetree/bindings/arm/cpus.txt | 1 +
.../bindings/arm/marvell/98dx3236-resume-ctrl.txt | 18 ++++++
arch/arm/mach-mvebu/Makefile | 1 +
arch/arm/mach-mvebu/common.h | 1 +
arch/arm/mach-mvebu/platsmp.c | 43 ++++++++++++++
arch/arm/mach-mvebu/pmsu-98dx3236.c | 69 ++++++++++++++++++++++
6 files changed, 133 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/marvell/98dx3236-resume-ctrl.txt
create mode 100644 arch/arm/mach-mvebu/pmsu-98dx3236.c
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
index a1bcfeed5f24..3c2fd72d0bf9 100644
--- a/Documentation/devicetree/bindings/arm/cpus.txt
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -202,6 +202,7 @@ nodes to be present and contain the properties described below.
"marvell,armada-380-smp"
"marvell,armada-390-smp"
"marvell,armada-xp-smp"
+ "marvell,98dx3236-smp"
"mediatek,mt6589-smp"
"mediatek,mt81xx-tz-smp"
"qcom,gcc-msm8660"
diff --git a/Documentation/devicetree/bindings/arm/marvell/98dx3236-resume-ctrl.txt b/Documentation/devicetree/bindings/arm/marvell/98dx3236-resume-ctrl.txt
new file mode 100644
index 000000000000..8082ba872edd
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/marvell/98dx3236-resume-ctrl.txt
@@ -0,0 +1,18 @@
+Resume Control
+--------------
+Available on Marvell SOCs: 98DX3336 and 98DX4251
+
+Required properties:
+
+- compatible: must be "marvell,98dx3336-resume-ctrl"
+
+- reg: Should contain resume control registers location and length
+
+Example:
+
+resume at 20980 {
+ compatible = "marvell,98dx3336-resume-ctrl";
+ reg = <0x20980 0x10>;
+};
+
+
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile
index 6c6497e80a7b..2a2dd8324fb8 100644
--- a/arch/arm/mach-mvebu/Makefile
+++ b/arch/arm/mach-mvebu/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_MACH_MVEBU_ANY) += system-controller.o mvebu-soc-id.o
ifeq ($(CONFIG_MACH_MVEBU_V7),y)
obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o
+obj-y += pmsu-98dx3236.o
obj-$(CONFIG_PM) += pm.o pm-board.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o
diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h
index 6b775492cfad..099dabf23461 100644
--- a/arch/arm/mach-mvebu/common.h
+++ b/arch/arm/mach-mvebu/common.h
@@ -27,4 +27,5 @@ void __iomem *mvebu_get_scu_base(void);
int mvebu_pm_suspend_init(void (*board_pm_enter)(void __iomem *sdram_reg,
u32 srcmd));
+void mv98dx3236_resume_set_cpu_boot_addr(int hw_cpu, void *boot_addr);
#endif
diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c
index 46c742d3bd41..3c9ab9a008ad 100644
--- a/arch/arm/mach-mvebu/platsmp.c
+++ b/arch/arm/mach-mvebu/platsmp.c
@@ -182,5 +182,48 @@ const struct smp_operations armada_xp_smp_ops __initconst = {
#endif
};
+static int mv98dx3236_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ int ret, hw_cpu;
+
+ pr_info("Booting CPU %d\n", cpu);
+
+ hw_cpu = cpu_logical_map(cpu);
+ set_secondary_cpu_clock(hw_cpu);
+ mv98dx3236_resume_set_cpu_boot_addr(hw_cpu,
+ armada_xp_secondary_startup);
+
+ /*
+ * This is needed to wake up CPUs in the offline state after
+ * using CPU hotplug.
+ */
+ arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+
+ /*
+ * This is needed to take secondary CPUs out of reset on the
+ * initial boot.
+ */
+ ret = mvebu_cpu_reset_deassert(hw_cpu);
+ if (ret) {
+ pr_warn("unable to boot CPU: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+struct smp_operations mv98dx3236_smp_ops __initdata = {
+ .smp_init_cpus = armada_xp_smp_init_cpus,
+ .smp_prepare_cpus = armada_xp_smp_prepare_cpus,
+ .smp_boot_secondary = mv98dx3236_boot_secondary,
+ .smp_secondary_init = armada_xp_secondary_init,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = armada_xp_cpu_die,
+ .cpu_kill = armada_xp_cpu_kill,
+#endif
+};
+
CPU_METHOD_OF_DECLARE(armada_xp_smp, "marvell,armada-xp-smp",
&armada_xp_smp_ops);
+CPU_METHOD_OF_DECLARE(mv98dx3236_smp, "marvell,98dx3236-smp",
+ &mv98dx3236_smp_ops);
diff --git a/arch/arm/mach-mvebu/pmsu-98dx3236.c b/arch/arm/mach-mvebu/pmsu-98dx3236.c
new file mode 100644
index 000000000000..87ca42ef40c7
--- /dev/null
+++ b/arch/arm/mach-mvebu/pmsu-98dx3236.c
@@ -0,0 +1,69 @@
+/**
+ * CPU resume support for 98DX3236 internal CPU (a.k.a. MSYS).
+ */
+
+#define pr_fmt(fmt) "mv98dx3236-resume: " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include "common.h"
+
+static void __iomem *mv98dx3236_resume_base;
+#define MV98DX3236_CPU_RESUME_CTRL_OFFSET 0x08
+#define MV98DX3236_CPU_RESUME_ADDR_OFFSET 0x04
+
+static const struct of_device_id of_mv98dx3236_resume_table[] = {
+ {.compatible = "marvell,98dx3336-resume-ctrl",},
+ { /* end of list */ },
+};
+
+void mv98dx3236_resume_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
+{
+ WARN_ON(hw_cpu != 1);
+
+ writel(0, mv98dx3236_resume_base + MV98DX3236_CPU_RESUME_CTRL_OFFSET);
+ writel(virt_to_phys(boot_addr), mv98dx3236_resume_base +
+ MV98DX3236_CPU_RESUME_ADDR_OFFSET);
+}
+
+static int __init mv98dx3236_resume_init(void)
+{
+ struct device_node *np;
+ struct resource res;
+ int ret = 0;
+
+ np = of_find_matching_node(NULL, of_mv98dx3236_resume_table);
+ if (!np)
+ return 0;
+
+ pr_info("Initializing 98DX3236 Resume\n");
+
+ if (of_address_to_resource(np, 0, &res)) {
+ pr_err("unable to get resource\n");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (!request_mem_region(res.start, resource_size(&res),
+ np->full_name)) {
+ pr_err("unable to request region\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ mv98dx3236_resume_base = ioremap(res.start, resource_size(&res));
+ if (!mv98dx3236_resume_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;
+}
+
+early_initcall(mv98dx3236_resume_init);
--
2.11.0.24.ge6920cf
^ permalink raw reply related
* [PATCHv2 3/5] pinctrl: mvebu: pinctrl driver for 98DX3236 SoC
From: Chris Packham @ 2017-01-05 3:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105033641.6212-1-chris.packham@alliedtelesis.co.nz>
From: Kalyan Kinthada <kalyan.kinthada@alliedtelesis.co.nz>
This pinctrl driver supports the 98DX3236, 98DX3336 and 98DX4251 SoCs
from Marvell.
Signed-off-by: Kalyan Kinthada <kalyan.kinthada@alliedtelesis.co.nz>
Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
Changes in v2:
- include sdio support for the 98DX4251
.../pinctrl/marvell,armada-98dx3236-pinctrl.txt | 46 ++++++
drivers/pinctrl/mvebu/pinctrl-armada-xp.c | 155 +++++++++++++++++++++
2 files changed, 201 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/marvell,armada-98dx3236-pinctrl.txt
diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,armada-98dx3236-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/marvell,armada-98dx3236-pinctrl.txt
new file mode 100644
index 000000000000..d4e6ecdfc853
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/marvell,armada-98dx3236-pinctrl.txt
@@ -0,0 +1,46 @@
+* Marvell 98dx3236 pinctrl driver for mpp
+
+Please refer to marvell,mvebu-pinctrl.txt in this directory for common binding
+part and usage
+
+Required properties:
+- compatible: "marvell,98dx3236-pinctrl" or "marvell,98dx4251-pinctrl"
+- reg: register specifier of MPP registers
+
+This driver supports all 98dx3236, 98dx3336 and 98dx4251 variants
+
+name pins functions
+================================================================================
+mpp0 0 gpio, spi0(mosi), dev(ad8)
+mpp1 1 gpio, spi0(miso), dev(ad9)
+mpp2 2 gpio, spi0(sck), dev(ad10)
+mpp3 3 gpio, spi0(cs0), dev(ad11)
+mpp4 4 gpio, spi0(cs1), smi(mdc), dev(cs0)
+mpp5 5 gpio, pex(rsto), sd0(cmd), dev(bootcs)
+mpp6 6 gpio, sd0(clk), dev(a2)
+mpp7 7 gpio, sd0(d0), dev(ale0)
+mpp8 8 gpio, sd0(d1), dev(ale1)
+mpp9 9 gpio, sd0(d2), dev(ready0)
+mpp10 10 gpio, sd0(d3), dev(ad12)
+mpp11 11 gpio, uart1(rxd), uart0(cts), dev(ad13)
+mpp12 12 gpio, uart1(txd), uart0(rts), dev(ad14)
+mpp13 13 gpio, intr(out), dev(ad15)
+mpp14 14 gpio, i2c0(sck)
+mpp15 15 gpio, i2c0(sda)
+mpp16 16 gpio, dev(oe)
+mpp17 17 gpio, dev(clk)
+mpp18 18 gpio, uart1(txd)
+mpp19 19 gpio, uart1(rxd), dev(rb)
+mpp20 20 gpio, dev(we)
+mpp21 21 gpio, dev(ad0)
+mpp22 22 gpio, dev(ad1)
+mpp23 23 gpio, dev(ad2)
+mpp24 24 gpio, dev(ad3)
+mpp25 25 gpio, dev(ad4)
+mpp26 26 gpio, dev(ad5)
+mpp27 27 gpio, dev(ad6)
+mpp28 28 gpio, dev(ad7)
+mpp29 29 gpio, dev(a0)
+mpp30 30 gpio, dev(a1)
+mpp31 31 gpio, slv_smi(mdc), smi(mdc), dev(we1)
+mpp32 32 gpio, slv_smi(mdio), smi(mdio), dev(cs1)
diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-xp.c b/drivers/pinctrl/mvebu/pinctrl-armada-xp.c
index e4ea71a9d985..554eeae8cd21 100644
--- a/drivers/pinctrl/mvebu/pinctrl-armada-xp.c
+++ b/drivers/pinctrl/mvebu/pinctrl-armada-xp.c
@@ -49,6 +49,10 @@ enum armada_xp_variant {
V_MV78460 = BIT(2),
V_MV78230_PLUS = (V_MV78230 | V_MV78260 | V_MV78460),
V_MV78260_PLUS = (V_MV78260 | V_MV78460),
+ V_98DX3236 = BIT(3),
+ V_98DX3336 = BIT(4),
+ V_98DX4251 = BIT(5),
+ V_98DX3236_PLUS = (V_98DX3236 | V_98DX3336 | V_98DX4251),
};
static struct mvebu_mpp_mode armada_xp_mpp_modes[] = {
@@ -360,6 +364,130 @@ static struct mvebu_mpp_mode armada_xp_mpp_modes[] = {
MPP_VAR_FUNCTION(0x1, "dev", "ad31", V_MV78260_PLUS)),
};
+static struct mvebu_mpp_mode mv98dx3236_mpp_modes[] = {
+ MPP_MODE(0,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "spi0", "mosi", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "ad8", V_98DX3236_PLUS)),
+ MPP_MODE(1,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "spi0", "miso", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "ad9", V_98DX3236_PLUS)),
+ MPP_MODE(2,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "spi0", "csk", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "ad10", V_98DX3236_PLUS)),
+ MPP_MODE(3,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "spi0", "cs0", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "ad11", V_98DX3236_PLUS)),
+ MPP_MODE(4,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "spi0", "cs1", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x3, "smi", "mdc", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "cs0", V_98DX3236_PLUS)),
+ MPP_MODE(5,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "pex", "rsto", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "sd0", "cmd", V_98DX4251),
+ MPP_VAR_FUNCTION(0x4, "dev", "bootcs0", V_98DX3236_PLUS)),
+ MPP_MODE(6,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "sd0", "clk", V_98DX4251),
+ MPP_VAR_FUNCTION(0x4, "dev", "a2", V_98DX3236_PLUS)),
+ MPP_MODE(7,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "sd0", "d0", V_98DX4251),
+ MPP_VAR_FUNCTION(0x4, "dev", "ale0", V_98DX3236_PLUS)),
+ MPP_MODE(8,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "sd0", "d1", V_98DX4251),
+ MPP_VAR_FUNCTION(0x4, "dev", "ale1", V_98DX3236_PLUS)),
+ MPP_MODE(9,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "sd0", "d2", V_98DX4251),
+ MPP_VAR_FUNCTION(0x4, "dev", "ready0", V_98DX3236_PLUS)),
+ MPP_MODE(10,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "sd0", "d3", V_98DX4251),
+ MPP_VAR_FUNCTION(0x4, "dev", "ad12", V_98DX3236_PLUS)),
+ MPP_MODE(11,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "uart1", "rxd", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x3, "uart0", "cts", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "ad13", V_98DX3236_PLUS)),
+ MPP_MODE(12,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x2, "uart1", "txd", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x3, "uart0", "rts", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "ad14", V_98DX3236_PLUS)),
+ MPP_MODE(13,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "intr", "out", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "ad15", V_98DX3236_PLUS)),
+ MPP_MODE(14,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "i2c0", "sck", V_98DX3236_PLUS)),
+ MPP_MODE(15,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "i2c0", "sda", V_98DX3236_PLUS)),
+ MPP_MODE(16,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "oe", V_98DX3236_PLUS)),
+ MPP_MODE(17,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "clkout", V_98DX3236_PLUS)),
+ MPP_MODE(18,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x3, "uart1", "txd", V_98DX3236_PLUS)),
+ MPP_MODE(19,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x3, "uart1", "rxd", V_98DX3236_PLUS)),
+ MPP_MODE(20,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "we0", V_98DX3236_PLUS)),
+ MPP_MODE(21,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "ad0", V_98DX3236_PLUS)),
+ MPP_MODE(22,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "ad1", V_98DX3236_PLUS)),
+ MPP_MODE(23,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "ad2", V_98DX3236_PLUS)),
+ MPP_MODE(24,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "ad3", V_98DX3236_PLUS)),
+ MPP_MODE(25,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "ad4", V_98DX3236_PLUS)),
+ MPP_MODE(26,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "ad5", V_98DX3236_PLUS)),
+ MPP_MODE(27,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "ad6", V_98DX3236_PLUS)),
+ MPP_MODE(28,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "ad7", V_98DX3236_PLUS)),
+ MPP_MODE(29,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "a0", V_98DX3236_PLUS)),
+ MPP_MODE(30,
+ MPP_VAR_FUNCTION(0x0, "gpo", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "dev", "a1", V_98DX3236_PLUS)),
+ MPP_MODE(31,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "slv_smi", "mdc", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x3, "smi", "mdc", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "we1", V_98DX3236_PLUS)),
+ MPP_MODE(32,
+ MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x1, "slv_smi", "mdio", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x3, "smi", "mdio", V_98DX3236_PLUS),
+ MPP_VAR_FUNCTION(0x4, "dev", "cs1", V_98DX3236_PLUS)),
+};
+
static struct mvebu_pinctrl_soc_info armada_xp_pinctrl_info;
static const struct of_device_id armada_xp_pinctrl_of_match[] = {
@@ -375,6 +503,14 @@ static const struct of_device_id armada_xp_pinctrl_of_match[] = {
.compatible = "marvell,mv78460-pinctrl",
.data = (void *) V_MV78460,
},
+ {
+ .compatible = "marvell,98dx3236-pinctrl",
+ .data = (void *) V_98DX3236,
+ },
+ {
+ .compatible = "marvell,98dx4251-pinctrl",
+ .data = (void *) V_98DX4251,
+ },
{ },
};
@@ -407,6 +543,14 @@ static struct pinctrl_gpio_range mv78460_mpp_gpio_ranges[] = {
MPP_GPIO_RANGE(2, 64, 64, 3),
};
+static struct mvebu_mpp_ctrl mv98dx3236_mpp_controls[] = {
+ MPP_FUNC_CTRL(0, 32, NULL, armada_xp_mpp_ctrl),
+};
+
+static struct pinctrl_gpio_range mv98dx3236_mpp_gpio_ranges[] = {
+ MPP_GPIO_RANGE(0, 0, 0, 32),
+};
+
static int armada_xp_pinctrl_suspend(struct platform_device *pdev,
pm_message_t state)
{
@@ -488,6 +632,17 @@ static int armada_xp_pinctrl_probe(struct platform_device *pdev)
soc->gpioranges = mv78460_mpp_gpio_ranges;
soc->ngpioranges = ARRAY_SIZE(mv78460_mpp_gpio_ranges);
break;
+ case V_98DX3236:
+ case V_98DX3336:
+ case V_98DX4251:
+ /* fall-through */
+ soc->controls = mv98dx3236_mpp_controls;
+ soc->ncontrols = ARRAY_SIZE(mv98dx3236_mpp_controls);
+ soc->modes = mv98dx3236_mpp_modes;
+ soc->nmodes = mv98dx3236_mpp_controls[0].npins;
+ soc->gpioranges = mv98dx3236_mpp_gpio_ranges;
+ soc->ngpioranges = ARRAY_SIZE(mv98dx3236_mpp_gpio_ranges);
+ break;
}
nregs = DIV_ROUND_UP(soc->nmodes, MVEBU_MPPS_PER_REG);
--
2.11.0.24.ge6920cf
^ permalink raw reply related
* [PATCHv2 4/5] arm: mvebu: Add device tree for 98DX3236 SoCs
From: Chris Packham @ 2017-01-05 3:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105033641.6212-1-chris.packham@alliedtelesis.co.nz>
The Marvell 98DX3236, 98DX3336, 98DX4521 and variants are switch ASICs
with integrated CPUs. They are similar to the Armada XP SoCs but have
different I/O interfaces.
Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
Changes in v2:
- Update devicetree binding documentation to reflect that 98DX3336 and
984251 are supersets of 98DX3236.
- disable crypto block
- disable sdio for 98DX3236, enable for 98DX4251
.../devicetree/bindings/arm/marvell/98dx3236.txt | 23 ++
arch/arm/boot/dts/armada-xp-98dx3236.dtsi | 247 +++++++++++++++++++++
arch/arm/boot/dts/armada-xp-98dx3336.dtsi | 78 +++++++
arch/arm/boot/dts/armada-xp-98dx4251.dtsi | 92 ++++++++
4 files changed, 440 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/marvell/98dx3236.txt
create mode 100644 arch/arm/boot/dts/armada-xp-98dx3236.dtsi
create mode 100644 arch/arm/boot/dts/armada-xp-98dx3336.dtsi
create mode 100644 arch/arm/boot/dts/armada-xp-98dx4251.dtsi
diff --git a/Documentation/devicetree/bindings/arm/marvell/98dx3236.txt b/Documentation/devicetree/bindings/arm/marvell/98dx3236.txt
new file mode 100644
index 000000000000..64e8c73fc5ab
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/marvell/98dx3236.txt
@@ -0,0 +1,23 @@
+Marvell 98DX3236, 98DX3336 and 98DX4251 Platforms Device Tree Bindings
+----------------------------------------------------------------------
+
+Boards with a SoC of the Marvell 98DX3236, 98DX3336 and 98DX4251 families
+shall have the following property:
+
+Required root node property:
+
+compatible: must contain "marvell,armadaxp-98dx3236"
+
+In addition, boards using the Marvell 98DX3336 SoC shall have the
+following property:
+
+Required root node property:
+
+compatible: must contain "marvell,armadaxp-98dx3336"
+
+In addition, boards using the Marvell 98DX4251 SoC shall have the
+following property:
+
+Required root node property:
+
+compatible: must contain "marvell,armadaxp-98dx4251"
diff --git a/arch/arm/boot/dts/armada-xp-98dx3236.dtsi b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi
new file mode 100644
index 000000000000..61bd3acc5cfe
--- /dev/null
+++ b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi
@@ -0,0 +1,247 @@
+/*
+ * Device Tree Include file for Marvell 98dx3236 family SoC
+ *
+ * Copyright (C) 2016 Allied Telesis Labs
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contains definitions specific to the 98dx3236 SoC that are not
+ * common to all Armada XP SoCs.
+ */
+
+#include "armada-xp.dtsi"
+
+/ {
+ model = "Marvell 98DX3236 SoC";
+ compatible = "marvell,armadaxp-98dx3236", "marvell,armadaxp", "marvell,armada-370-xp";
+
+ aliases {
+ gpio0 = &gpio0;
+ gpio1 = &gpio1;
+ gpio2 = &gpio2;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ enable-method = "marvell,98dx3236-smp";
+
+ cpu at 0 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <0>;
+ clocks = <&cpuclk 0>;
+ clock-latency = <1000000>;
+ };
+ };
+
+ soc {
+ ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
+ MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
+ MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x1000000
+ MBUS_ID(0x03, 0x00) 0 0 0xa8000000 0x4000000
+ MBUS_ID(0x08, 0x00) 0 0 0xac000000 0x100000>;
+
+ /*
+ * 98DX3236 has 1 x1 PCIe unit Gen2.0: One unit can be
+ */
+ pcie-controller {
+ compatible = "marvell,armada-xp-pcie";
+ status = "disabled";
+ device_type = "pci";
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ msi-parent = <&mpic>;
+ bus-range = <0x00 0xff>;
+
+ ranges =
+ <0x82000000 0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x00002000 /* Port 0.0 registers */
+ 0x82000000 0x1 0 MBUS_ID(0x04, 0xe8) 0 1 0 /* Port 0.0 MEM */
+ 0x81000000 0x1 0 MBUS_ID(0x04, 0xe0) 0 1 0 /* Port 0.0 IO */
+ 0x82000000 0x2 0 MBUS_ID(0x04, 0xd8) 0 1 0 /* Port 0.1 MEM */>;
+
+ pcie at 1,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &mpic 58>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
+ };
+ };
+
+ internal-regs {
+ coreclk: mvebu-sar at 18230 {
+ compatible = "marvell,mv98dx3236-core-clock";
+ };
+
+ cpuclk: clock-complex at 18700 {
+ compatible = "marvell,mv98dx3236-cpu-clock";
+ };
+
+ corediv-clock at 18740 {
+ compatible = "marvell,mv98dx3236-corediv-clock";
+ reg = <0xf8268 0xc>;
+ base = <&dfx>;
+ #clock-cells = <1>;
+ clocks = <&mainpll>;
+ clock-output-names = "nand";
+ };
+
+ xor at 60900 {
+ status = "disabled";
+ };
+
+ crypto at 90000 {
+ status = "disabled";
+ };
+
+ xor at f0900 {
+ status = "disabled";
+ };
+
+ xor at f0800 {
+ compatible = "marvell,orion-xor";
+ reg = <0xf0800 0x100
+ 0xf0a00 0x100>;
+ clocks = <&gateclk 22>;
+ status = "okay";
+
+ xor10 {
+ interrupts = <51>;
+ dmacap,memcpy;
+ dmacap,xor;
+ };
+ xor11 {
+ interrupts = <52>;
+ dmacap,memcpy;
+ dmacap,xor;
+ dmacap,memset;
+ };
+ };
+
+ gpio0: gpio at 18100 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18100 0x40>;
+ ngpios = <32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <82>, <83>, <84>, <85>;
+ };
+
+ /* does not exist */
+ gpio1: gpio at 18140 {
+ compatible = "marvell,orion-gpio";
+ reg = <0x18140 0x40>;
+ status = "disabled";
+ };
+
+ gpio2: gpio at 18180 { /* rework some properties */
+ compatible = "marvell,orion-gpio";
+ reg = <0x18180 0x40>;
+ ngpios = <1>; /* only gpio #32 */
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <87>;
+ };
+ };
+
+ dfx-registers {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 MBUS_ID(0x08, 0x00) 0 0x100000>;
+
+ dfx: dfx at 0 {
+ compatible = "simple-bus";
+ reg = <0 0x100000>;
+ };
+ };
+
+ switch {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 MBUS_ID(0x03, 0x00) 0 0x100000>;
+
+ packet-processor at 0 {
+ compatible = "marvell,prestera-98dx3236";
+ reg = <0 0x4000000>;
+ interrupts = <33>, <34>, <35>;
+ dfx = <&dfx>;
+ };
+ };
+ };
+};
+
+&pinctrl {
+ compatible = "marvell,98dx3236-pinctrl";
+
+ spi0_pins: spi0-pins {
+ marvell,pins = "mpp0", "mpp1",
+ "mpp2", "mpp3";
+ marvell,function = "spi0";
+ };
+};
+
+&sdio {
+ status = "disabled";
+};
+
+&crypto_sram0 {
+ status = "disabled";
+};
+
+&crypto_sram1 {
+ status = "disabled";
+};
diff --git a/arch/arm/boot/dts/armada-xp-98dx3336.dtsi b/arch/arm/boot/dts/armada-xp-98dx3336.dtsi
new file mode 100644
index 000000000000..9c9aa565fd82
--- /dev/null
+++ b/arch/arm/boot/dts/armada-xp-98dx3336.dtsi
@@ -0,0 +1,78 @@
+/*
+ * Device Tree Include file for Marvell 98dx3336 family SoC
+ *
+ * Copyright (C) 2016 Allied Telesis Labs
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contains definitions specific to the 98dx3336 SoC that are not
+ * common to all Armada XP SoCs.
+ */
+
+#include "armada-xp-98dx3236.dtsi"
+
+/ {
+ model = "Marvell 98DX3336 SoC";
+ compatible = "marvell,armadaxp-98dx3336", "marvell,armadaxp-98dx3236", "marvell,armadaxp", "marvell,armada-370-xp";
+
+ cpus {
+ cpu at 1 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <1>;
+ clocks = <&cpuclk 1>;
+ clock-latency = <1000000>;
+ };
+ };
+
+ soc {
+ internal-regs {
+ resume at 20980 {
+ compatible = "marvell,98dx3336-resume-ctrl";
+ reg = <0x20980 0x10>;
+ };
+ };
+
+ switch {
+ packet-processor at 0 {
+ compatible = "marvell,prestera-98dx3336";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/armada-xp-98dx4251.dtsi b/arch/arm/boot/dts/armada-xp-98dx4251.dtsi
new file mode 100644
index 000000000000..5f7edc23d5ae
--- /dev/null
+++ b/arch/arm/boot/dts/armada-xp-98dx4251.dtsi
@@ -0,0 +1,92 @@
+/*
+ * Device Tree Include file for Marvell 98dx4521 family SoC
+ *
+ * Copyright (C) 2016 Allied Telesis Labs
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Contains definitions specific to the 98dx4521 SoC that are not
+ * common to all Armada XP SoCs.
+ */
+
+#include "armada-xp-98dx3236.dtsi"
+
+/ {
+ model = "Marvell 98DX4251 SoC";
+ compatible = "marvell,armadaxp-98dx4521", "marvell,armadaxp-98dx3236", "marvell,armadaxp", "marvell,armada-370-xp";
+
+ cpus {
+ cpu at 1 {
+ device_type = "cpu";
+ compatible = "marvell,sheeva-v7";
+ reg = <1>;
+ clocks = <&cpuclk 1>;
+ clock-latency = <1000000>;
+ };
+ };
+
+ soc {
+ internal-regs {
+ resume at 20980 {
+ compatible = "marvell,98dx3336-resume-ctrl";
+ reg = <0x20980 0x10>;
+ };
+ };
+
+ switch {
+ packet-processor at 0 {
+ compatible = "marvell,prestera-98dx4521";
+ };
+ };
+ };
+};
+
+&sdio {
+ status = "okay";
+};
+
+&pinctrl {
+ compatible = "marvell,98dx4251-pinctrl";
+
+ sdio_pins: sdio-pins {
+ marvell,pins = "mpp5", "mpp6", "mpp7",
+ "mpp8", "mpp9", "mpp10";
+ marvell,function = "sd0";
+ };
+};
--
2.11.0.24.ge6920cf
^ permalink raw reply related
* [PATCHv2 5/5] arm: mvebu: Add device tree for db-dxbc2 and db-xc3-24g4xg boards
From: Chris Packham @ 2017-01-05 3:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105033641.6212-1-chris.packham@alliedtelesis.co.nz>
These boards are Marvell's evaluation boards for the 98DX4251 and
98DX3336 SoCs.
Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
Chaqnges in v2:
- None
arch/arm/boot/dts/db-dxbc2.dts | 159 ++++++++++++++++++++++++++++++++++++
arch/arm/boot/dts/db-xc3-24g4xg.dts | 155 +++++++++++++++++++++++++++++++++++
2 files changed, 314 insertions(+)
create mode 100644 arch/arm/boot/dts/db-dxbc2.dts
create mode 100644 arch/arm/boot/dts/db-xc3-24g4xg.dts
diff --git a/arch/arm/boot/dts/db-dxbc2.dts b/arch/arm/boot/dts/db-dxbc2.dts
new file mode 100644
index 000000000000..f56786cea5f8
--- /dev/null
+++ b/arch/arm/boot/dts/db-dxbc2.dts
@@ -0,0 +1,159 @@
+/*
+ * Device Tree file for DB-DXBC2 board
+ *
+ * Copyright (C) 2016 Allied Telesis Labs
+ *
+ * Based on armada-xp-db.dts
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Note: this Device Tree assumes that the bootloader has remapped the
+ * internal registers to 0xf1000000 (instead of the default
+ * 0xd0000000). The 0xf1000000 is the default used by the recent,
+ * DT-capable, U-Boot bootloaders provided by Marvell. Some earlier
+ * boards were delivered with an older version of the bootloader that
+ * left internal registers mapped at 0xd0000000. If you are in this
+ * situation, you should either update your bootloader (preferred
+ * solution) or the below Device Tree should be adjusted.
+ */
+
+/dts-v1/;
+#include "armada-xp-98dx4251.dtsi"
+
+/ {
+ model = "Marvell Bobcat2 Evaluation Board";
+ compatible = "marvell,db-dxbc2", "marvell,armadaxp-98dx4251", "marvell,armadaxp", "marvell,armada-370-xp";
+
+ chosen {
+ bootargs = "console=ttyS0,115200 earlyprintk";
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0 0x00000000 0 0x20000000>; /* 512 MB */
+ };
+
+ soc {
+ ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
+ MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
+ MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x1000000
+ MBUS_ID(0x03, 0x00) 0 0 0xa8000000 0x4000000
+ MBUS_ID(0x08, 0x00) 0 0 0xac000000 0x100000>;
+
+ devbus-bootcs {
+ status = "okay";
+
+ /* Device Bus parameters are required */
+
+ /* Read parameters */
+ devbus,bus-width = <16>;
+ devbus,turn-off-ps = <60000>;
+ devbus,badr-skew-ps = <0>;
+ devbus,acc-first-ps = <124000>;
+ devbus,acc-next-ps = <248000>;
+ devbus,rd-setup-ps = <0>;
+ devbus,rd-hold-ps = <0>;
+
+ /* Write parameters */
+ devbus,sync-enable = <0>;
+ devbus,wr-high-ps = <60000>;
+ devbus,wr-low-ps = <60000>;
+ devbus,ale-wr-ps = <60000>;
+ };
+
+ internal-regs {
+ serial at 12000 {
+ status = "okay";
+ };
+ serial at 12100 {
+ status = "okay";
+ };
+
+ i2c at 11000 {
+ clock-frequency = <100000>;
+ status = "okay";
+ };
+
+ mvsdio at d4000 {
+ pinctrl-0 = <&sdio_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+ /* No CD or WP GPIOs */
+ broken-cd;
+ };
+
+ nand at d0000 {
+ status = "okay";
+ num-cs = <1>;
+ marvell,nand-keep-config;
+ marvell,nand-enable-arbiter;
+ nand-on-flash-bbt;
+ nand-ecc-strength = <4>;
+ nand-ecc-step-size = <512>;
+ };
+ };
+ };
+};
+
+&spi0 {
+ status = "okay";
+
+ spi-flash at 0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "m25p64";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <20000000>;
+ m25p,fast-read;
+
+ partition at u-boot {
+ reg = <0x00000000 0x00100000>;
+ label = "u-boot";
+ };
+ partition at u-boot-env {
+ reg = <0x00100000 0x00040000>;
+ label = "u-boot-env";
+ };
+ partition at unused {
+ reg = <0x00140000 0x00ec0000>;
+ label = "unused";
+ };
+
+ };
+};
diff --git a/arch/arm/boot/dts/db-xc3-24g4xg.dts b/arch/arm/boot/dts/db-xc3-24g4xg.dts
new file mode 100644
index 000000000000..5eb89ffb9a7d
--- /dev/null
+++ b/arch/arm/boot/dts/db-xc3-24g4xg.dts
@@ -0,0 +1,155 @@
+/*
+ * Device Tree file for DB-XC3-24G4XG board
+ *
+ * Copyright (C) 2016 Allied Telesis Labs
+ *
+ * Based on armada-xp-db.dts
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Note: this Device Tree assumes that the bootloader has remapped the
+ * internal registers to 0xf1000000 (instead of the default
+ * 0xd0000000). The 0xf1000000 is the default used by the recent,
+ * DT-capable, U-Boot bootloaders provided by Marvell. Some earlier
+ * boards were delivered with an older version of the bootloader that
+ * left internal registers mapped at 0xd0000000. If you are in this
+ * situation, you should either update your bootloader (preferred
+ * solution) or the below Device Tree should be adjusted.
+ */
+
+/dts-v1/;
+#include "armada-xp-98dx3336.dtsi"
+
+/ {
+ model = "DB-XC3-24G4XG";
+ compatible = "marvell,db-xc3-24g4xg", "marvell,armadaxp-98dx3336", "marvell,armadaxp", "marvell,armada-370-xp";
+
+ chosen {
+ bootargs = "console=ttyS0,115200 earlyprintk";
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0 0x00000000 0 0x40000000>; /* 1 GB */
+ };
+
+ soc {
+ ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
+ MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
+ MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x1000000
+ MBUS_ID(0x03, 0x00) 0 0 0xa8000000 0x4000000
+ MBUS_ID(0x08, 0x00) 0 0 0xac000000 0x100000>;
+
+ devbus-bootcs {
+ status = "okay";
+
+ /* Device Bus parameters are required */
+
+ /* Read parameters */
+ devbus,bus-width = <16>;
+ devbus,turn-off-ps = <60000>;
+ devbus,badr-skew-ps = <0>;
+ devbus,acc-first-ps = <124000>;
+ devbus,acc-next-ps = <248000>;
+ devbus,rd-setup-ps = <0>;
+ devbus,rd-hold-ps = <0>;
+
+ /* Write parameters */
+ devbus,sync-enable = <0>;
+ devbus,wr-high-ps = <60000>;
+ devbus,wr-low-ps = <60000>;
+ devbus,ale-wr-ps = <60000>;
+ };
+
+ internal-regs {
+ serial at 12000 {
+ status = "okay";
+ };
+ serial at 12100 {
+ status = "okay";
+ };
+
+ i2c at 11000 {
+ clock-frequency = <100000>;
+ status = "okay";
+ };
+
+ mvsdio at d4000 {
+ status = "disabled";
+ };
+
+ nand at d0000 {
+ status = "okay";
+ num-cs = <1>;
+ marvell,nand-keep-config;
+ marvell,nand-enable-arbiter;
+ nand-on-flash-bbt;
+ nand-ecc-strength = <4>;
+ nand-ecc-step-size = <512>;
+ };
+ };
+ };
+};
+
+&spi0 {
+ status = "okay";
+
+ spi-flash at 0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "m25p64";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <20000000>;
+ m25p,fast-read;
+
+ partition at u-boot {
+ reg = <0x00000000 0x00100000>;
+ label = "u-boot";
+ };
+ partition at u-boot-env {
+ reg = <0x00100000 0x00040000>;
+ label = "u-boot-env";
+ };
+ partition at unused {
+ reg = <0x00140000 0x00ec0000>;
+ label = "unused";
+ };
+
+ };
+};
--
2.11.0.24.ge6920cf
^ permalink raw reply related
* [QUESTION] Arm64: Query L3 cache info via DT
From: Tan Xiaojun @ 2017-01-05 3:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <81784854-11f8-468a-a280-69be0a714a3b@arm.com>
OK. Thank you very much. I will try it in my arm64 board.
Xiaojun.
On 2017/1/4 19:47, Sudeep Holla wrote:
>
> Hi Tan,
>
> On 28/12/16 03:42, Tan Xiaojun wrote:
>> Hi.
>>
>> I saw you discussed how to achieve querying cache information and
>> tend to implement the external ones(like L3 cache) via DT a few
>> months ago.
>>
>> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-February/405399.html
>>
>> Are these implementations progressing? Forgive me to take the
>> liberty to ask, we care about this thing.
>>
>
> Yes the support to override the cache properties for DT was added in v4.10
>
> However, it still depends on the the sysreg to get the total levels of
> caches supported in the system. You may need to tweak a bit around that
> to support what you need.
>
>> If you've already implemented some codes, we can help with testing
>> (in Hisilicon D02, D03, D05) after you send it to the mail-list.
>>
>
> Sure, that would help.
>
>> We can try our best to help if there is any difficulty.
>
> OK, you can start trying the patch below :). It's not even compile
> tested, so you make have to make some changes necessary. I just wanted
> to put my thoughts here. Make sure all the L3 cacheinfo is present in DT.
>
> Regards,
> Sudeep
>
>
> --
>
> diff --git i/arch/arm64/kernel/cacheinfo.c w/arch/arm64/kernel/cacheinfo.c
> index 9617301f76b5..88fbcd368104 100644
> --- i/arch/arm64/kernel/cacheinfo.c
> +++ w/arch/arm64/kernel/cacheinfo.c
> @@ -84,7 +84,7 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,
>
> static int __init_cache_level(unsigned int cpu)
> {
> - unsigned int ctype, level, leaves;
> + unsigned int ctype, level, leaves, of_level;
> struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
>
> for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) {
> @@ -97,6 +97,17 @@ static int __init_cache_level(unsigned int cpu)
> leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
> }
>
> + of_level = of_count_cache_levels(cpu);
> + if (level < of_level) {
> + /*
> + * some external caches not specified in CLIDR_EL1
> + * the information may be available in the device tree
> + * only unified external caches are considered here
> + */
> + level = of_level;
> + leaves += (of_level - level);
> + }
> +
> this_cpu_ci->num_levels = level;
> this_cpu_ci->num_leaves = leaves;
> return 0;
> diff --git i/drivers/of/base.c w/drivers/of/base.c
> index d4bea3c797d6..8007f3b06cb8 100644
> --- i/drivers/of/base.c
> +++ w/drivers/of/base.c
> @@ -2267,6 +2267,20 @@ struct device_node *of_find_next_cache_node(const
> struct device_node *np)
> return NULL;
> }
>
> +int of_count_cache_levels(unsigned int cpu)
> +{
> + int level = 0;
> + struct device *cpu_dev = get_cpu_device(cpu);
> + struct device_node *np = cpu_dev->of_node;
> +
> + while (np) {
> + level++;
> + np = of_find_next_cache_node(np);
> + }
> +
> + return level;
> +}
> +
> /**
> * of_graph_parse_endpoint() - parse common endpoint node properties
> * @node: pointer to endpoint device_node
> diff --git i/include/linux/of.h w/include/linux/of.h
> index d72f01009297..c8597ae71ff3 100644
> --- i/include/linux/of.h
> +++ w/include/linux/of.h
> @@ -280,6 +280,7 @@ extern struct device_node
> *of_get_child_by_name(const struct device_node *node,
>
> /* cache lookup */
> extern struct device_node *of_find_next_cache_node(const struct
> device_node *);
> +extern int of_count_cache_levels(unsigned int cpu);
> extern struct device_node *of_find_node_with_property(
> struct device_node *from, const char *prop_name);
>
>
> .
>
^ permalink raw reply
* [PATCH v2] PCI: exynos: refactor exynos pcie driver
From: Pankaj Dubey @ 2017-01-05 3:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CGME20170105035104epcas1p2de6c52e577dd78a4f3539fb32d1f0dfe@epcas1p2.samsung.com>
From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
This patch does refactoring of Exynos PCIe driver to extend support
for other Exynos SoC.
Following are the main changes done via this patch:
1) It adds separate structs for memory, clock resources.
Reason behind this change is, moving ahead various Exynos SoC will
have different hardware resources such as iomem, clocks, regmap handles
etc. for PCIe controller, so keeping these resources in separate struct
will help us in intiailizing them via per SoC ops, and will avoid too
many of_machine_is_compatible in code, and help us simplifying
exynos_pcie struct.
2) It add exynos_pcie_ops struct which will allow us to support the
differences in resources in different Exynos SoC.
No functional change intended.
Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
Reviewed-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
---
This patch set is prepared on top of Krzysztof's for-next and
PCIe driver cleanup patch [1] by Jaehoon Chung.
[1]: https://lkml.org/lkml/2016/12/19/44
Changes from v1:
- Addressed review comments from Krzysztof and Jingoo Han.
drivers/pci/host/pci-exynos.c | 348 ++++++++++++++++++++++++++----------------
1 file changed, 219 insertions(+), 129 deletions(-)
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
index 33562cf..d91751b 100644
--- a/drivers/pci/host/pci-exynos.c
+++ b/drivers/pci/host/pci-exynos.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
@@ -28,16 +29,6 @@
#define to_exynos_pcie(x) container_of(x, struct exynos_pcie, pp)
-struct exynos_pcie {
- struct pcie_port pp;
- void __iomem *elbi_base; /* DT 0th resource */
- void __iomem *phy_base; /* DT 1st resource */
- void __iomem *block_base; /* DT 2nd resource */
- int reset_gpio;
- struct clk *clk;
- struct clk *bus_clk;
-};
-
/* PCIe ELBI registers */
#define PCIE_IRQ_PULSE 0x000
#define IRQ_INTA_ASSERT BIT(0)
@@ -102,6 +93,122 @@ struct exynos_pcie {
#define PCIE_PHY_TRSV3_PD_TSV BIT(7)
#define PCIE_PHY_TRSV3_LVCC 0x31c
+struct exynos_pcie_mem_res {
+ void __iomem *elbi_base; /* DT 0th resource: PCIE CTRL */
+ void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
+ void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
+};
+
+struct exynos_pcie_clk_res {
+ struct clk *clk;
+ struct clk *bus_clk;
+};
+
+struct exynos_pcie {
+ struct pcie_port pp;
+ struct exynos_pcie_mem_res *mem_res;
+ struct exynos_pcie_clk_res *clk_res;
+ const struct exynos_pcie_ops *ops;
+ int reset_gpio;
+};
+
+struct exynos_pcie_ops {
+ int (*get_mem_resources)(struct platform_device *pdev,
+ struct exynos_pcie *ep);
+ int (*get_clk_resources)(struct exynos_pcie *ep);
+ int (*init_clk_resources)(struct exynos_pcie *ep);
+ void (*deinit_clk_resources)(struct exynos_pcie *ep);
+};
+
+static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
+ struct exynos_pcie *ep)
+{
+ struct resource *res;
+ struct device *dev = ep->pp.dev;
+
+ ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
+ if (!ep->mem_res)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ep->mem_res->elbi_base))
+ return PTR_ERR(ep->mem_res->elbi_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ep->mem_res->phy_base))
+ return PTR_ERR(ep->mem_res->phy_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ ep->mem_res->block_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ep->mem_res->block_base))
+ return PTR_ERR(ep->mem_res->block_base);
+
+ return 0;
+}
+
+static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
+{
+ struct device *dev = ep->pp.dev;
+
+ ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
+ if (!ep->clk_res)
+ return -ENOMEM;
+
+ ep->clk_res->clk = devm_clk_get(dev, "pcie");
+ if (IS_ERR(ep->clk_res->clk)) {
+ dev_err(dev, "Failed to get pcie rc clock\n");
+ return PTR_ERR(ep->clk_res->clk);
+ }
+
+ ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
+ if (IS_ERR(ep->clk_res->bus_clk)) {
+ dev_err(dev, "Failed to get pcie bus clock\n");
+ return PTR_ERR(ep->clk_res->bus_clk);
+ }
+
+ return 0;
+}
+
+static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
+{
+ struct device *dev = ep->pp.dev;
+ int ret;
+
+ ret = clk_prepare_enable(ep->clk_res->clk);
+ if (ret) {
+ dev_err(dev, "cannot enable pcie rc clock");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ep->clk_res->bus_clk);
+ if (ret) {
+ dev_err(dev, "cannot enable pcie bus clock");
+ goto err_bus_clk;
+ }
+
+ return 0;
+
+err_bus_clk:
+ clk_disable_unprepare(ep->clk_res->clk);
+
+ return ret;
+}
+
+static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
+{
+ clk_disable_unprepare(ep->clk_res->bus_clk);
+ clk_disable_unprepare(ep->clk_res->clk);
+}
+
+static const struct exynos_pcie_ops exynos5440_pcie_ops = {
+ .get_mem_resources = exynos5440_pcie_get_mem_resources,
+ .get_clk_resources = exynos5440_pcie_get_clk_resources,
+ .init_clk_resources = exynos5440_pcie_init_clk_resources,
+ .deinit_clk_resources = exynos5440_pcie_deinit_clk_resources,
+};
+
static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
{
writel(val, base + reg);
@@ -116,155 +223,155 @@ static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
{
u32 val;
- val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
if (on)
val |= PCIE_ELBI_SLV_DBI_ENABLE;
else
val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
- exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
}
static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
{
u32 val;
- val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
if (on)
val |= PCIE_ELBI_SLV_DBI_ENABLE;
else
val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
- exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
}
static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
{
u32 val;
- val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
val &= ~PCIE_CORE_RESET_ENABLE;
- exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
- exynos_pcie_writel(ep->elbi_base, 0, PCIE_PWR_RESET);
- exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
- exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
}
static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
{
u32 val;
- val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
val |= PCIE_CORE_RESET_ENABLE;
- exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
- exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
- exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
- exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
- exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
- exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_MAC_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
}
static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
{
- exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_MAC_RESET);
- exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_GLOBAL_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
}
static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
{
- exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_GLOBAL_RESET);
- exynos_pcie_writel(ep->elbi_base, 1, PCIE_PWR_RESET);
- exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_COMMON_RESET);
- exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_CMN_REG);
- exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSVREG_RESET);
- exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSV_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
}
static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
{
u32 val;
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
val &= ~PCIE_PHY_COMMON_PD_CMN;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
val &= ~PCIE_PHY_TRSV0_PD_TSV;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
val &= ~PCIE_PHY_TRSV1_PD_TSV;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
val &= ~PCIE_PHY_TRSV2_PD_TSV;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
val &= ~PCIE_PHY_TRSV3_PD_TSV;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
}
static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
{
u32 val;
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
val |= PCIE_PHY_COMMON_PD_CMN;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
val |= PCIE_PHY_TRSV0_PD_TSV;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
val |= PCIE_PHY_TRSV1_PD_TSV;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
val |= PCIE_PHY_TRSV2_PD_TSV;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
- val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
val |= PCIE_PHY_TRSV3_PD_TSV;
- exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
}
static void exynos_pcie_init_phy(struct exynos_pcie *ep)
{
/* DCC feedback control off */
- exynos_pcie_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
/* set TX/RX impedance */
- exynos_pcie_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
/* set 50Mhz PHY clock */
- exynos_pcie_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
- exynos_pcie_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
/* set TX Differential output for lane 0 */
- exynos_pcie_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
/* set TX Pre-emphasis Level Control for lane 0 to minimum */
- exynos_pcie_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
/* set RX clock and data recovery bandwidth */
- exynos_pcie_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
- exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
- exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
- exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
- exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
/* change TX Pre-emphasis Level Control for lanes */
- exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
- exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
- exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
- exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
/* set LVCC */
- exynos_pcie_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
- exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
- exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
- exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
}
static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
@@ -295,25 +402,27 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
exynos_pcie_init_phy(exynos_pcie);
/* pulse for common reset */
- exynos_pcie_writel(exynos_pcie->block_base, 1, PCIE_PHY_COMMON_RESET);
+ exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
+ PCIE_PHY_COMMON_RESET);
udelay(500);
- exynos_pcie_writel(exynos_pcie->block_base, 0, PCIE_PHY_COMMON_RESET);
+ exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
+ PCIE_PHY_COMMON_RESET);
exynos_pcie_deassert_core_reset(exynos_pcie);
dw_pcie_setup_rc(pp);
exynos_pcie_assert_reset(exynos_pcie);
/* assert LTSSM enable */
- exynos_pcie_writel(exynos_pcie->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
- PCIE_APP_LTSSM_ENABLE);
+ exynos_pcie_writel(exynos_pcie->mem_res->elbi_base,
+ PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE);
/* check if the link is up or not */
if (!dw_pcie_wait_for_link(pp))
return 0;
- while (exynos_pcie_readl(exynos_pcie->phy_base,
+ while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
PCIE_PHY_PLL_LOCKED) == 0) {
- val = exynos_pcie_readl(exynos_pcie->block_base,
+ val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
PCIE_PHY_PLL_LOCKED);
dev_info(dev, "PLL Locked: 0x%x\n", val);
}
@@ -325,8 +434,8 @@ static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
{
u32 val;
- val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
- exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
}
static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
@@ -336,7 +445,7 @@ static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
/* enable INTX interrupt */
val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
- exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
}
static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
@@ -363,9 +472,9 @@ static void exynos_pcie_msi_init(struct exynos_pcie *ep)
dw_pcie_msi_init(pp);
/* enable MSI interrupt */
- val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_EN_LEVEL);
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
val |= IRQ_MSI_ENABLE;
- exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_LEVEL);
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
}
static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
@@ -425,7 +534,8 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
u32 val;
- val = exynos_pcie_readl(exynos_pcie->elbi_base, PCIE_ELBI_RDLH_LINKUP);
+ val = exynos_pcie_readl(exynos_pcie->mem_res->elbi_base,
+ PCIE_ELBI_RDLH_LINKUP);
if (val == PCIE_ELBI_LTSSM_ENABLE)
return 1;
@@ -503,7 +613,6 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
struct exynos_pcie *exynos_pcie;
struct pcie_port *pp;
struct device_node *np = dev->of_node;
- struct resource *res;
int ret;
exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
@@ -513,59 +622,37 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
pp = &exynos_pcie->pp;
pp->dev = dev;
- exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
-
- exynos_pcie->clk = devm_clk_get(dev, "pcie");
- if (IS_ERR(exynos_pcie->clk)) {
- dev_err(dev, "Failed to get pcie rc clock\n");
- return PTR_ERR(exynos_pcie->clk);
- }
- ret = clk_prepare_enable(exynos_pcie->clk);
- if (ret)
- return ret;
+ exynos_pcie->ops = (const struct exynos_pcie_ops *)
+ of_device_get_match_data(dev);
- exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
- if (IS_ERR(exynos_pcie->bus_clk)) {
- dev_err(dev, "Failed to get pcie bus clock\n");
- ret = PTR_ERR(exynos_pcie->bus_clk);
- goto fail_clk;
- }
- ret = clk_prepare_enable(exynos_pcie->bus_clk);
- if (ret)
- goto fail_clk;
+ exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- exynos_pcie->elbi_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(exynos_pcie->elbi_base)) {
- ret = PTR_ERR(exynos_pcie->elbi_base);
- goto fail_bus_clk;
+ if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
+ ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
+ if (ret)
+ return ret;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- exynos_pcie->phy_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(exynos_pcie->phy_base)) {
- ret = PTR_ERR(exynos_pcie->phy_base);
- goto fail_bus_clk;
- }
+ if (exynos_pcie->ops && exynos_pcie->ops->get_clk_resources) {
+ ret = exynos_pcie->ops->get_clk_resources(exynos_pcie);
+ if (ret)
+ return ret;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- exynos_pcie->block_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(exynos_pcie->block_base)) {
- ret = PTR_ERR(exynos_pcie->block_base);
- goto fail_bus_clk;
+ ret = exynos_pcie->ops->init_clk_resources(exynos_pcie);
+ if (ret)
+ return ret;
}
ret = exynos_add_pcie_port(exynos_pcie, pdev);
if (ret < 0)
- goto fail_bus_clk;
+ goto fail_probe;
platform_set_drvdata(pdev, exynos_pcie);
return 0;
-fail_bus_clk:
- clk_disable_unprepare(exynos_pcie->bus_clk);
-fail_clk:
- clk_disable_unprepare(exynos_pcie->clk);
+fail_probe:
+ if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
+ exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
return ret;
}
@@ -573,14 +660,17 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev)
{
struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
- clk_disable_unprepare(exynos_pcie->bus_clk);
- clk_disable_unprepare(exynos_pcie->clk);
+ if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
+ exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
return 0;
}
static const struct of_device_id exynos_pcie_of_match[] = {
- { .compatible = "samsung,exynos5440-pcie", },
+ {
+ .compatible = "samsung,exynos5440-pcie",
+ .data = &exynos5440_pcie_ops
+ },
{},
};
--
2.7.4
^ permalink raw reply related
* [PATCH v3] mfd: axp20x: Fix AXP806 access errors on cold boot
From: Chen-Yu Tsai @ 2017-01-05 4:01 UTC (permalink / raw)
To: linux-arm-kernel
The AXP806 supports either master/standalone or slave mode.
Slave mode allows sharing the serial bus, even with multiple
AXP806 which all have the same hardware address.
This is done with extra "serial interface address extension",
or AXP806_BUS_ADDR_EXT, and "register address extension", or
AXP806_REG_ADDR_EXT, registers. The former is read-only, with
1 bit customizable at the factory, and 1 bit depending on the
state of an external pin. The latter is writable. Only when
the these device addressing bits (in the upper 4 bits of the
registers) match, will the device respond to operations on
its other registers.
The AXP806_REG_ADDR_EXT was previously configured by Allwinner's
bootloader. Work on U-boot SPL support now allows us to switch
to mainline U-boot, which doesn't do this for us. There might
be other bare minimum bootloaders out there which don't to this
either. It's best to handle this in the kernel.
This patch sets AXP806_REG_ADDR_EXT to 0x10, which is what we
know to be the proper value for a standard AXP806 in slave mode.
Afterwards it will reinitialize the regmap cache, to purge any
invalid stale values.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
Changes since v2:
- Dropped regcache_sync_region() and regmap_reinit_cache() calls.
The write immediately after getting the regmap from the caller
of axp20x_device_probe() is enough.
- Slightly rearranged a few sentences in the comment block.
---
drivers/mfd/axp20x.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index ed918de84238..81b9ddd10e6c 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -31,6 +31,8 @@
#define AXP20X_OFF 0x80
+#define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4)
+
static const char * const axp20x_model_names[] = {
"AXP152",
"AXP202",
@@ -830,6 +832,30 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
{
int ret;
+ /*
+ * The AXP806 supports either master/standalone or slave mode.
+ * Slave mode allows sharing the serial bus, even with multiple
+ * AXP806 which all have the same hardware address.
+ *
+ * This is done with extra "serial interface address extension",
+ * or AXP806_BUS_ADDR_EXT, and "register address extension", or
+ * AXP806_REG_ADDR_EXT, registers. The former is read-only, with
+ * 1 bit customizable at the factory, and 1 bit depending on the
+ * state of an external pin. The latter is writable. The device
+ * will only respond to operations to its other registers when
+ * the these device addressing bits (in the upper 4 bits of the
+ * registers) match.
+ *
+ * Since we only support an AXP806 chained to an AXP809 in slave
+ * mode, and there isn't any existing hardware which uses AXP806
+ * in master mode, or has 2 AXP806s in the same system, we can
+ * just program the register address extension to the slave mode
+ * address.
+ */
+ if (axp20x->variant == AXP806_ID)
+ regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT,
+ AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE);
+
ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq,
IRQF_ONESHOT | IRQF_SHARED, -1,
axp20x->regmap_irq_chip,
--
2.11.0
^ permalink raw reply related
* [PATCHv2 2/5] arm: mvebu: support for SMP on 98DX3336 SoC
From: Florian Fainelli @ 2017-01-05 4:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105033641.6212-3-chris.packham@alliedtelesis.co.nz>
Le 01/04/17 ? 19:36, Chris Packham a ?crit :
> Compared to the armada-xp the 98DX3336 uses different registers to set
> the boot address for the secondary CPU so a new enable-method is needed.
> This will only work if the machine definition doesn't define an overall
> smp_ops because there is not currently a way of overriding this from the
> device tree if it is set in the machine definition.
Not sure I follow you here, in theory, each individual CPU could have a
different enable-method property, and considering how you leverage
existing DTS include files, you should be able to override the DTS with
the appropriate enable-method, or do you have a different problem?
mv98dx3236_resume_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
> +{
> + WARN_ON(hw_cpu != 1);
> +
> + writel(0, mv98dx3236_resume_base + MV98DX3236_CPU_RESUME_CTRL_OFFSET);
> + writel(virt_to_phys(boot_addr), mv98dx3236_resume_base +
> + MV98DX3236_CPU_RESUME_ADDR_OFFSET);
I just submitted a patch series that switches such users to
__pa_symbol() instead of virt_to_phys() because this is a kernel image
symbol. For now this will work as-is, but depending on which patch
series makes it first, it may be a good idea to keep this on someone's
TODO list (yours or mine). I do expect having to make a second pass of
conversions anyway.
[1]: https://lkml.org/lkml/2017/1/4/1101
> +}
> +
> +static int __init mv98dx3236_resume_init(void)
> +{
> + struct device_node *np;
> + struct resource res;
> + int ret = 0;
> +
> + np = of_find_matching_node(NULL, of_mv98dx3236_resume_table);
> + if (!np)
> + return 0;
> +
> + pr_info("Initializing 98DX3236 Resume\n");
This can be probably be dropped, you should know fairly quickly if this
succeeded or not.
Can't this function be implemented as a smp_ops::smp_init_cpus instead
of having this initialization done at arch_initcall time?
> +
> + if (of_address_to_resource(np, 0, &res)) {
> + pr_err("unable to get resource\n");
> + ret = -ENOENT;
> + goto out;
> + }
> +
> + if (!request_mem_region(res.start, resource_size(&res),
> + np->full_name)) {
> + pr_err("unable to request region\n");
> + ret = -EBUSY;
> + goto out;
> + }
You should be able to use of_io_request_and_map() and here.
--
Florian
^ permalink raw reply
* [PATCH 01/22] dt-bindings: iio: adc: add AXP20X/AXP22X ADC DT binding
From: Chen-Yu Tsai @ 2017-01-05 4:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103232035.wwsqmea5dxzu2r5m@rob-hp-laptop>
On Wed, Jan 4, 2017 at 7:20 AM, Rob Herring <robh@kernel.org> wrote:
> On Mon, Jan 02, 2017 at 05:37:01PM +0100, Quentin Schulz wrote:
>> The X-Powers AXP20X and AXP22X PMICs have multiple ADCs. They expose the
>> battery voltage, battery charge and discharge currents, AC-in and VBUS
>> voltages and currents, 2 GPIOs muxable in ADC mode and PMIC temperature.
>>
>> This adds the device tree binding documentation for the X-Powers AXP20X
>> and AXP22X PMICs ADCs.
>>
>> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
>> ---
>> .../devicetree/bindings/iio/adc/axp20x_adc.txt | 24 ++++++++++++++++++++++
>> 1 file changed, 24 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/iio/adc/axp20x_adc.txt
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/axp20x_adc.txt b/Documentation/devicetree/bindings/iio/adc/axp20x_adc.txt
>> new file mode 100644
>> index 0000000..1b60065
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/axp20x_adc.txt
>> @@ -0,0 +1,24 @@
>> +X-Powers AXP20X and AXP22X PMIC Analog to Digital Converter (ADC)
>> +
>> +The X-Powers AXP20X and AXP22X PMICs have multiple ADCs. They expose the
>> +battery voltage, battery charge and discharge currents, AC-in and VBUS
>> +voltages and currents, 2 GPIOs muxable in ADC mode and PMIC temperature.
>> +
>> +The AXP22X PMICs do not have all ADCs of the AXP20X though.
>> +
>> +Required properties:
>> + - compatible, one of:
>> + "x-powers,axp209-adc"
>> + "x-powers,axp221-adc"
>> + - #io-channel-cells = <1>;
>> +
>> +This is a subnode of the AXP20X PMIC.
>> +
>> +Example:
>> +
>> +&axp209 {
>> + axp209_adc: axp209_adc {
>
> Use 'adc' for node name:
>
> With that,
>
> Acked-by: Rob Herring <robh@kernel.org>
Same comments as Rob.
Acked-by: Chen-Yu Tsai <wens@csie.org>
>
>> + compatible = "x-powers,axp209-adc";
>> + #io-channel-cells = <1>;
>> + };
>> +};
>> --
>> 2.9.3
>>
^ permalink raw reply
* [PATCHv2 4/5] arm: mvebu: Add device tree for 98DX3236 SoCs
From: Florian Fainelli @ 2017-01-05 4:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105033641.6212-5-chris.packham@alliedtelesis.co.nz>
Le 01/04/17 ? 19:36, Chris Packham a ?crit :
> The Marvell 98DX3236, 98DX3336, 98DX4521 and variants are switch ASICs
> with integrated CPUs. They are similar to the Armada XP SoCs but have
> different I/O interfaces.
>
> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
> ---
> +
> + switch {
> + packet-processor at 0 {
> + compatible = "marvell,prestera-98dx4521";
> + };
> + };
This may be a bit premature if you are not providing a binding document
for this sub-node, you might as well add it once you also add a
corresponding driver (or if this will remain out of tree, at least
submitting a separate binding document).
Also, if this node's unit address is 0, you would expect at least a reg
property whose address cell is 0 to be present.
--
Florian
^ permalink raw reply
* [PATCHv2 0/5] Support for Marvell switches with integrated CPUs
From: Florian Fainelli @ 2017-01-05 4:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105033641.6212-1-chris.packham@alliedtelesis.co.nz>
Le 01/04/17 ? 19:36, Chris Packham a ?crit :
> The 98DX3236, 98DX3336 and 98DX4251 are a set of switch ASICs with
> integrated CPUs. They CPU block is common within these product lines and
> (as far as I can tell/have been told) is based on the Armada XP. There
> are a few differences due to the fact they have to squeeze the CPU into
> the same package as the switch.
It's really great to see these changes, do you have a plan to also add
support for the integrated switch using a DSA/switchdev driver?
Thanks!
--
Florian
^ permalink raw reply
* [PATCH v2] PCI: exynos: refactor exynos pcie driver
From: Alim Akhtar @ 2017-01-05 4:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483588418-2551-1-git-send-email-pankaj.dubey@samsung.com>
Hello Pankaj,
On 01/05/2017 09:23 AM, Pankaj Dubey wrote:
> From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>
> Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
> This patch does refactoring of Exynos PCIe driver to extend support
> for other Exynos SoC.
>
> Following are the main changes done via this patch:
>
> 1) It adds separate structs for memory, clock resources.
>
> Reason behind this change is, moving ahead various Exynos SoC will
> have different hardware resources such as iomem, clocks, regmap handles
> etc. for PCIe controller, so keeping these resources in separate struct
> will help us in intiailizing them via per SoC ops, and will avoid too
> many of_machine_is_compatible in code, and help us simplifying
> exynos_pcie struct.
>
> 2) It add exynos_pcie_ops struct which will allow us to support the
> differences in resources in different Exynos SoC.
>
> No functional change intended.
>
> Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
> Reviewed-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
> ---
Looks good.
Reviewed-by: Alim Akhtar <alim.akhtar@samsung.com>
> This patch set is prepared on top of Krzysztof's for-next and
> PCIe driver cleanup patch [1] by Jaehoon Chung.
>
> [1]: https://lkml.org/lkml/2016/12/19/44
>
> Changes from v1:
> - Addressed review comments from Krzysztof and Jingoo Han.
>
> drivers/pci/host/pci-exynos.c | 348 ++++++++++++++++++++++++++----------------
> 1 file changed, 219 insertions(+), 129 deletions(-)
>
> diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
> index 33562cf..d91751b 100644
> --- a/drivers/pci/host/pci-exynos.c
> +++ b/drivers/pci/host/pci-exynos.c
> @@ -17,6 +17,7 @@
> #include <linux/interrupt.h>
> #include <linux/kernel.h>
> #include <linux/init.h>
> +#include <linux/of_device.h>
> #include <linux/of_gpio.h>
> #include <linux/pci.h>
> #include <linux/platform_device.h>
> @@ -28,16 +29,6 @@
>
> #define to_exynos_pcie(x) container_of(x, struct exynos_pcie, pp)
>
> -struct exynos_pcie {
> - struct pcie_port pp;
> - void __iomem *elbi_base; /* DT 0th resource */
> - void __iomem *phy_base; /* DT 1st resource */
> - void __iomem *block_base; /* DT 2nd resource */
> - int reset_gpio;
> - struct clk *clk;
> - struct clk *bus_clk;
> -};
> -
> /* PCIe ELBI registers */
> #define PCIE_IRQ_PULSE 0x000
> #define IRQ_INTA_ASSERT BIT(0)
> @@ -102,6 +93,122 @@ struct exynos_pcie {
> #define PCIE_PHY_TRSV3_PD_TSV BIT(7)
> #define PCIE_PHY_TRSV3_LVCC 0x31c
>
> +struct exynos_pcie_mem_res {
> + void __iomem *elbi_base; /* DT 0th resource: PCIE CTRL */
> + void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
> + void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
> +};
> +
> +struct exynos_pcie_clk_res {
> + struct clk *clk;
> + struct clk *bus_clk;
> +};
> +
> +struct exynos_pcie {
> + struct pcie_port pp;
> + struct exynos_pcie_mem_res *mem_res;
> + struct exynos_pcie_clk_res *clk_res;
> + const struct exynos_pcie_ops *ops;
> + int reset_gpio;
> +};
> +
> +struct exynos_pcie_ops {
> + int (*get_mem_resources)(struct platform_device *pdev,
> + struct exynos_pcie *ep);
> + int (*get_clk_resources)(struct exynos_pcie *ep);
> + int (*init_clk_resources)(struct exynos_pcie *ep);
> + void (*deinit_clk_resources)(struct exynos_pcie *ep);
> +};
> +
> +static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
> + struct exynos_pcie *ep)
> +{
> + struct resource *res;
> + struct device *dev = ep->pp.dev;
> +
> + ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
> + if (!ep->mem_res)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ep->mem_res->elbi_base))
> + return PTR_ERR(ep->mem_res->elbi_base);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ep->mem_res->phy_base))
> + return PTR_ERR(ep->mem_res->phy_base);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> + ep->mem_res->block_base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ep->mem_res->block_base))
> + return PTR_ERR(ep->mem_res->block_base);
> +
> + return 0;
> +}
> +
> +static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
> +{
> + struct device *dev = ep->pp.dev;
> +
> + ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
> + if (!ep->clk_res)
> + return -ENOMEM;
> +
> + ep->clk_res->clk = devm_clk_get(dev, "pcie");
> + if (IS_ERR(ep->clk_res->clk)) {
> + dev_err(dev, "Failed to get pcie rc clock\n");
> + return PTR_ERR(ep->clk_res->clk);
> + }
> +
> + ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
> + if (IS_ERR(ep->clk_res->bus_clk)) {
> + dev_err(dev, "Failed to get pcie bus clock\n");
> + return PTR_ERR(ep->clk_res->bus_clk);
> + }
> +
> + return 0;
> +}
> +
> +static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
> +{
> + struct device *dev = ep->pp.dev;
> + int ret;
> +
> + ret = clk_prepare_enable(ep->clk_res->clk);
> + if (ret) {
> + dev_err(dev, "cannot enable pcie rc clock");
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(ep->clk_res->bus_clk);
> + if (ret) {
> + dev_err(dev, "cannot enable pcie bus clock");
> + goto err_bus_clk;
> + }
> +
> + return 0;
> +
> +err_bus_clk:
> + clk_disable_unprepare(ep->clk_res->clk);
> +
> + return ret;
> +}
> +
> +static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
> +{
> + clk_disable_unprepare(ep->clk_res->bus_clk);
> + clk_disable_unprepare(ep->clk_res->clk);
> +}
> +
> +static const struct exynos_pcie_ops exynos5440_pcie_ops = {
> + .get_mem_resources = exynos5440_pcie_get_mem_resources,
> + .get_clk_resources = exynos5440_pcie_get_clk_resources,
> + .init_clk_resources = exynos5440_pcie_init_clk_resources,
> + .deinit_clk_resources = exynos5440_pcie_deinit_clk_resources,
> +};
> +
> static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
> {
> writel(val, base + reg);
> @@ -116,155 +223,155 @@ static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
> if (on)
> val |= PCIE_ELBI_SLV_DBI_ENABLE;
> else
> val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
> }
>
> static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
> if (on)
> val |= PCIE_ELBI_SLV_DBI_ENABLE;
> else
> val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
> }
>
> static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
> val &= ~PCIE_CORE_RESET_ENABLE;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
> - exynos_pcie_writel(ep->elbi_base, 0, PCIE_PWR_RESET);
> - exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
> - exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
> }
>
> static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
> val |= PCIE_CORE_RESET_ENABLE;
>
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
> - exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
> - exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
> - exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
> - exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
> - exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_MAC_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
> }
>
> static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
> {
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_MAC_RESET);
> - exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_GLOBAL_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
> }
>
> static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
> {
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_GLOBAL_RESET);
> - exynos_pcie_writel(ep->elbi_base, 1, PCIE_PWR_RESET);
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_COMMON_RESET);
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_CMN_REG);
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSVREG_RESET);
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSV_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
> }
>
> static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
> val &= ~PCIE_PHY_COMMON_PD_CMN;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
> val &= ~PCIE_PHY_TRSV0_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
> val &= ~PCIE_PHY_TRSV1_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
> val &= ~PCIE_PHY_TRSV2_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
> val &= ~PCIE_PHY_TRSV3_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
> }
>
> static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
> val |= PCIE_PHY_COMMON_PD_CMN;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
> val |= PCIE_PHY_TRSV0_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
> val |= PCIE_PHY_TRSV1_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
> val |= PCIE_PHY_TRSV2_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
> val |= PCIE_PHY_TRSV3_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
> }
>
> static void exynos_pcie_init_phy(struct exynos_pcie *ep)
> {
> /* DCC feedback control off */
> - exynos_pcie_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>
> /* set TX/RX impedance */
> - exynos_pcie_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>
> /* set 50Mhz PHY clock */
> - exynos_pcie_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
> - exynos_pcie_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>
> /* set TX Differential output for lane 0 */
> - exynos_pcie_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>
> /* set TX Pre-emphasis Level Control for lane 0 to minimum */
> - exynos_pcie_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>
> /* set RX clock and data recovery bandwidth */
> - exynos_pcie_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
> - exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
> - exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
> - exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
> - exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>
> /* change TX Pre-emphasis Level Control for lanes */
> - exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
> - exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
> - exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
> - exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>
> /* set LVCC */
> - exynos_pcie_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
> - exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
> - exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
> - exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
> }
>
> static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
> @@ -295,25 +402,27 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
> exynos_pcie_init_phy(exynos_pcie);
>
> /* pulse for common reset */
> - exynos_pcie_writel(exynos_pcie->block_base, 1, PCIE_PHY_COMMON_RESET);
> + exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
> + PCIE_PHY_COMMON_RESET);
> udelay(500);
> - exynos_pcie_writel(exynos_pcie->block_base, 0, PCIE_PHY_COMMON_RESET);
> + exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
> + PCIE_PHY_COMMON_RESET);
>
> exynos_pcie_deassert_core_reset(exynos_pcie);
> dw_pcie_setup_rc(pp);
> exynos_pcie_assert_reset(exynos_pcie);
>
> /* assert LTSSM enable */
> - exynos_pcie_writel(exynos_pcie->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
> - PCIE_APP_LTSSM_ENABLE);
> + exynos_pcie_writel(exynos_pcie->mem_res->elbi_base,
> + PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE);
>
> /* check if the link is up or not */
> if (!dw_pcie_wait_for_link(pp))
> return 0;
>
> - while (exynos_pcie_readl(exynos_pcie->phy_base,
> + while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
> PCIE_PHY_PLL_LOCKED) == 0) {
> - val = exynos_pcie_readl(exynos_pcie->block_base,
> + val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
> PCIE_PHY_PLL_LOCKED);
> dev_info(dev, "PLL Locked: 0x%x\n", val);
> }
> @@ -325,8 +434,8 @@ static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
> }
>
> static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
> @@ -336,7 +445,7 @@ static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
> /* enable INTX interrupt */
> val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
> IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
> }
>
> static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
> @@ -363,9 +472,9 @@ static void exynos_pcie_msi_init(struct exynos_pcie *ep)
> dw_pcie_msi_init(pp);
>
> /* enable MSI interrupt */
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_EN_LEVEL);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
> val |= IRQ_MSI_ENABLE;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_LEVEL);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
> }
>
> static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
> @@ -425,7 +534,8 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
> struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
> u32 val;
>
> - val = exynos_pcie_readl(exynos_pcie->elbi_base, PCIE_ELBI_RDLH_LINKUP);
> + val = exynos_pcie_readl(exynos_pcie->mem_res->elbi_base,
> + PCIE_ELBI_RDLH_LINKUP);
> if (val == PCIE_ELBI_LTSSM_ENABLE)
> return 1;
>
> @@ -503,7 +613,6 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
> struct exynos_pcie *exynos_pcie;
> struct pcie_port *pp;
> struct device_node *np = dev->of_node;
> - struct resource *res;
> int ret;
>
> exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
> @@ -513,59 +622,37 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
> pp = &exynos_pcie->pp;
> pp->dev = dev;
>
> - exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
> -
> - exynos_pcie->clk = devm_clk_get(dev, "pcie");
> - if (IS_ERR(exynos_pcie->clk)) {
> - dev_err(dev, "Failed to get pcie rc clock\n");
> - return PTR_ERR(exynos_pcie->clk);
> - }
> - ret = clk_prepare_enable(exynos_pcie->clk);
> - if (ret)
> - return ret;
> + exynos_pcie->ops = (const struct exynos_pcie_ops *)
> + of_device_get_match_data(dev);
>
> - exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
> - if (IS_ERR(exynos_pcie->bus_clk)) {
> - dev_err(dev, "Failed to get pcie bus clock\n");
> - ret = PTR_ERR(exynos_pcie->bus_clk);
> - goto fail_clk;
> - }
> - ret = clk_prepare_enable(exynos_pcie->bus_clk);
> - if (ret)
> - goto fail_clk;
> + exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - exynos_pcie->elbi_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(exynos_pcie->elbi_base)) {
> - ret = PTR_ERR(exynos_pcie->elbi_base);
> - goto fail_bus_clk;
> + if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
> + ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
> + if (ret)
> + return ret;
> }
>
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> - exynos_pcie->phy_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(exynos_pcie->phy_base)) {
> - ret = PTR_ERR(exynos_pcie->phy_base);
> - goto fail_bus_clk;
> - }
> + if (exynos_pcie->ops && exynos_pcie->ops->get_clk_resources) {
> + ret = exynos_pcie->ops->get_clk_resources(exynos_pcie);
> + if (ret)
> + return ret;
>
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> - exynos_pcie->block_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(exynos_pcie->block_base)) {
> - ret = PTR_ERR(exynos_pcie->block_base);
> - goto fail_bus_clk;
> + ret = exynos_pcie->ops->init_clk_resources(exynos_pcie);
> + if (ret)
> + return ret;
> }
>
> ret = exynos_add_pcie_port(exynos_pcie, pdev);
> if (ret < 0)
> - goto fail_bus_clk;
> + goto fail_probe;
>
> platform_set_drvdata(pdev, exynos_pcie);
> return 0;
>
> -fail_bus_clk:
> - clk_disable_unprepare(exynos_pcie->bus_clk);
> -fail_clk:
> - clk_disable_unprepare(exynos_pcie->clk);
> +fail_probe:
> + if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
> + exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
> return ret;
> }
>
> @@ -573,14 +660,17 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev)
> {
> struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
>
> - clk_disable_unprepare(exynos_pcie->bus_clk);
> - clk_disable_unprepare(exynos_pcie->clk);
> + if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
> + exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>
> return 0;
> }
>
> static const struct of_device_id exynos_pcie_of_match[] = {
> - { .compatible = "samsung,exynos5440-pcie", },
> + {
> + .compatible = "samsung,exynos5440-pcie",
> + .data = &exynos5440_pcie_ops
> + },
> {},
> };
>
>
^ permalink raw reply
* [PATCH 02/22] mfd: axp20x: add ADC data regs to volatile regs for AXP22X
From: Chen-Yu Tsai @ 2017-01-05 4:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170102163723.7939-3-quentin.schulz@free-electrons.com>
On Tue, Jan 3, 2017 at 12:37 AM, Quentin Schulz
<quentin.schulz@free-electrons.com> wrote:
> The AXP22X PMICs have multiple ADCs, each one exposing data from the
> different power supplies connected to the PMIC.
>
> This adds the different ADC data registers to the volatile registers of
> the AXP22X PMIC.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
> drivers/mfd/axp20x.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> index 619a83e..a33db5e 100644
> --- a/drivers/mfd/axp20x.c
> +++ b/drivers/mfd/axp20x.c
> @@ -100,6 +100,7 @@ static const struct regmap_range axp22x_writeable_ranges[] = {
> static const struct regmap_range axp22x_volatile_ranges[] = {
> regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP20X_PWR_OP_MODE),
> regmap_reg_range(AXP20X_VBUS_IPSOUT_MGMT, AXP20X_VBUS_IPSOUT_MGMT),
> + regmap_reg_range(AXP22X_TEMP_ADC_H, AXP20X_BATT_DISCHRG_I_L),
You added this macro in the next patch. Please move that hunk to this patch.
ChenYu
> regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
> regmap_reg_range(AXP22X_GPIO_STATE, AXP22X_GPIO_STATE),
> regmap_reg_range(AXP22X_PMIC_ADC_H, AXP20X_IPSOUT_V_HIGH_L),
> --
> 2.9.3
>
^ permalink raw reply
* [PATCH v2] PCI: exynos: refactor exynos pcie driver
From: Jaehoon Chung @ 2017-01-05 4:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483588418-2551-1-git-send-email-pankaj.dubey@samsung.com>
On 01/05/2017 12:53 PM, Pankaj Dubey wrote:
> From: Niyas Ahmed S T <niyas.ahmed@samsung.com>
>
> Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
> This patch does refactoring of Exynos PCIe driver to extend support
> for other Exynos SoC.
>
> Following are the main changes done via this patch:
>
> 1) It adds separate structs for memory, clock resources.
>
> Reason behind this change is, moving ahead various Exynos SoC will
> have different hardware resources such as iomem, clocks, regmap handles
> etc. for PCIe controller, so keeping these resources in separate struct
> will help us in intiailizing them via per SoC ops, and will avoid too
> many of_machine_is_compatible in code, and help us simplifying
> exynos_pcie struct.
>
> 2) It add exynos_pcie_ops struct which will allow us to support the
> differences in resources in different Exynos SoC.
>
> No functional change intended.
>
> Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
> Reviewed-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
Best Regards,
Jaehoon Chung
> ---
> This patch set is prepared on top of Krzysztof's for-next and
> PCIe driver cleanup patch [1] by Jaehoon Chung.
>
> [1]: https://lkml.org/lkml/2016/12/19/44
>
> Changes from v1:
> - Addressed review comments from Krzysztof and Jingoo Han.
>
> drivers/pci/host/pci-exynos.c | 348 ++++++++++++++++++++++++++----------------
> 1 file changed, 219 insertions(+), 129 deletions(-)
>
> diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
> index 33562cf..d91751b 100644
> --- a/drivers/pci/host/pci-exynos.c
> +++ b/drivers/pci/host/pci-exynos.c
> @@ -17,6 +17,7 @@
> #include <linux/interrupt.h>
> #include <linux/kernel.h>
> #include <linux/init.h>
> +#include <linux/of_device.h>
> #include <linux/of_gpio.h>
> #include <linux/pci.h>
> #include <linux/platform_device.h>
> @@ -28,16 +29,6 @@
>
> #define to_exynos_pcie(x) container_of(x, struct exynos_pcie, pp)
>
> -struct exynos_pcie {
> - struct pcie_port pp;
> - void __iomem *elbi_base; /* DT 0th resource */
> - void __iomem *phy_base; /* DT 1st resource */
> - void __iomem *block_base; /* DT 2nd resource */
> - int reset_gpio;
> - struct clk *clk;
> - struct clk *bus_clk;
> -};
> -
> /* PCIe ELBI registers */
> #define PCIE_IRQ_PULSE 0x000
> #define IRQ_INTA_ASSERT BIT(0)
> @@ -102,6 +93,122 @@ struct exynos_pcie {
> #define PCIE_PHY_TRSV3_PD_TSV BIT(7)
> #define PCIE_PHY_TRSV3_LVCC 0x31c
>
> +struct exynos_pcie_mem_res {
> + void __iomem *elbi_base; /* DT 0th resource: PCIE CTRL */
> + void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
> + void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
> +};
> +
> +struct exynos_pcie_clk_res {
> + struct clk *clk;
> + struct clk *bus_clk;
> +};
> +
> +struct exynos_pcie {
> + struct pcie_port pp;
> + struct exynos_pcie_mem_res *mem_res;
> + struct exynos_pcie_clk_res *clk_res;
> + const struct exynos_pcie_ops *ops;
> + int reset_gpio;
> +};
> +
> +struct exynos_pcie_ops {
> + int (*get_mem_resources)(struct platform_device *pdev,
> + struct exynos_pcie *ep);
> + int (*get_clk_resources)(struct exynos_pcie *ep);
> + int (*init_clk_resources)(struct exynos_pcie *ep);
> + void (*deinit_clk_resources)(struct exynos_pcie *ep);
> +};
> +
> +static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
> + struct exynos_pcie *ep)
> +{
> + struct resource *res;
> + struct device *dev = ep->pp.dev;
> +
> + ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
> + if (!ep->mem_res)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ep->mem_res->elbi_base))
> + return PTR_ERR(ep->mem_res->elbi_base);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ep->mem_res->phy_base))
> + return PTR_ERR(ep->mem_res->phy_base);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> + ep->mem_res->block_base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(ep->mem_res->block_base))
> + return PTR_ERR(ep->mem_res->block_base);
> +
> + return 0;
> +}
> +
> +static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
> +{
> + struct device *dev = ep->pp.dev;
> +
> + ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
> + if (!ep->clk_res)
> + return -ENOMEM;
> +
> + ep->clk_res->clk = devm_clk_get(dev, "pcie");
> + if (IS_ERR(ep->clk_res->clk)) {
> + dev_err(dev, "Failed to get pcie rc clock\n");
> + return PTR_ERR(ep->clk_res->clk);
> + }
> +
> + ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
> + if (IS_ERR(ep->clk_res->bus_clk)) {
> + dev_err(dev, "Failed to get pcie bus clock\n");
> + return PTR_ERR(ep->clk_res->bus_clk);
> + }
> +
> + return 0;
> +}
> +
> +static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
> +{
> + struct device *dev = ep->pp.dev;
> + int ret;
> +
> + ret = clk_prepare_enable(ep->clk_res->clk);
> + if (ret) {
> + dev_err(dev, "cannot enable pcie rc clock");
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(ep->clk_res->bus_clk);
> + if (ret) {
> + dev_err(dev, "cannot enable pcie bus clock");
> + goto err_bus_clk;
> + }
> +
> + return 0;
> +
> +err_bus_clk:
> + clk_disable_unprepare(ep->clk_res->clk);
> +
> + return ret;
> +}
> +
> +static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
> +{
> + clk_disable_unprepare(ep->clk_res->bus_clk);
> + clk_disable_unprepare(ep->clk_res->clk);
> +}
> +
> +static const struct exynos_pcie_ops exynos5440_pcie_ops = {
> + .get_mem_resources = exynos5440_pcie_get_mem_resources,
> + .get_clk_resources = exynos5440_pcie_get_clk_resources,
> + .init_clk_resources = exynos5440_pcie_init_clk_resources,
> + .deinit_clk_resources = exynos5440_pcie_deinit_clk_resources,
> +};
> +
> static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
> {
> writel(val, base + reg);
> @@ -116,155 +223,155 @@ static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
> if (on)
> val |= PCIE_ELBI_SLV_DBI_ENABLE;
> else
> val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
> }
>
> static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
> if (on)
> val |= PCIE_ELBI_SLV_DBI_ENABLE;
> else
> val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
> }
>
> static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
> val &= ~PCIE_CORE_RESET_ENABLE;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
> - exynos_pcie_writel(ep->elbi_base, 0, PCIE_PWR_RESET);
> - exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
> - exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
> }
>
> static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
> val |= PCIE_CORE_RESET_ENABLE;
>
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
> - exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
> - exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
> - exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
> - exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
> - exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_MAC_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
> }
>
> static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
> {
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_MAC_RESET);
> - exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_GLOBAL_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
> }
>
> static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
> {
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_GLOBAL_RESET);
> - exynos_pcie_writel(ep->elbi_base, 1, PCIE_PWR_RESET);
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_COMMON_RESET);
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_CMN_REG);
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSVREG_RESET);
> - exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSV_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
> + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
> + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
> }
>
> static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
> val &= ~PCIE_PHY_COMMON_PD_CMN;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
> val &= ~PCIE_PHY_TRSV0_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
> val &= ~PCIE_PHY_TRSV1_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
> val &= ~PCIE_PHY_TRSV2_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
> val &= ~PCIE_PHY_TRSV3_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
> }
>
> static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
> val |= PCIE_PHY_COMMON_PD_CMN;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
> val |= PCIE_PHY_TRSV0_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
> val |= PCIE_PHY_TRSV1_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
> val |= PCIE_PHY_TRSV2_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
>
> - val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
> + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
> val |= PCIE_PHY_TRSV3_PD_TSV;
> - exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
> + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
> }
>
> static void exynos_pcie_init_phy(struct exynos_pcie *ep)
> {
> /* DCC feedback control off */
> - exynos_pcie_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
>
> /* set TX/RX impedance */
> - exynos_pcie_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
>
> /* set 50Mhz PHY clock */
> - exynos_pcie_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
> - exynos_pcie_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
>
> /* set TX Differential output for lane 0 */
> - exynos_pcie_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
>
> /* set TX Pre-emphasis Level Control for lane 0 to minimum */
> - exynos_pcie_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
>
> /* set RX clock and data recovery bandwidth */
> - exynos_pcie_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
> - exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
> - exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
> - exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
> - exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
>
> /* change TX Pre-emphasis Level Control for lanes */
> - exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
> - exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
> - exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
> - exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
>
> /* set LVCC */
> - exynos_pcie_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
> - exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
> - exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
> - exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
> + exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
> }
>
> static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
> @@ -295,25 +402,27 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
> exynos_pcie_init_phy(exynos_pcie);
>
> /* pulse for common reset */
> - exynos_pcie_writel(exynos_pcie->block_base, 1, PCIE_PHY_COMMON_RESET);
> + exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
> + PCIE_PHY_COMMON_RESET);
> udelay(500);
> - exynos_pcie_writel(exynos_pcie->block_base, 0, PCIE_PHY_COMMON_RESET);
> + exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
> + PCIE_PHY_COMMON_RESET);
>
> exynos_pcie_deassert_core_reset(exynos_pcie);
> dw_pcie_setup_rc(pp);
> exynos_pcie_assert_reset(exynos_pcie);
>
> /* assert LTSSM enable */
> - exynos_pcie_writel(exynos_pcie->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
> - PCIE_APP_LTSSM_ENABLE);
> + exynos_pcie_writel(exynos_pcie->mem_res->elbi_base,
> + PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE);
>
> /* check if the link is up or not */
> if (!dw_pcie_wait_for_link(pp))
> return 0;
>
> - while (exynos_pcie_readl(exynos_pcie->phy_base,
> + while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
> PCIE_PHY_PLL_LOCKED) == 0) {
> - val = exynos_pcie_readl(exynos_pcie->block_base,
> + val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
> PCIE_PHY_PLL_LOCKED);
> dev_info(dev, "PLL Locked: 0x%x\n", val);
> }
> @@ -325,8 +434,8 @@ static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
> {
> u32 val;
>
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
> }
>
> static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
> @@ -336,7 +445,7 @@ static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
> /* enable INTX interrupt */
> val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
> IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
> }
>
> static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
> @@ -363,9 +472,9 @@ static void exynos_pcie_msi_init(struct exynos_pcie *ep)
> dw_pcie_msi_init(pp);
>
> /* enable MSI interrupt */
> - val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_EN_LEVEL);
> + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
> val |= IRQ_MSI_ENABLE;
> - exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_LEVEL);
> + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
> }
>
> static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
> @@ -425,7 +534,8 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
> struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
> u32 val;
>
> - val = exynos_pcie_readl(exynos_pcie->elbi_base, PCIE_ELBI_RDLH_LINKUP);
> + val = exynos_pcie_readl(exynos_pcie->mem_res->elbi_base,
> + PCIE_ELBI_RDLH_LINKUP);
> if (val == PCIE_ELBI_LTSSM_ENABLE)
> return 1;
>
> @@ -503,7 +613,6 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
> struct exynos_pcie *exynos_pcie;
> struct pcie_port *pp;
> struct device_node *np = dev->of_node;
> - struct resource *res;
> int ret;
>
> exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
> @@ -513,59 +622,37 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
> pp = &exynos_pcie->pp;
> pp->dev = dev;
>
> - exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
> -
> - exynos_pcie->clk = devm_clk_get(dev, "pcie");
> - if (IS_ERR(exynos_pcie->clk)) {
> - dev_err(dev, "Failed to get pcie rc clock\n");
> - return PTR_ERR(exynos_pcie->clk);
> - }
> - ret = clk_prepare_enable(exynos_pcie->clk);
> - if (ret)
> - return ret;
> + exynos_pcie->ops = (const struct exynos_pcie_ops *)
> + of_device_get_match_data(dev);
>
> - exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
> - if (IS_ERR(exynos_pcie->bus_clk)) {
> - dev_err(dev, "Failed to get pcie bus clock\n");
> - ret = PTR_ERR(exynos_pcie->bus_clk);
> - goto fail_clk;
> - }
> - ret = clk_prepare_enable(exynos_pcie->bus_clk);
> - if (ret)
> - goto fail_clk;
> + exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
>
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - exynos_pcie->elbi_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(exynos_pcie->elbi_base)) {
> - ret = PTR_ERR(exynos_pcie->elbi_base);
> - goto fail_bus_clk;
> + if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
> + ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
> + if (ret)
> + return ret;
> }
>
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> - exynos_pcie->phy_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(exynos_pcie->phy_base)) {
> - ret = PTR_ERR(exynos_pcie->phy_base);
> - goto fail_bus_clk;
> - }
> + if (exynos_pcie->ops && exynos_pcie->ops->get_clk_resources) {
> + ret = exynos_pcie->ops->get_clk_resources(exynos_pcie);
> + if (ret)
> + return ret;
>
> - res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> - exynos_pcie->block_base = devm_ioremap_resource(dev, res);
> - if (IS_ERR(exynos_pcie->block_base)) {
> - ret = PTR_ERR(exynos_pcie->block_base);
> - goto fail_bus_clk;
> + ret = exynos_pcie->ops->init_clk_resources(exynos_pcie);
> + if (ret)
> + return ret;
> }
>
> ret = exynos_add_pcie_port(exynos_pcie, pdev);
> if (ret < 0)
> - goto fail_bus_clk;
> + goto fail_probe;
>
> platform_set_drvdata(pdev, exynos_pcie);
> return 0;
>
> -fail_bus_clk:
> - clk_disable_unprepare(exynos_pcie->bus_clk);
> -fail_clk:
> - clk_disable_unprepare(exynos_pcie->clk);
> +fail_probe:
> + if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
> + exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
> return ret;
> }
>
> @@ -573,14 +660,17 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev)
> {
> struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
>
> - clk_disable_unprepare(exynos_pcie->bus_clk);
> - clk_disable_unprepare(exynos_pcie->clk);
> + if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
> + exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
>
> return 0;
> }
>
> static const struct of_device_id exynos_pcie_of_match[] = {
> - { .compatible = "samsung,exynos5440-pcie", },
> + {
> + .compatible = "samsung,exynos5440-pcie",
> + .data = &exynos5440_pcie_ops
> + },
> {},
> };
>
>
^ permalink raw reply
* [PATCHv2 0/5] Support for Marvell switches with integrated CPUs
From: Chris Packham @ 2017-01-05 4:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <6c6795d3-06ab-a4f0-78ad-5ecc78c1aa2f@gmail.com>
On 05/01/17 17:07, Florian Fainelli wrote:
> Le 01/04/17 ? 19:36, Chris Packham a ?crit :
>> The 98DX3236, 98DX3336 and 98DX4251 are a set of switch ASICs with
>> integrated CPUs. They CPU block is common within these product lines and
>> (as far as I can tell/have been told) is based on the Armada XP. There
>> are a few differences due to the fact they have to squeeze the CPU into
>> the same package as the switch.
>
> It's really great to see these changes, do you have a plan to also add
> support for the integrated switch using a DSA/switchdev driver?
I'd love to see a switchdev driver but it's a huge task (and no I'm not
committing to writing it). As it stands Marvell ship a switch SDK
largely executes in userspace with a small kernel module providing some
linkage to the underlying hardware.
We (a few of us here at Allied Telesis NZ) have discussed switchdev and
how we get from using Marvell's SDK in our products to using switchdev
proper.
The first step would probably be some kind of trampoline driver which
communicates with a userspace helper to do the actual work.
Alternatively there is some support in Marvell's SDK for compilation as
a binary blob so a proprietary kernel module is another option. Neither
of these are particularly nice in a free software world.
A full "free" implementation would be a large undertaking. Ideally I'd
like to see Marvell involved with producing one but so far they've not
been interested whenever I've brought it up.
^ permalink raw reply
* [PATCHv2 4/5] arm: mvebu: Add device tree for 98DX3236 SoCs
From: Chris Packham @ 2017-01-05 4:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1ee850ee-f771-1aeb-6b96-dfbe3118f2fc@gmail.com>
On 05/01/17 17:06, Florian Fainelli wrote:
> Le 01/04/17 ? 19:36, Chris Packham a ?crit :
>> The Marvell 98DX3236, 98DX3336, 98DX4521 and variants are switch ASICs
>> with integrated CPUs. They are similar to the Armada XP SoCs but have
>> different I/O interfaces.
>>
>> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
>> ---
>> +
>> + switch {
>> + packet-processor at 0 {
>> + compatible = "marvell,prestera-98dx4521";
>> + };
>> + };
>
> This may be a bit premature if you are not providing a binding document
> for this sub-node, you might as well add it once you also add a
> corresponding driver (or if this will remain out of tree, at least
> submitting a separate binding document).
I also see that I made a typo 4521 should be 4251.
The driver that uses this is an out of tree one so I will add a binding
document for it for now.
It wouldn't he hard to whip up a simple uio based driver that provides
mmap access to the switch register space so I might look at that if I
get time. It would be useful for testing if nothing else.
>
> Also, if this node's unit address is 0, you would expect at least a reg
> property whose address cell is 0 to be present.
>
The reg property is in the base 98dx3236 dsti file. The 3 devices have
the same IO footprint but the switching functionality is quite different
between them hence the different compatible strings (all of which I need
to add binding documentation for). I guess I could make the 3236 entry
pp0: packet-processor at 0 {
compatible = "marvell,prestera-98dx3236";
reg = <0 0x4000000>;
...
}
Then the 3336 and 4251 could just override the compatible string
&pp0 {
compatible = "marvell,prestera-98dx4251";
};
Would that be clearer?
^ permalink raw reply
* [PATCHv2 2/5] arm: mvebu: support for SMP on 98DX3336 SoC
From: Chris Packham @ 2017-01-05 4:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5edb6751-d15b-6654-f4ae-adf38fafb8a4@gmail.com>
On 05/01/17 17:04, Florian Fainelli wrote:
> Le 01/04/17 ? 19:36, Chris Packham a ?crit :
>> Compared to the armada-xp the 98DX3336 uses different registers to set
>> the boot address for the secondary CPU so a new enable-method is needed.
>> This will only work if the machine definition doesn't define an overall
>> smp_ops because there is not currently a way of overriding this from the
>> device tree if it is set in the machine definition.
>
> Not sure I follow you here, in theory, each individual CPU could have a
> different enable-method property, and considering how you leverage
> existing DTS include files, you should be able to override the DTS with
> the appropriate enable-method, or do you have a different problem?
>
The problem is I can't override the enable method if it's set in the
machine definition. It's because the devicetree is looked up first then
the machine definition overrides it.
Heres an old thread that basically outlines the problem.
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-November/300371.html
I posted a few attempts to work around it but there never really was any
consensus reached.
> mv98dx3236_resume_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
>> +{
>> + WARN_ON(hw_cpu != 1);
>> +
>> + writel(0, mv98dx3236_resume_base + MV98DX3236_CPU_RESUME_CTRL_OFFSET);
>> + writel(virt_to_phys(boot_addr), mv98dx3236_resume_base +
>> + MV98DX3236_CPU_RESUME_ADDR_OFFSET);
>
> I just submitted a patch series that switches such users to
> __pa_symbol() instead of virt_to_phys() because this is a kernel image
> symbol. For now this will work as-is, but depending on which patch
> series makes it first, it may be a good idea to keep this on someone's
> TODO list (yours or mine). I do expect having to make a second pass of
> conversions anyway.
>
> [1]: https://lkml.org/lkml/2017/1/4/1101
OK I'll keep that in mind.
>
>> +}
>> +
>> +static int __init mv98dx3236_resume_init(void)
>> +{
>> + struct device_node *np;
>> + struct resource res;
>> + int ret = 0;
>> +
>> + np = of_find_matching_node(NULL, of_mv98dx3236_resume_table);
>> + if (!np)
>> + return 0;
>> +
>> + pr_info("Initializing 98DX3236 Resume\n");
>
> This can be probably be dropped, you should know fairly quickly if this
> succeeded or not.
Will do.
>
> Can't this function be implemented as a smp_ops::smp_init_cpus instead
> of having this initialization done at arch_initcall time?
>
I'll look into it. My initial reaction is no because I still need
armada_xp_smp_init_cpus().
>> +
>> + if (of_address_to_resource(np, 0, &res)) {
>> + pr_err("unable to get resource\n");
>> + ret = -ENOENT;
>> + goto out;
>> + }
>> +
>> + if (!request_mem_region(res.start, resource_size(&res),
>> + np->full_name)) {
>> + pr_err("unable to request region\n");
>> + ret = -EBUSY;
>> + goto out;
>> + }
>
> You should be able to use of_io_request_and_map() and here.
>
Will do.
^ permalink raw reply
* [PATCH v3 02/10] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Djtag dts bindings
From: Anurup M @ 2017-01-05 4:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103225623.ynhc7n267jpzu44z@rob-hp-laptop>
On Wednesday 04 January 2017 04:26 AM, Rob Herring wrote:
> On Mon, Jan 02, 2017 at 01:49:03AM -0500, Anurup M wrote:
>> From: Tan Xiaojun <tanxiaojun@huawei.com>
>>
>> Add Hisilicon HiP05/06/07 Djtag dts bindings for CPU and IO Die
>>
>> Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
>> Signed-off-by: Anurup M <anurup.m@huawei.com>
>> ---
>> .../devicetree/bindings/arm/hisilicon/djtag.txt | 41 ++++++++++++++++++++++
>> 1 file changed, 41 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt b/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> new file mode 100644
>> index 0000000..bbe8b45
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> @@ -0,0 +1,41 @@
>> +The Hisilicon Djtag is an independent component which connects with some other
>> +components in the SoC by Debug Bus. The djtag is available in CPU and IO dies
>> +in the chip. The djtag controls access to connecting modules of CPU and IO
>> +dies.
>> +The various connecting components in CPU die (like L3 cache, L3 cache PMU etc.)
>> +are accessed by djtag during real time debugging. In IO die there are connecting
>> +components like RSA. These components appear as devices attached to djtag bus.
>> +
>> +Hisilicon HiP05/06/07 djtag for CPU and IO die
>> +Required properties:
>> + - compatible : The value should be as follows
>> + (a) "hisilicon,hip05-djtag-v1" for CPU and IO die which use v1 hw in
>> + HiP05 chipset.
> You don't need to distinguish the CPU and IO blocks?
The CPU and IO djtag nodes will have different base address(in reg
property).
There is no difference in handling of CPU and IO dies in the djtag driver.
So there is currently no need to distinguish them.
>> + (b) "hisilicon,hip06-djtag-v1" for CPU die which use v1 hw in HiP06 chipset.
>> + (c) "hisilicon,hip06-djtag-v2" for IO die which use v2 hw in HiP06 chipset.
>> + (d) "hisilicon,hip07-djtag-v2" for CPU and IO die which use v2 hw in
>> + HiP07 chipset.
>> + - reg : Register address and size
>> + - hisi-scl-id : The Super Cluster ID for CPU or IO die
> Still needs a vendor prefix. i.e. hisilicon,scl-id
>
Ok. I shall modify it.
Thanks,
Anurup
>> +
>> +Example 1: Djtag for CPU die
>> +
>> + /* for Hisilicon HiP05 djtag for CPU Die */
>> + djtag0: djtag at 80010000 {
>> + compatible = "hisilicon,hip05-djtag-v1";
>> + reg = <0x0 0x80010000 0x0 0x10000>;
>> + hisi-scl-id = <0x02>;
>> +
>> + /* All connecting components will appear as child nodes */
>> + };
>> +
>> +Example 2: Djtag for IO die
>> +
>> + /* for Hisilicon HiP05 djtag for IO Die */
>> + djtag1: djtag at d0000000 {
>> + compatible = "hisilicon,hip05-djtag-v1";
>> + reg = <0x0 0xd0000000 0x0 0x10000>;
>> + hisi-scl-id = <0x01>;
>> +
>> + /* All connecting components will appear as child nodes */
>> + };
>> --
>> 2.1.4
>>
^ permalink raw reply
* [PATCH v3 03/10] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
From: Anurup M @ 2017-01-05 5:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103225914.w3drvtpvupnpfa4x@rob-hp-laptop>
On Wednesday 04 January 2017 04:29 AM, Rob Herring wrote:
> On Mon, Jan 02, 2017 at 01:49:21AM -0500, Anurup M wrote:
>> 1) Device tree bindings for Hisilicon SoC PMU.
>> 2) Add example for Hisilicon L3 cache and MN PMU.
>> 3) Add child nodes of L3C and MN in djtag bindings example.
>>
>> Signed-off-by: Anurup M <anurup.m@huawei.com>
>> Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
>> ---
>> .../devicetree/bindings/arm/hisilicon/djtag.txt | 25 ++++++
>> .../devicetree/bindings/arm/hisilicon/pmu.txt | 100 +++++++++++++++++++++
>> 2 files changed, 125 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt b/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> index bbe8b45..653fdb7 100644
>> --- a/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> @@ -27,6 +27,31 @@ Example 1: Djtag for CPU die
>> hisi-scl-id = <0x02>;
>>
>> /* All connecting components will appear as child nodes */
>> +
>> + pmul3c0 {
>> + compatible = "hisilicon,hip05-pmu-l3c-v1";
>> + hisi-module-id = <0x04 0x02>;
>> + };
>> +
>> + pmul3c1 {
>> + compatible = "hisilicon,hip05-pmu-l3c-v1";
>> + hisi-module-id = <0x04 0x04>;
>> + };
>> +
>> + pmul3c2 {
>> + compatible = "hisilicon,hip05-pmu-l3c-v1";
>> + hisi-module-id = <0x04 0x01>;
>> + };
>> +
>> + pmul3c3 {
>> + compatible = "hisilicon,hip05-pmu-l3c-v1";
>> + hisi-module-id = <0x04 0x08>;
>> + };
>> +
>> + pmumn0 {
>> + compatible = "hisilicon,hip05-pmu-mn-v1";
>> + hisi-module-id = <0x0b>;
>> + };
>> };
>>
>> Example 2: Djtag for IO die
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> new file mode 100644
>> index 0000000..fceef8d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> @@ -0,0 +1,100 @@
>> +Hisilicon SoC HiP05/06/07 ARMv8 PMU
>> +===================================
>> +
>> +The Hisilicon SoC chips like HiP05/06/07 etc. consist of various independent
>> +system device PMUs such as L3 cache (L3C) and Miscellaneous Nodes(MN). These
>> +PMU devices are independent and have hardware logic to gather statistics and
>> +performance information.
>> +
>> +HiSilicon SoC chip is encapsulated by multiple CPU and IO dies. The CPU die
>> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
>> +in HiP05/06/07 chips are further grouped as CPU clusters (CCL) which includes
>> +4 cpu-cores each.
>> +e.g. In the case of HiP05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
>> +The L3 cache is further grouped as 4 L3 cache banks in a SCCL.
>> +
>> +The Hisilicon SoC PMU DT node bindings for uncore PMU devices are as below.
>> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
>> +the parent node will be the djtag node of the corresponding CPU die (SCCL).
>> +
>> +L3 cache
>> +---------
>> +The L3 cache is dedicated for each SCCL. Each SCCL in HiP05/06/07 chips have 4
>> +L3 cache banks. Each L3 cache bank have separate DT nodes.
>> +
>> +Required properties:
>> +
>> + - compatible : This value should be as follows
>> + (a) "hisilicon,hip05-pmu-l3c-v1" for v1 hw in HiP05 chipset
>> + (b) "hisilicon,hip06-pmu-l3c-v1" for v1 hw in HiP06 chipset
>> + (c) "hisilicon,hip07-pmu-l3c-v2" for v2 hw in HiP07 chipset
>> +
>> + - hisi-module-id : This property is a combination of two values in the below order.
> Vendor prefix: hisilicon,module-id
Ok. I shall modify it.
>> + a) Module ID: The module identifier for djtag.
>> + b) Instance or Bank ID: This will identify the L3 cache bank
>> + or instance.
>> +
>> +Optional properties:
>> +
>> + - interrupt-parent : A phandle indicating which interrupt controller
>> + this PMU signals interrupts to.
>> +
>> + - interrupts : Interrupt line used by this L3 cache bank.
>> +
>> + *The counter overflow IRQ is not supported in v1 hardware (HiP05/06).
>> +
>> +Miscellaneous Node
>> +------------------
>> +The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
>> +for each SCCL.
>> +
>> +Required properties:
>> +
>> + - compatible : This value should be as follows
>> + (a) "hisilicon,hip05-pmu-mn-v1" for v1 hw in HiP05 chipset
>> + (b) "hisilicon,hip06-pmu-mn-v1" for v1 hw in HiP06 chipset
>> + (c) "hisilicon,hip07-pmu-mn-v2" for v2 hw in HiP07 chipset
>> +
>> + - hisi-module-id : Module ID to input for djtag.
> ditto
Ok. I shall modify it.
Thanks,
Anurup
>> +
>> +Optional properties:
>> +
>> + - interrupt-parent : A phandle indicating which interrupt controller
>> + this PMU signals interrupts to.
>> +
>> + - interrupts : Interrupt line used by this PMU.
>> +
>> + *The counter overflow IRQ is not supported in v1 hardware (HiP05/06).
>> +
>> +Example:
>> +
>> + djtag0: djtag at 80010000 {
>> + compatible = "hisilicon,hip05-djtag-v1";
>> + reg = <0x0 0x80010000 0x0 0x10000>;
>> + scl-id = <0x02>;
>> +
>> + pmul3c0 {
>> + compatible = "hisilicon,hip05-pmu-l3c-v1";
>> + hisi-module-id = <0x04 0x02>;
>> + };
>> +
>> + pmul3c1 {
>> + compatible = "hisilicon,hip05-pmu-l3c-v1";
>> + hisi-module-id = <0x04 0x04>;
>> + };
>> +
>> + pmul3c2 {
>> + compatible = "hisilicon,hip05-pmu-l3c-v1";
>> + hisi-module-id = <0x04 0x01>;
>> + };
>> +
>> + pmul3c3 {
>> + compatible = "hisilicon,hip05-pmu-l3c-v1";
>> + hisi-module-id = <0x04 0x08>;
>> + };
>> +
>> + pmumn0 {
>> + compatible = "hisilicon,hip05-pmu-mn-v1";
>> + hisi-module-id = <0x0b>;
>> + };
>> + };
>> --
>> 2.1.4
>>
^ permalink raw reply
* [RFC PATCH] usb: dwc3: add support for OTG driver compilation
From: Manish Narani @ 2017-01-05 5:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87r34jvtn5.fsf@linux.intel.com>
Hi Felipe,
> From: Felipe Balbi [mailto:balbi at kernel.org]
> Sent: Wednesday, January 04, 2017 7:01 PM
> Hi,
>
> Manish Narani <manish.narani@xilinx.com> writes:
> > This patch adds support for OTG driver compilation and object file
> > creation
> >
> > Signed-off-by: Manish Narani <mnarani@xilinx.com>
> > ---
> > drivers/usb/dwc3/Makefile | 4 ++++
> > 1 file changed, 4 insertions(+)
> >
> > diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
> > index ffca340..2d269ad 100644
> > --- a/drivers/usb/dwc3/Makefile
> > +++ b/drivers/usb/dwc3/Makefile
> > @@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET)
> $(CONFIG_USB_DWC3_DUAL_ROLE)),)
> > dwc3-y += gadget.o ep0.o
> > endif
> >
> > +ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
> > + dwc3-y += otg.o
> > +endif
>
> you just broke compilation
Should I add new USB_DWC3_OTG macro in Kconfig?
Thanks,
Manish
This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
^ permalink raw reply
* [PATCH v6 0/4] arm64: arch_timer: Add workaround for hisilicon-161601 erratum
From: Ding Tianhong @ 2017-01-05 5:31 UTC (permalink / raw)
To: linux-arm-kernel
Erratum Hisilicon-161601 says that the ARM generic timer counter "has the
potential to contain an erroneous value when the timer value changes".
Accesses to TVAL (both read and write) are also affected due to the implicit counter
read. Accesses to CVAL are not affected.
The workaround is to reread the system count registers until the value of the second
read is larger than the first one by less than 32, the system counter can be guaranteed
not to return wrong value twice by back-to-back read and the error value is always larger
than the correct one by 32. Writes to TVAL are replaced with an equivalent write to CVAL.
v2: Introducing a new generic erratum handling mechanism for fsl,a008585 and hisilicon,161601.
Significant rework based on feedback, including seperate the fsl erratum a008585
to another patch, update the erratum name and remove unwanted code.
v3: Introducing the erratum_workaround_set_sne generic function for fsl erratum a008585
and make the #define __fsl_a008585_read_reg to be private to the .c file instead of
being globally visible. After discussion with Marc and Will, a consensus decision was
made to remove the commandline parameter for enabling fsl,erratum-a008585 erratum,
and make some generic name more specific, export timer_unstable_counter_workaround
for module access.
Significant rework based on feedback, including fix some alignment problem, make the
#define __hisi_161601_read_reg to be private to the .c file instead of being globally
visible, add more accurate annotation and modify a bit of logical format to enable
arch_timer_read_ool_enabled, remove the kernel commandline parameter
clocksource.arm_arch_timer.hisilicon-161601.
Introduce a generic aquick framework for erratum in ACPI mode.
v4: rename the quirk handler parameter to make it more generic, and
avoid break loop when handling the quirk becasue it need to
support multi quirks handler.
update some data structures for acpi mode.
v5: Adapt the new kernel-parameters.txt for latest kernel version.
Set the retries of reread system counter to 50, because it is possible
that some interrupts may lead to more than twice read errors and break the loop,
it will trigger the warning, so we set the number of retries far beyond the number of
iterations the loop has been observed to take.
v6: The last 2 patches in the previous version about the ACPI mode will conflict witch Fuwei's
GTDT patches, so remove the ACPI part and only support the DT base code for this patch set.
We have trigger a bug when select the CONFIG_FUNCTION_GRAPH_TRACER and enable function_graph
to /sys/kernel/debug/tracing/current_tracer, the system will stall into an endless loop, it looks
like that the ftrace_graph_caller will be related to xxx.read_cntvct_el0 and read the system counter
again, so mark the xxx.read_cntvct_el0 with notrace to fix the problem.
Ding Tianhong (4):
arm64: arch_timer: Add device tree binding for hisilicon-161601
erratum
arm64: arch_timer: Introduce a generic erratum handing mechanism for
fsl-a008585
arm64: arch_timer: Work around Erratum Hisilicon-161601
arm64: arch timer: Add timer erratum property for Hip05-d02 and
Hip06-d03
Documentation/admin-guide/kernel-parameters.txt | 9 --
Documentation/arm64/silicon-errata.txt | 1 +
.../devicetree/bindings/arm/arch_timer.txt | 8 ++
arch/arm64/boot/dts/hisilicon/hip05.dtsi | 1 +
arch/arm64/boot/dts/hisilicon/hip06.dtsi | 1 +
arch/arm64/include/asm/arch_timer.h | 38 ++----
drivers/clocksource/Kconfig | 9 ++
drivers/clocksource/arm_arch_timer.c | 146 ++++++++++++++++-----
8 files changed, 143 insertions(+), 70 deletions(-)
--
1.9.0
^ permalink raw reply
* [PATCH v6 1/4] arm64: arch_timer: Add device tree binding for hisilicon-161601 erratum
From: Ding Tianhong @ 2017-01-05 5:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483594317-10732-1-git-send-email-dingtianhong@huawei.com>
This erratum describes a bug in logic outside the core, so MIDR can't be
used to identify its presence, and reading an SoC-specific revision
register from common arch timer code would be awkward. So, describe it
in the device tree.
v2: Use the new erratum name and update the description.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/arm/arch_timer.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index ad440a2..935f142 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -31,6 +31,14 @@ to deliver its interrupts via SPIs.
This also affects writes to the tval register, due to the implicit
counter read.
+- hisilicon,erratum-161601 : A boolean property. Indicates the presence of
+ erratum 161601, which says that reading the counter is unreliable unless
+ reading twice on the register and the value of the second read is larger
+ than the first by less than 32. If the verification is unsuccessful, then
+ discard the value of this read and repeat this procedure until the verification
+ is successful. This also affects writes to the tval register, due to the
+ implicit counter read.
+
** Optional properties:
- arm,cpu-registers-not-fw-configured : Firmware does not initialize
--
1.9.0
^ permalink raw reply related
* [PATCH v6 2/4] arm64: arch_timer: Introduce a generic erratum handing mechanism for fsl-a008585
From: Ding Tianhong @ 2017-01-05 5:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483594317-10732-1-git-send-email-dingtianhong@huawei.com>
The workaround for hisilicon,161601 will check the return value of the system counter
by different way, in order to distinguish with the fsl-a008585 workaround, introduce
a new generic erratum handing mechanism for fsl-a008585 and rename some functions.
v2: Introducing a new generic erratum handling mechanism for fsl erratum a008585.
v3: Introducing the erratum_workaround_set_sne generic function for fsl erratum a008585
and make the #define __fsl_a008585_read_reg to be private to the .c file instead of
being globally visible. After discussion with Marc and Will, a consensus decision was
made to remove the commandline parameter for enabling fsl,erratum-a008585 erratum,
and make some generic name more specific, export timer_unstable_counter_workaround
for module access.
v5: Adapt the new documentation for kernel-parameters.txt.
v6: Mark the fsl_a008585_read_xxx_el0 with notrace, if CONFIG_FUNCTION_GRAPH_TRACER selected,
fsl_a008585_read_xxx_el0 will be related to ftrace_graph_caller which will calling sched_clock
to read system counter again, it will cause the system stall into an endless loop.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
Documentation/admin-guide/kernel-parameters.txt | 9 ---
arch/arm64/include/asm/arch_timer.h | 36 ++++--------
drivers/clocksource/arm_arch_timer.c | 78 +++++++++++++++----------
3 files changed, 58 insertions(+), 65 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 21e2d88..76437ad 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -539,15 +539,6 @@
loops can be debugged more effectively on production
systems.
- clocksource.arm_arch_timer.fsl-a008585=
- [ARM64]
- Format: <bool>
- Enable/disable the workaround of Freescale/NXP
- erratum A-008585. This can be useful for KVM
- guests, if the guest device tree doesn't show the
- erratum. If unspecified, the workaround is
- enabled based on the device tree.
-
clearcpuid=BITNUM [X86]
Disable CPUID feature X for the kernel. See
arch/x86/include/asm/cpufeatures.h for the valid bit
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index eaa5bbe..f882c7c 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -31,39 +31,27 @@
#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
extern struct static_key_false arch_timer_read_ool_enabled;
-#define needs_fsl_a008585_workaround() \
+#define needs_unstable_timer_counter_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
#else
-#define needs_fsl_a008585_workaround() false
+#define needs_unstable_timer_counter_workaround() false
#endif
-u32 __fsl_a008585_read_cntp_tval_el0(void);
-u32 __fsl_a008585_read_cntv_tval_el0(void);
-u64 __fsl_a008585_read_cntvct_el0(void);
-/*
- * The number of retries is an arbitrary value well beyond the highest number
- * of iterations the loop has been observed to take.
- */
-#define __fsl_a008585_read_reg(reg) ({ \
- u64 _old, _new; \
- int _retries = 200; \
- \
- do { \
- _old = read_sysreg(reg); \
- _new = read_sysreg(reg); \
- _retries--; \
- } while (unlikely(_old != _new) && _retries); \
- \
- WARN_ON_ONCE(!_retries); \
- _new; \
-})
+struct arch_timer_erratum_workaround {
+ int erratum; /* Indicate the Erratum ID */
+ u32 (*read_cntp_tval_el0)(void);
+ u32 (*read_cntv_tval_el0)(void);
+ u64 (*read_cntvct_el0)(void);
+};
+
+extern struct arch_timer_erratum_workaround *timer_unstable_counter_workaround;
#define arch_timer_reg_read_stable(reg) \
({ \
u64 _val; \
- if (needs_fsl_a008585_workaround()) \
- _val = __fsl_a008585_read_##reg(); \
+ if (needs_unstable_timer_counter_workaround()) \
+ _val = timer_unstable_counter_workaround->read_##reg();\
else \
_val = read_sysreg(reg); \
_val; \
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 02fef68..f9097b6 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -96,40 +96,53 @@ static int __init early_evtstrm_cfg(char *buf)
*/
#ifdef CONFIG_FSL_ERRATUM_A008585
-DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
-EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
-
-static int fsl_a008585_enable = -1;
+struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
+EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
-static int __init early_fsl_a008585_cfg(char *buf)
-{
- int ret;
- bool val;
+#define FSL_A008585 0x0001
- ret = strtobool(buf, &val);
- if (ret)
- return ret;
-
- fsl_a008585_enable = val;
- return 0;
-}
-early_param("clocksource.arm_arch_timer.fsl-a008585", early_fsl_a008585_cfg);
+DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
+EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
-u32 __fsl_a008585_read_cntp_tval_el0(void)
+/*
+ * The number of retries is an arbitrary value well beyond the highest number
+ * of iterations the loop has been observed to take.
+ */
+#define __fsl_a008585_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 200; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely(_old != _new) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+static u32 notrace fsl_a008585_read_cntp_tval_el0(void)
{
return __fsl_a008585_read_reg(cntp_tval_el0);
}
-u32 __fsl_a008585_read_cntv_tval_el0(void)
+static u32 notrace fsl_a008585_read_cntv_tval_el0(void)
{
return __fsl_a008585_read_reg(cntv_tval_el0);
}
-u64 __fsl_a008585_read_cntvct_el0(void)
+static u64 notrace fsl_a008585_read_cntvct_el0(void)
{
return __fsl_a008585_read_reg(cntvct_el0);
}
-EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0);
+
+static struct arch_timer_erratum_workaround arch_timer_fsl_a008585 = {
+ .erratum = FSL_A008585,
+ .read_cntp_tval_el0 = fsl_a008585_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = fsl_a008585_read_cntv_tval_el0,
+ .read_cntvct_el0 = fsl_a008585_read_cntvct_el0,
+};
#endif /* CONFIG_FSL_ERRATUM_A008585 */
static __always_inline
@@ -282,7 +295,7 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
}
#ifdef CONFIG_FSL_ERRATUM_A008585
-static __always_inline void fsl_a008585_set_next_event(const int access,
+static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk)
{
unsigned long ctrl;
@@ -300,17 +313,17 @@ static __always_inline void fsl_a008585_set_next_event(const int access,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-static int fsl_a008585_set_next_event_virt(unsigned long evt,
+static int erratum_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_VIRT_ACCESS, evt, clk);
return 0;
}
-static int fsl_a008585_set_next_event_phys(unsigned long evt,
+static int erratum_set_next_event_phys(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
+ erratum_set_next_event_generic(ARCH_TIMER_PHYS_ACCESS, evt, clk);
return 0;
}
#endif /* CONFIG_FSL_ERRATUM_A008585 */
@@ -343,16 +356,16 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
return 0;
}
-static void fsl_a008585_set_sne(struct clock_event_device *clk)
+static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
#ifdef CONFIG_FSL_ERRATUM_A008585
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
if (arch_timer_uses_ppi == VIRT_PPI)
- clk->set_next_event = fsl_a008585_set_next_event_virt;
+ clk->set_next_event = erratum_set_next_event_virt;
else
- clk->set_next_event = fsl_a008585_set_next_event_phys;
+ clk->set_next_event = erratum_set_next_event_phys;
#endif
}
@@ -385,7 +398,7 @@ static void __arch_timer_setup(unsigned type,
BUG();
}
- fsl_a008585_set_sne(clk);
+ erratum_workaround_set_sne(clk);
} else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer";
@@ -894,9 +907,10 @@ static int __init arch_timer_of_init(struct device_node *np)
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
#ifdef CONFIG_FSL_ERRATUM_A008585
- if (fsl_a008585_enable < 0)
- fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585");
- if (fsl_a008585_enable) {
+ if (!timer_unstable_counter_workaround && of_property_read_bool(np, "fsl,erratum-a008585"))
+ timer_unstable_counter_workaround = &arch_timer_fsl_a008585;
+
+ if (timer_unstable_counter_workaround) {
static_branch_enable(&arch_timer_read_ool_enabled);
pr_info("Enabling workaround for FSL erratum A-008585\n");
}
--
1.9.0
^ permalink raw reply related
* [PATCH v6 3/4] arm64: arch_timer: Work around Erratum Hisilicon-161601
From: Ding Tianhong @ 2017-01-05 5:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483594317-10732-1-git-send-email-dingtianhong@huawei.com>
Erratum Hisilicon-161601 says that the ARM generic timer counter "has the
potential to contain an erroneous value when the timer value changes".
Accesses to TVAL (both read and write) are also affected due to the implicit counter
read. Accesses to CVAL are not affected.
The workaround is to reread the system count registers until the value of the second
read is larger than the first one by less than 32, the system counter can be guaranteed
not to return wrong value twice by back-to-back read and the error value is always larger
than the correct one by 32. Writes to TVAL are replaced with an equivalent write to CVAL.
The workaround is enabled if the hisilicon,erratum-161601 property is found in
the timer node in the device tree. This can be overridden with the
clocksource.arm_arch_timer.hisilicon-161601 boot parameter, which allows KVM
users to enable the workaround until a mechanism is implemented to
automatically communicate this information.
Fix some description for fsl erratum a008585.
v2: Significant rework based on feedback, including seperate the fsl erratum a008585
to another patch, update the erratum name and remove unwanted code.
v3: Significant rework based on feedback, including fix some alignment problem, make the
#define __hisi_161601_read_reg to be private to the .c file instead of being globally
visible, add more accurate annotation and modify a bit of logical format to enable
arch_timer_read_ool_enabled, remove the kernel commandline parameter
clocksource.arm_arch_timer.hisilicon-161601.
v5: Theoretically the erratum should not occur more than twice in succession when reading
the system counter, but it is possible that some interrupts may lead to more than twice
read errors, triggering the warning, so setting the number of retries to 50 which is far
beyond the number of iterations the loop has been observed to take.
v6: Mark the hisi_161601_read_xxx_el0 with notrace, if CONFIG_FUNCTION_GRAPH_TRACER selected,
hisi_161601_read_xxx_el0 will be related to ftrace_graph_caller which will calling sched_clock
to read system counter again, it will cause the system stall into an endless loop.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
Documentation/arm64/silicon-errata.txt | 1 +
arch/arm64/include/asm/arch_timer.h | 2 +-
drivers/clocksource/Kconfig | 9 +++++
drivers/clocksource/arm_arch_timer.c | 70 +++++++++++++++++++++++++++++++---
4 files changed, 76 insertions(+), 6 deletions(-)
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
index 405da11..1c1a95f 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -63,3 +63,4 @@ stable kernels.
| Cavium | ThunderX SMMUv2 | #27704 | N/A |
| | | | |
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
+| Hisilicon | Hip0{5,6,7} | #161601 | HISILICON_ERRATUM_161601|
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index f882c7c..ebf4cde 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -29,7 +29,7 @@
#include <clocksource/arm_arch_timer.h>
-#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
extern struct static_key_false arch_timer_read_ool_enabled;
#define needs_unstable_timer_counter_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 4866f7a..162d820 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -335,6 +335,15 @@ config FSL_ERRATUM_A008585
value"). The workaround will only be active if the
fsl,erratum-a008585 property is found in the timer node.
+config HISILICON_ERRATUM_161601
+ bool "Workaround for Hisilicon Erratum 161601"
+ default y
+ depends on ARM_ARCH_TIMER && ARM64
+ help
+ This option enables a workaround for Hisilicon Erratum
+ 161601. The workaround will be active if the hisilicon,erratum-161601
+ property is found in the timer node.
+
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select CLKSRC_OF if OF
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index f9097b6..078d38f 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -95,15 +95,18 @@ static int __init early_evtstrm_cfg(char *buf)
* Architected system timer support.
*/
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if CONFIG_FSL_ERRATUM_A008585 || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
#define FSL_A008585 0x0001
+#define HISILICON_161601 0x0002
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
+#endif
+#ifdef CONFIG_FSL_ERRATUM_A008585
/*
* The number of retries is an arbitrary value well beyond the highest number
* of iterations the loop has been observed to take.
@@ -145,6 +148,54 @@ static u64 notrace fsl_a008585_read_cntvct_el0(void)
};
#endif /* CONFIG_FSL_ERRATUM_A008585 */
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+/*
+ * Verify whether the value of the second read is larger than the first by
+ * less than 32 is the only way to confirm the value is correct, so clear the
+ * lower 5 bits to check whether the difference is greater than 32 or not.
+ * Theoretically the erratum should not occur more than twice in succession
+ * when reading the system counter, but it is possible that some interrupts
+ * may lead to more than twice read errors, triggering the warning, so setting
+ * the number of retries far beyond the number of iterations the loop has been
+ * observed to take.
+ */
+#define __hisi_161601_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 50; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely((_new - _old) >> 5) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+static u32 notrace hisi_161601_read_cntp_tval_el0(void)
+{
+ return __hisi_161601_read_reg(cntp_tval_el0);
+}
+
+static u32 notrace hisi_161601_read_cntv_tval_el0(void)
+{
+ return __hisi_161601_read_reg(cntv_tval_el0);
+}
+
+static u64 notrace hisi_161601_read_cntvct_el0(void)
+{
+ return __hisi_161601_read_reg(cntvct_el0);
+}
+
+static struct arch_timer_erratum_workaround arch_timer_hisi_161601 = {
+ .erratum = HISILICON_161601,
+ .read_cntp_tval_el0 = hisi_161601_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = hisi_161601_read_cntv_tval_el0,
+ .read_cntvct_el0 = hisi_161601_read_cntvct_el0,
+};
+#endif /* CONFIG_HISILICON_ERRATUM_161601 */
+
static __always_inline
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
struct clock_event_device *clk)
@@ -294,7 +345,7 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
static __always_inline void erratum_set_next_event_generic(const int access,
unsigned long evt, struct clock_event_device *clk)
{
@@ -358,7 +409,7 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
static void erratum_workaround_set_sne(struct clock_event_device *clk)
{
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
@@ -618,7 +669,7 @@ static void __init arch_counter_register(unsigned type)
clocksource_counter.archdata.vdso_direct = true;
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
/*
* Don't use the vdso fastpath if errata require using
* the out-of-line counter accessor.
@@ -909,10 +960,19 @@ static int __init arch_timer_of_init(struct device_node *np)
#ifdef CONFIG_FSL_ERRATUM_A008585
if (!timer_unstable_counter_workaround && of_property_read_bool(np, "fsl,erratum-a008585"))
timer_unstable_counter_workaround = &arch_timer_fsl_a008585;
+#endif
+
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+ if (!timer_unstable_counter_workaround && of_property_read_bool(np, "hisilicon,erratum-161601"))
+ timer_unstable_counter_workaround = &arch_timer_hisi_161601;
+#endif
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161601)
if (timer_unstable_counter_workaround) {
static_branch_enable(&arch_timer_read_ool_enabled);
- pr_info("Enabling workaround for FSL erratum A-008585\n");
+ pr_info("Enabling workaround for %s\n",
+ timer_unstable_counter_workaround->erratum == FSL_A008585 ?
+ "FSL ERRATUM A-008585" : "HISILICON ERRATUM 161601");
}
#endif
--
1.9.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox