2016-03-03 11:53:26

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 00/17] Add Initial support for PLX Technology OX810SE

This serie adds initial support (IRQ, Timer, GPIO, Reset, Serial, Clocks) for
the PLX Technology OX810SE used in the well-known Western Digital My Book
World Edition Network Attached Storage device.

Extended support for SATA, DMA and Ethernet will come in further patches.

Upstream support for following devices like the OX820SE is welcome !

Neil Armstrong (17):
dt-bindings: vendor-prefixes: Add PLX Technology
irqchip: Add PLX Technology RPS IRQ Controller
dt-bindings: Add PLX Technology RPS IRQ Controller bindings
clocksource: Add PLX Technology RPS Timer
dt-bindings: Add PLX Technology RPS Timer bindings
reset: Add PLX Technology Reset Controller driver
dt-bindings: Add PLX Technology Reset Controller bindings
clk: Add PLX Technology OXNAS Standard Clocks
dt-bindings: Add PLX Technology OXNAS Standard Clocks bindings
pinctrl: Add PLX Technology OXNAS pinctrl and gpio driver
dt-bindings: Add PLX Technology OXNAS pinctrl and gpio bindings
arm: Add new mach-oxnas
arm: Add build support for mach-oxnas
arm: boot: dts: Add PLX Technology OX810SE dtsi
dt-bindings: Add OXNAS bindings
dt-bindings: Add Western Digital to vendor prefixes
arm: boot: dts: Add Western Digital My Book World Edition device tree

Documentation/devicetree/bindings/arm/oxnas.txt | 9 +
.../devicetree/bindings/clock/plxtech,stdclk.txt | 24 +
.../devicetree/bindings/gpio/gpio_oxnas.txt | 27 +
.../interrupt-controller/plxtech,rps-irq.txt | 17 +
.../bindings/pinctrl/plxtech,pinctrl.txt | 100 ++
.../devicetree/bindings/reset/plxtech,reset.txt | 25 +
.../bindings/timer/plxtech,rps-timer.txt | 17 +
.../devicetree/bindings/vendor-prefixes.txt | 2 +
arch/arm/Kconfig | 2 +
arch/arm/Makefile | 1 +
arch/arm/boot/dts/Makefile | 2 +
arch/arm/boot/dts/ox810se.dtsi | 279 ++++
arch/arm/boot/dts/wd-mbwe.dts | 106 ++
arch/arm/mach-oxnas/Kconfig | 24 +
arch/arm/mach-oxnas/Makefile | 1 +
arch/arm/mach-oxnas/oxnas.c | 34 +
drivers/clk/Kconfig | 6 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-oxnas.c | 159 +++
drivers/clocksource/Kconfig | 6 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-rps.c | 249 ++++
drivers/irqchip/Kconfig | 5 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-rps.c | 128 ++
drivers/pinctrl/Kconfig | 9 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-oxnas.c | 1393 ++++++++++++++++++++
drivers/reset/Kconfig | 4 +
drivers/reset/Makefile | 1 +
drivers/reset/reset-oxnas.c | 149 +++
31 files changed, 2783 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/oxnas.txt
create mode 100644 Documentation/devicetree/bindings/clock/plxtech,stdclk.txt
create mode 100644 Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/plxtech,rps-irq.txt
create mode 100644 Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt
create mode 100644 Documentation/devicetree/bindings/reset/plxtech,reset.txt
create mode 100644 Documentation/devicetree/bindings/timer/plxtech,rps-timer.txt
create mode 100644 arch/arm/boot/dts/ox810se.dtsi
create mode 100644 arch/arm/boot/dts/wd-mbwe.dts
create mode 100644 arch/arm/mach-oxnas/Kconfig
create mode 100644 arch/arm/mach-oxnas/Makefile
create mode 100644 arch/arm/mach-oxnas/oxnas.c
create mode 100644 drivers/clk/clk-oxnas.c
create mode 100644 drivers/clocksource/timer-rps.c
create mode 100644 drivers/irqchip/irq-rps.c
create mode 100644 drivers/pinctrl/pinctrl-oxnas.c
create mode 100644 drivers/reset/reset-oxnas.c

--
1.9.1


2016-03-03 11:40:34

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 01/17] dt-bindings: vendor-prefixes: Add PLX Technology

Add PLX Technology vendor prefix.

Signed-off-by: Neil Armstrong <[email protected]>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 72e2c5a..03970fb 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -178,6 +178,7 @@ picochip Picochip Ltd
plathome Plat'Home Co., Ltd.
plda PLDA
pixcir PIXCIR MICROELECTRONICS Co., Ltd
+plxtech PLX Technology, Inc.
pulsedlight PulsedLight, Inc
powervr PowerVR (deprecated, use img)
qca Qualcomm Atheros, Inc.
--
1.9.1

2016-03-03 11:40:40

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 04/17] clocksource: Add PLX Technology RPS Timer

Add clocksource and clockevent driver from dual RPS timer.

CC: Ma Haijun <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/clocksource/Kconfig | 6 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-rps.c | 249 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 256 insertions(+)
create mode 100644 drivers/clocksource/timer-rps.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 33db740..f79bc0f 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -276,6 +276,12 @@ config VF_PIT_TIMER
help
Support for Period Interrupt Timer on Freescale Vybrid Family SoCs.

+config CLKSRC_RPS_TIMER
+ bool
+ select CLKSRC_MMIO
+ help
+ This enables support for the PLX Tech OXNAS RPS timers.
+
config SYS_SUPPORTS_SH_CMT
bool

diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index dc2b899..120bc09 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
obj-$(CONFIG_MTK_TIMER) += mtk_timer.o
obj-$(CONFIG_CLKSRC_PISTACHIO) += time-pistachio.o
obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o
+obj-$(CONFIG_CLKSRC_RPS_TIMER) += timer-rps.o

obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
diff --git a/drivers/clocksource/timer-rps.c b/drivers/clocksource/timer-rps.c
new file mode 100644
index 0000000..79621b8
--- /dev/null
+++ b/drivers/clocksource/timer-rps.c
@@ -0,0 +1,249 @@
+/*
+ * drivers/clocksource/timer-rps.c
+ *
+ * Copyright (C) 2009 Oxford Semiconductor Ltd
+ * Copyright (C) 2013 Ma Haijun <[email protected]>
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/clockchips.h>
+#include <linux/clk.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/sched_clock.h>
+
+/* TIMER1 as tick
+ * TIMER2 as clocksource
+ */
+
+enum {
+ TIMER_LOAD = 0,
+ TIMER_CURR = 4,
+ TIMER_CTRL = 8,
+ TIMER_CLRINT = 0xC,
+
+ TIMER_BITS = 24,
+
+ TIMER_MAX_VAL = (1 << TIMER_BITS) - 1,
+
+ TIMER_PERIODIC = (1 << 6),
+ TIMER_ENABLE = (1 << 7),
+
+ TIMER_DIV1 = (0 << 2),
+ TIMER_DIV16 = (1 << 2),
+ TIMER_DIV256 = (2 << 2),
+
+ TIMER1_OFFSET = 0,
+ TIMER2_OFFSET = 0x20,
+};
+
+/* Clockevent */
+
+static unsigned long timer_period = HZ;
+static unsigned timer_prescaler = 1;
+static void __iomem *timer_base;
+
+static irqreturn_t rps_timer_irq(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+
+ iowrite32(0, timer_base + TIMER_CLRINT);
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static void rps_timer_config(unsigned long period, unsigned periodic)
+{
+ uint32_t cfg = 0;
+
+ if (period)
+ cfg |= TIMER_ENABLE;
+
+ if (periodic)
+ cfg |= TIMER_PERIODIC;
+
+ switch (timer_prescaler) {
+ case 1:
+ cfg |= TIMER_DIV1;
+ break;
+ case 16:
+ cfg |= TIMER_DIV16;
+ break;
+ case 256:
+ cfg |= TIMER_DIV256;
+ break;
+ }
+
+ iowrite32(period, timer_base + TIMER_LOAD);
+ iowrite32(cfg, timer_base + TIMER_CTRL);
+}
+
+static int rps_timer_shutdown(struct clock_event_device *evt)
+{
+ if (!clockevent_state_periodic(evt))
+ return 0;
+
+ rps_timer_config(0, 0);
+
+ return 0;
+}
+
+static int rps_timer_set_periodic(struct clock_event_device *evt)
+{
+ rps_timer_config(timer_period, 1);
+
+ return 0;
+}
+
+static int rps_timer_set_oneshot(struct clock_event_device *evt)
+{
+ rps_timer_config(timer_period, 0);
+
+ return 0;
+}
+
+static int rps_timer_next_event(unsigned long delta,
+ struct clock_event_device *evt)
+{
+ rps_timer_config(delta, 0);
+
+ return 0;
+}
+
+static struct clock_event_device rps_clockevent = {
+ .name = "rps",
+ .features = CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_ONESHOT,
+ .tick_resume = rps_timer_shutdown,
+ .set_state_shutdown = rps_timer_shutdown,
+ .set_state_periodic = rps_timer_set_periodic,
+ .set_state_oneshot = rps_timer_set_oneshot,
+ .set_next_event = rps_timer_next_event,
+ .rating = 200,
+};
+
+static void __init rps_clockevent_init(void __iomem *base, ulong ref_rate,
+ int irq)
+{
+ timer_base = base;
+
+ /* Start with prescaler 1 */
+ timer_prescaler = 1;
+ timer_period = DIV_ROUND_UP(ref_rate, HZ);
+
+ if (timer_period > TIMER_MAX_VAL) {
+ timer_prescaler = 16;
+ timer_period = DIV_ROUND_UP(ref_rate / timer_prescaler, HZ);
+ }
+ if (timer_period > TIMER_MAX_VAL) {
+ timer_prescaler = 256;
+ timer_period = DIV_ROUND_UP(ref_rate / timer_prescaler, HZ);
+ }
+
+ rps_clockevent.cpumask = cpu_possible_mask;
+ rps_clockevent.irq = irq;
+ clockevents_config_and_register(&rps_clockevent,
+ ref_rate / timer_prescaler,
+ 1,
+ TIMER_MAX_VAL);
+
+ pr_info("rps: Registered clock event rate %luHz prescaler %d period %lu\n",
+ ref_rate,
+ timer_prescaler,
+ timer_period);
+}
+
+/* Clocksource */
+
+static void __iomem *timer_curr;
+
+static u64 notrace rps_read_sched_clock(void)
+{
+ return ~readl_relaxed(timer_curr);
+}
+
+static void __init rps_clocksource_init(void __iomem *base, ulong ref_rate)
+{
+ int ret;
+ ulong clock_rate;
+ /* use prescale 16 */
+ clock_rate = ref_rate / 16;
+
+ iowrite32(TIMER_MAX_VAL, base + TIMER_LOAD);
+ iowrite32(TIMER_PERIODIC | TIMER_ENABLE | TIMER_DIV16,
+ base + TIMER_CTRL);
+
+ timer_curr = base + TIMER_CURR;
+ sched_clock_register(rps_read_sched_clock, TIMER_BITS, clock_rate);
+ ret = clocksource_mmio_init(base + TIMER_CURR, "rps_clocksource_timer",
+ clock_rate, 250, TIMER_BITS,
+ clocksource_mmio_readl_down);
+ if (ret)
+ panic("can't register clocksource\n");
+
+ pr_info("rps: Registered clocksource rate %luHz\n", clock_rate);
+}
+
+static struct irqaction rps_timer_irqaction = {
+ .name = "rps_timer",
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = rps_timer_irq,
+ .dev_id = &rps_clockevent,
+};
+
+static void __init rps_timer_init(struct device_node *np)
+{
+ struct clk *refclk;
+ unsigned long ref_rate;
+ void __iomem *base;
+ int irq, ret;
+
+ refclk = of_clk_get(np, 0);
+
+ if (IS_ERR(refclk) || clk_prepare_enable(refclk))
+ panic("rps_timer_init: failed to get refclk\n");
+ ref_rate = clk_get_rate(refclk);
+
+ base = of_iomap(np, 0);
+ if (!base)
+ panic("rps_timer_init: failed to map io\n");
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq < 0)
+ panic("rps_timer_init: failed to parse IRQ\n");
+
+ /* Disable timers */
+ iowrite32(0, base + TIMER1_OFFSET + TIMER_CTRL);
+ iowrite32(0, base + TIMER2_OFFSET + TIMER_CTRL);
+ iowrite32(0, base + TIMER1_OFFSET + TIMER_LOAD);
+ iowrite32(0, base + TIMER2_OFFSET + TIMER_LOAD);
+ iowrite32(0, base + TIMER1_OFFSET + TIMER_CLRINT);
+ iowrite32(0, base + TIMER2_OFFSET + TIMER_CLRINT);
+
+ rps_clocksource_init(base + TIMER2_OFFSET, ref_rate);
+ rps_clockevent_init(base + TIMER1_OFFSET, ref_rate, irq);
+
+ ret = setup_irq(irq, &rps_timer_irqaction);
+ if (ret)
+ panic("rps_timer_init: failed to request irq\n");
+}
+
+CLOCKSOURCE_OF_DECLARE(nas782x, "plxtech,nas782x-rps-timer", rps_timer_init);
--
1.9.1

2016-03-03 11:40:50

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 12/17] arm: Add new mach-oxnas

Add mach-oxnas directory containing Kconfig and generic oxnas
handling for future SoC specific features like system reset.

Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm/mach-oxnas/Kconfig | 24 ++++++++++++++++++++++++
arch/arm/mach-oxnas/Makefile | 1 +
arch/arm/mach-oxnas/oxnas.c | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 59 insertions(+)
create mode 100644 arch/arm/mach-oxnas/Kconfig
create mode 100644 arch/arm/mach-oxnas/Makefile
create mode 100644 arch/arm/mach-oxnas/oxnas.c

diff --git a/arch/arm/mach-oxnas/Kconfig b/arch/arm/mach-oxnas/Kconfig
new file mode 100644
index 0000000..6a1f24c
--- /dev/null
+++ b/arch/arm/mach-oxnas/Kconfig
@@ -0,0 +1,24 @@
+menuconfig ARCH_OXNAS
+ bool "PLX Technology OXNAS Family SoCs"
+ select ARCH_REQUIRE_GPIOLIB
+ select ARCH_HAS_RESET_CONTROLLER
+ select PINCTRL
+ depends on ARCH_MULTI_V5
+ help
+ Support for OxNas SoC family developed by PLX Technology.
+ (Formely Oxford Semiconductor)
+
+if ARCH_OXNAS
+
+config MACH_OX810SE
+ bool "Support OX810SE Based Products"
+ select CPU_ARM926T
+ select PLXTECH_RPS
+ select CLKSRC_RPS_TIMER
+ select RESET_OXNAS
+ select COMMON_CLK_OXNAS
+ select PINCTRL_OXNAS
+ help
+ Include Support for the PLX Technology OX810SE SoC Based Products.
+
+endif
diff --git a/arch/arm/mach-oxnas/Makefile b/arch/arm/mach-oxnas/Makefile
new file mode 100644
index 0000000..c54bec7
--- /dev/null
+++ b/arch/arm/mach-oxnas/Makefile
@@ -0,0 +1 @@
+obj-y := oxnas.o
diff --git a/arch/arm/mach-oxnas/oxnas.c b/arch/arm/mach-oxnas/oxnas.c
new file mode 100644
index 0000000..ebdcd9d
--- /dev/null
+++ b/arch/arm/mach-oxnas/oxnas.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/mach/arch.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+static const char * const oxnas_dt_compat[] __initconst = {
+ "plxtech,ox810se",
+ NULL,
+};
+
+static void __init oxnas_init(void)
+{
+ pr_info("OXNAS Device Tree boot\n");
+
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+DT_MACHINE_START(OXNAS, "PLX Technology OXNAS Family")
+ .dt_compat = oxnas_dt_compat,
+ .init_machine = oxnas_init,
+MACHINE_END
--
1.9.1

2016-03-03 11:40:43

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 03/17] dt-bindings: Add PLX Technology RPS IRQ Controller bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
.../bindings/interrupt-controller/plxtech,rps-irq.txt | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/plxtech,rps-irq.txt

diff --git a/Documentation/devicetree/bindings/interrupt-controller/plxtech,rps-irq.txt b/Documentation/devicetree/bindings/interrupt-controller/plxtech,rps-irq.txt
new file mode 100644
index 0000000..db117a0
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/plxtech,rps-irq.txt
@@ -0,0 +1,17 @@
+PLX Technology OXNAS SoCs Family RPS Interrupt Controller
+=========================================================
+
+Required properties:
+- compatible: Should be "plxtech,nas782x-rps"
+- reg : Specifies base physical address and size of the registers.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Should be 1
+
+example:
+
+intc: interrupt-controller@0 {
+ compatible = "plxtech,nas782x-rps";
+ interrupt-controller;
+ reg = <0 0x200>;
+ #interrupt-cells = <1>;
+};
--
1.9.1

2016-03-03 11:41:13

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 13/17] arm: Add build support for mach-oxnas

Add Kconfig and Makefile support for mach-oxnas.

Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm/Kconfig | 2 ++
arch/arm/Makefile | 1 +
2 files changed, 3 insertions(+)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4f799e5..2025fc9 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -801,6 +801,8 @@ source "arch/arm/plat-pxa/Kconfig"

source "arch/arm/mach-mmp/Kconfig"

+source "arch/arm/mach-oxnas/Kconfig"
+
source "arch/arm/mach-qcom/Kconfig"

source "arch/arm/mach-realview/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index fe25410..0e96c63 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -194,6 +194,7 @@ machine-$(CONFIG_ARCH_NSPIRE) += nspire
machine-$(CONFIG_ARCH_OMAP1) += omap1
machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
machine-$(CONFIG_ARCH_ORION5X) += orion5x
+machine-$(CONFIG_ARCH_OXNAS) += oxnas
machine-$(CONFIG_ARCH_PICOXCELL) += picoxcell
machine-$(CONFIG_ARCH_PXA) += pxa
machine-$(CONFIG_ARCH_QCOM) += qcom
--
1.9.1

2016-03-03 11:41:11

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 14/17] arm: boot: dts: Add PLX Technology OX810SE dtsi

Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm/boot/dts/ox810se.dtsi | 279 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 279 insertions(+)
create mode 100644 arch/arm/boot/dts/ox810se.dtsi

diff --git a/arch/arm/boot/dts/ox810se.dtsi b/arch/arm/boot/dts/ox810se.dtsi
new file mode 100644
index 0000000..a86d7f0
--- /dev/null
+++ b/arch/arm/boot/dts/ox810se.dtsi
@@ -0,0 +1,279 @@
+/*
+ * ox810se.dtsi - Device tree file for PLX Technology OX810SE SoC
+ *
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ compatible = "plxtech,ox810se";
+
+ cpus {
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ device_type = "cpu";
+ compatible = "arm,arm926ej-s";
+ clocks = <&armclk>;
+ };
+ };
+
+ memory {
+ /* Max 256MB @ 0x48000000 */
+ reg = <0x48000000 0x10000000>;
+ };
+
+ aliases {
+ serial0 = &uart0;
+ serial1 = &uart1;
+ serial2 = &uart2;
+ serial3 = &uart3;
+ gpio0 = &gpio0;
+ gpio1 = &gpio1;
+ };
+
+ clocks {
+ osc: oscillator {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ };
+
+ gmacclk: gmacclk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <125000000>;
+ };
+
+ rpsclk: rspclk {
+ compatible = "fixed-factor-clock";
+ #clock-cells = <0>;
+ clock-div = <1>;
+ clock-mult = <1>;
+ clocks = <&osc>;
+ };
+
+ pll400: pll400 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <733333333>;
+ };
+
+ sysclk: sysclk {
+ compatible = "fixed-factor-clock";
+ #clock-cells = <0>;
+ clock-div = <4>;
+ clock-mult = <1>;
+ clocks = <&pll400>;
+ };
+
+ armclk: armclk {
+ compatible = "fixed-factor-clock";
+ #clock-cells = <0>;
+ clock-div = <2>;
+ clock-mult = <1>;
+ clocks = <&pll400>;
+ };
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges;
+ interrupt-parent = <&intc>;
+
+ apb-bridge@44000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges = <0 0x44000000 0x1000000>;
+
+ pinctrl: pinctrl {
+ compatible = "plxtech,nas782x-pinctrl", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ /* Regmap for sys registers */
+ plxtech,sys-ctrl = <&sys>;
+
+ /* Default, all-open mux-map */
+ plxtech,mux-mask = <
+ 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
+ 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
+ >;
+
+ gpio0: gpio@000000 {
+ compatible = "plxtech,nas782x-gpio";
+ reg = <0x000000 0x100000>;
+ interrupts = <21>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #gpio-lines = <32>;
+ };
+
+ gpio1: gpio@100000 {
+ compatible = "plxtech,nas782x-gpio";
+ reg = <0x100000 0x100000>;
+ interrupts = <22>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #gpio-lines = <3>;
+ };
+
+ uart0 {
+ pinctrl_uart0: uart0 {
+ plxtech,pins = <0 31 3 0
+ 0 32 3 0>;
+ };
+ pinctrl_uart0_modem: uart0_modem {
+ plxtech,pins = <0 27 3 0
+ 0 28 3 0
+ 0 29 3 0
+ 0 30 3 0
+ 0 33 3 0
+ 0 34 3 0>;
+ };
+ };
+
+ uart1 {
+ pinctrl_uart1: uart1 {
+ plxtech,pins = <0 20 3 0
+ 0 22 3 0>;
+ };
+ pinctrl_uart1_modem: uart1_modem {
+ plxtech,pins = <0 8 3 0
+ 0 9 3 0
+ 0 23 3 0
+ 0 24 3 0
+ 0 25 3 0
+ 0 26 3 0>;
+ };
+ };
+
+ uart2 {
+ pinctrl_uart2: uart2 {
+ plxtech,pins = <0 6 3 0
+ 0 7 3 0>;
+ };
+ pinctrl_uart2_modem: uart2_modem {
+ plxtech,pins = <0 0 3 0
+ 0 1 3 0
+ 0 2 3 0
+ 0 3 3 0
+ 0 4 3 0
+ 0 5 3 0>;
+ };
+ };
+ };
+
+ uart0: uart@200000 {
+ compatible = "ns16550a";
+ reg = <0x200000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <23>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 17>;
+ };
+
+ uart1: uart@300000 {
+ compatible = "ns16550a";
+ reg = <0x300000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <24>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 18>;
+ };
+
+ uart2: uart@900000 {
+ compatible = "ns16550a";
+ reg = <0x900000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <29>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 22>;
+ };
+
+ uart3: uart@a00000 {
+ compatible = "ns16550a";
+ reg = <0xa00000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <30>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 23>;
+ };
+ };
+
+ apb-bridge@45000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges = <0 0x45000000 0x1000000>;
+
+ sys: sys-ctrl@000000 {
+ compatible = "plxtech,ox810se-sys-ctrl", "syscon", "simple-mfd";
+ reg = <0x000000 0x100000>;
+
+ reset: reset-controller {
+ compatible = "plxtech,nas782x-reset";
+ #reset-cells = <1>;
+ };
+
+ stdclk: stdclk {
+ compatible = "plxtech,ox810se-stdclk", "plxtech,nas782x-stdclk";
+ #clock-cells = <1>;
+ };
+ };
+
+ rps@300000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges = <0 0x300000 0x100000>;
+
+ intc: interrupt-controller@0 {
+ compatible = "plxtech,nas782x-rps";
+ interrupt-controller;
+ reg = <0 0x200>;
+ #interrupt-cells = <1>;
+ };
+
+ timer0: timer@200 {
+ compatible = "plxtech,nas782x-rps-timer";
+ reg = <0x200 0x40>;
+ clocks = <&rpsclk>;
+ interrupts = <4 5>;
+ };
+ };
+ };
+ };
+};
--
1.9.1

2016-03-03 11:41:09

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 15/17] dt-bindings: Add OXNAS bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
Documentation/devicetree/bindings/arm/oxnas.txt | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/oxnas.txt

diff --git a/Documentation/devicetree/bindings/arm/oxnas.txt b/Documentation/devicetree/bindings/arm/oxnas.txt
new file mode 100644
index 0000000..6e17ca4
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/oxnas.txt
@@ -0,0 +1,9 @@
+PLX Technology OXNAS SoCs Family device tree bindings
+-------------------------------------------
+
+Boards with the OX810SE Soc SoC shall have the following properties:
+ Required root node property:
+ compatible: "plxtech,ox810se"
+
+Board compatible values:
+ - "wd,mbwe" (OX810SE)
--
1.9.1

2016-03-03 11:41:07

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 16/17] dt-bindings: Add Western Digital to vendor prefixes

Signed-off-by: Neil Armstrong <[email protected]>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 03970fb..4055608 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -248,6 +248,7 @@ via VIA Technologies, Inc.
virtio Virtual I/O Device Specification, developed by the OASIS consortium
vivante Vivante Corporation
voipac Voipac Technologies s.r.o.
+wd Western Digital Corp.
wexler Wexler
winbond Winbond Electronics corp.
wlf Wolfson Microelectronics
--
1.9.1

2016-03-03 11:41:05

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 17/17] arm: boot: dts: Add Western Digital My Book World Edition device tree

Add Western Digital My Book World Edition device tree based on
PLX Technology OX810SE SoC.

Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm/boot/dts/Makefile | 2 +
arch/arm/boot/dts/wd-mbwe.dts | 106 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+)
create mode 100644 arch/arm/boot/dts/wd-mbwe.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index a4a6d70..0395674 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -520,6 +520,8 @@ dtb-$(CONFIG_ARCH_ORION5X) += \
orion5x-rd88f5182-nas.dtb
dtb-$(CONFIG_ARCH_PRIMA2) += \
prima2-evb.dtb
+dtb-$(CONFIG_ARCH_OXNAS) += \
+ wd-mbwe.dtb
dtb-$(CONFIG_ARCH_QCOM) += \
qcom-apq8064-cm-qs600.dtb \
qcom-apq8064-ifc6410.dtb \
diff --git a/arch/arm/boot/dts/wd-mbwe.dts b/arch/arm/boot/dts/wd-mbwe.dts
new file mode 100644
index 0000000..ad97e2f
--- /dev/null
+++ b/arch/arm/boot/dts/wd-mbwe.dts
@@ -0,0 +1,106 @@
+/*
+ * wd-mbwe.dtsi - Device tree file for Western Digital My Book World Edition
+ *
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/dts-v1/;
+#include "ox810se.dtsi"
+
+/ {
+ model = "Western Digital My Book World Edition";
+
+ compatible = "wd,mbwe", "plxtech,ox810se";
+
+ chosen {
+ bootargs = "console=ttyS1,115200n8 earlyprintk=serial";
+ };
+
+ memory {
+ /* 128Mbytes DDR */
+ reg = <0x48000000 0x8000000>;
+ };
+
+ gpio-keys-polled {
+ compatible = "gpio-keys-polled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ poll-interval = <100>;
+
+ power {
+ label = "power";
+ gpios = <&gpio0 0 1>;
+ linux,code = <0x198>;
+ };
+
+ recovery {
+ label = "recovery";
+ gpios = <&gpio0 4 1>;
+ linux,code = <0xab>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ a0 {
+ label = "activity0";
+ gpios = <&gpio0 25 0>;
+ default-state = "keep";
+ };
+
+ a1 {
+ label = "activity1";
+ gpios = <&gpio0 26 0>;
+ default-state = "keep";
+ };
+
+ a2 {
+ label = "activity2";
+ gpios = <&gpio0 5 0>;
+ default-state = "keep";
+ };
+
+ a3 {
+ label = "activity3";
+ gpios = <&gpio0 6 0>;
+ default-state = "keep";
+ };
+
+ a4 {
+ label = "activity4";
+ gpios = <&gpio0 7 0>;
+ default-state = "keep";
+ };
+
+ a5 {
+ label = "activity5";
+ gpios = <&gpio1 2 0>;
+ default-state = "keep";
+ };
+ };
+
+ i2c-gpio {
+ compatible = "i2c-gpio";
+ gpios = <&gpio0 3 0 /* sda */
+ &gpio0 2 0 /* scl */
+ >;
+ i2c-gpio,delay-us = <2>; /* ~100 kHz */
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc0: rtc@48 {
+ compatible = "st,m41t00";
+ reg = <0x68>;
+ };
+ };
+};
+
+&uart1 {
+ status = "okay";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+};
--
1.9.1

2016-03-03 11:49:27

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 11/17] dt-bindings: Add PLX Technology OXNAS pinctrl and gpio bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
.../devicetree/bindings/gpio/gpio_oxnas.txt | 27 ++++++
.../bindings/pinctrl/plxtech,pinctrl.txt | 100 +++++++++++++++++++++
2 files changed, 127 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
create mode 100644 Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt

diff --git a/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt b/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
new file mode 100644
index 0000000..0ef6c55
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
@@ -0,0 +1,27 @@
+PLX Technology OXNAS SoC GPIO Controller
+==========================================
+
+Required properties:
+- compatible: "plxtech,nas782x-gpio".
+- reg: Should contain GPIO controller registers location and length
+- interrupts: Should be the port interrupt shared by all the pins.
+- #gpio-cells: Should be two. The first cell is the pin number and
+ the second cell is used to specify optional parameters (currently
+ unused).
+- gpio-controller: Marks the device node as a GPIO controller.
+
+optional properties:
+- #gpio-lines: Number of gpio if absent 32.
+
+
+Example:
+ gpio0: gpio@000000 {
+ compatible = "plxtech,nas782x-gpio";
+ reg = <0x000000 0x100000>;
+ interrupts = <21>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #gpio-lines = <32>;
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt
new file mode 100644
index 0000000..30f013f
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt
@@ -0,0 +1,100 @@
+PLX Technology OXNAS SoC Pinmux Controller
+==========================================
+
+The OXNAS Pinmux Controller, enables the IC to share one PAD to several
+functional blocks. The sharing is done by multiplexing the PAD input/output
+signals. For each PAD there are up to 8 muxing options (called periph modes).
+Since different modules require different PAD settings
+(like pull up, keeper, etc) the contoller controls also the PAD settings
+parameters.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+OXNAS pin configuration node is a node of a group of pins which can be
+used for a specific device or function. This node represents both mux and config
+of the pins in that group. The 'pins' selects the function mode(also named pin
+mode) this pin can work on and the 'config' configures various pad settings
+such as pull-up, multi drive, etc.
+
+Required properties for iomux controller:
+- compatible: "plxtech,nas782x-pinctrl" or "plxtech,ox810se-pinctrl"
+- plxtech,mux-mask: array of mask (periph per bank) to describe if a pin can be
+ configured in this periph mode. All the periph and bank need to be describe.
+- plxtech,sys-ctrl: a phandle to the system controller syscon node
+
+How to create such array:
+
+Each column will represent the possible peripheral of the pinctrl
+Each line will represent a pio bank
+
+For example :
+Peripheral: 2 ( A and B)
+Bank: 2 (A, B and C)
+=>
+
+ /* A B */
+ 0xffffffff 0xffc00c3b /* pioA */
+ 0xffffffff 0x7fff3ccf /* pioB */
+
+For each peripheral/bank we will descibe in a u32 if a pin can be
+configured in it by putting 1 to the pin bit (1 << pin)
+
+Required properties for pin configuration node:
+- plxtech,pins: 4 integers array, represents a group of pins mux and config
+ setting. The format is plxtech,pins = <PIN_BANK PIN_BANK_NUM PERIPH CONFIG>.
+ The PERIPH 0 means gpio, PERIPH 1 is periph A, PERIPH 2 is periph B...
+ PIN_BANK 0 is pioA, PIN_BANK 1 is pioB...
+
+Bits used for CONFIG:
+ - None Yet
+
+Examples:
+
+pinctrl: pinctrl {
+ compatible = "plxtech,nas782x-pinctrl", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ /* Regmap for sys registers */
+ plxtech,sys-ctrl = <&sys>;
+
+ /* Default, all-open mux-map */
+ plxtech,mux-mask = <
+ 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
+ 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
+ >;
+
+ uart0 {
+ pinctrl_uart0: uart0 {
+ plxtech,pins = <0 31 3 0
+ 0 32 3 0>;
+ };
+ pinctrl_uart0_modem: uart0_modem {
+ plxtech,pins = <0 27 3 0
+ 0 28 3 0
+ 0 29 3 0
+ 0 30 3 0
+ 0 33 3 0
+ 0 34 3 0>;
+ };
+ };
+};
+
+uart0: uart@200000 {
+ compatible = "ns16550a";
+ reg = <0x200000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <23>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 17>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart0>;
+};
--
1.9.1

2016-03-03 11:49:30

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 10/17] pinctrl: Add PLX Technology OXNAS pinctrl and gpio driver

Add pinctrl and gprio control support to PLX Technology OXNAS SoC Family

CC: Ma Haijun <[email protected]>
CC: Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/pinctrl/Kconfig | 9 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-oxnas.c | 1393 +++++++++++++++++++++++++++++++++++++++
3 files changed, 1403 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-oxnas.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 99a4c10..1d0c513 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -129,6 +129,15 @@ config PINCTRL_MESON
select OF_GPIO
select REGMAP_MMIO

+config PINCTRL_OXNAS
+ bool
+ depends on OF
+ select PINMUX
+ select PINCONF
+ select GPIOLIB
+ select OF_GPIO
+ select MFD_SYSCON
+
config PINCTRL_ROCKCHIP
bool
select PINMUX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index bf1b5ca..3351d10 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_PINCTRL_AMD) += pinctrl-amd.o
obj-$(CONFIG_PINCTRL_DIGICOLOR) += pinctrl-digicolor.o
obj-$(CONFIG_PINCTRL_FALCON) += pinctrl-falcon.o
obj-$(CONFIG_PINCTRL_MESON) += meson/
+obj-$(CONFIG_PINCTRL_OXNAS) += pinctrl-oxnas.o
obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o
obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
diff --git a/drivers/pinctrl/pinctrl-oxnas.c b/drivers/pinctrl/pinctrl-oxnas.c
new file mode 100644
index 0000000..5dfd3a9
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-oxnas.c
@@ -0,0 +1,1393 @@
+/*
+ * PLX Technology OXNAS Pinctrl driver based on at91 pinctrl driver
+ *
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ * Copyright (C) 2013 Ma Hajun <[email protected]>
+ * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+/* Since we request GPIOs from ourself */
+#include <linux/pinctrl/consumer.h>
+#include <linux/version.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "core.h"
+
+#define MAX_NB_GPIO_PER_BANK 32
+#define MAX_GPIO_BANKS 2
+
+struct oxnas_gpio_chip {
+ struct gpio_chip chip;
+ struct pinctrl_gpio_range range;
+ void __iomem *regbase; /* GPIOA/B virtual address */
+ struct irq_domain *domain; /* associated irq domain */
+ struct regmap *regmap;
+};
+
+#define to_oxnas_gpio_chip(c) container_of(c, struct oxnas_gpio_chip, chip)
+
+static struct oxnas_gpio_chip *gpio_chips[MAX_GPIO_BANKS];
+
+static int gpio_banks;
+
+/**
+ * struct oxnas_pmx_func - describes pinmux functions
+ * @name: the name of this specific function
+ * @groups: corresponding pin groups
+ * @ngroups: the number of groups
+ */
+struct oxnas_pmx_func {
+ const char *name;
+ const char **groups;
+ unsigned ngroups;
+};
+
+enum oxnas_mux {
+ OXNAS_PINMUX_GPIO,
+ OXNAS_PINMUX_FUNC1,
+ OXNAS_PINMUX_FUNC2,
+ OXNAS_PINMUX_FUNC3,
+};
+
+enum {
+ INPUT_VALUE = 0,
+ OUTPUT_ENABLE = 4,
+ IRQ_PENDING = 0xC,
+ OUTPUT_VALUE = 0x10,
+ OUTPUT_SET = 0x14,
+ OUTPUT_CLEAR = 0x18,
+ OUTPUT_EN_SET = 0x1C,
+ OUTPUT_EN_CLEAR = 0x20,
+ RE_IRQ_ENABLE = 0x28, /* rising edge */
+ FE_IRQ_ENABLE = 0x2C, /* falling edge */
+ RE_IRQ_PENDING = 0x30, /* rising edge */
+ FE_IRQ_PENDING = 0x34, /* falling edge */
+};
+
+enum {
+ PINMUX_PRIMARY_SEL0 = 0x0c,
+ PINMUX_PRIMARY_SEL1 = 0x10,
+ PINMUX_SECONDARY_SEL0 = 0x14,
+ PINMUX_SECONDARY_SEL1 = 0x18,
+ PINMUX_TERTIARY_SEL0 = 0x8c,
+ PINMUX_TERTIARY_SEL1 = 0x90,
+};
+
+/**
+ * struct oxnas_pmx_pin - describes an pin mux
+ * @bank: the bank of the pin
+ * @pin: the pin number in the @bank
+ * @mux: the mux mode : gpio or periph_x of the pin i.e. alternate function.
+ * @conf: the configuration of the pin: PULL_UP, MULTIDRIVE etc...
+ */
+struct oxnas_pmx_pin {
+ uint32_t bank;
+ uint32_t pin;
+ enum oxnas_mux mux;
+ unsigned long conf;
+};
+
+/**
+ * struct oxnas_pin_group - describes an pin group
+ * @name: the name of this specific pin group
+ * @pins_conf: the mux mode for each pin in this group. The size of this
+ * array is the same as pins.
+ * @pins: an array of discrete physical pins used in this group, taken
+ * from the driver-local pin enumeration space
+ * @npins: the number of pins in this group array, i.e. the number of
+ * elements in .pins so we can iterate over that array
+ */
+struct oxnas_pin_group {
+ const char *name;
+ struct oxnas_pmx_pin *pins_conf;
+ unsigned int *pins;
+ unsigned npins;
+};
+
+struct oxnas_pinctrl {
+ struct device *dev;
+ struct pinctrl_dev *pctl;
+ struct regmap *regmap;
+
+ int nbanks;
+
+ uint32_t *mux_mask;
+ int nmux;
+
+ struct oxnas_pmx_func *functions;
+ int nfunctions;
+
+ struct oxnas_pin_group *groups;
+ int ngroups;
+};
+
+static const inline struct oxnas_pin_group *oxnas_pinctrl_find_group_by_name(
+ const struct oxnas_pinctrl *info,
+ const char *name)
+{
+ const struct oxnas_pin_group *grp = NULL;
+ int i;
+
+ for (i = 0; i < info->ngroups; i++) {
+ if (strcmp(info->groups[i].name, name))
+ continue;
+
+ grp = &info->groups[i];
+ dev_dbg(info->dev, "%s: %d 0:%d\n", name, grp->npins,
+ grp->pins[0]);
+ break;
+ }
+
+ return grp;
+}
+
+static int oxnas_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->ngroups;
+}
+
+static const char *oxnas_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->groups[selector].name;
+}
+
+static int oxnas_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
+ const unsigned **pins,
+ unsigned *npins)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ if (selector >= info->ngroups)
+ return -EINVAL;
+
+ *pins = info->groups[selector].pins;
+ *npins = info->groups[selector].npins;
+
+ return 0;
+}
+
+static void oxnas_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
+ unsigned offset)
+{
+ seq_printf(s, "%s", dev_name(pctldev->dev));
+}
+
+static int oxnas_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ const struct oxnas_pin_group *grp;
+ struct pinctrl_map *new_map;
+ struct device_node *parent;
+ int map_num = 1;
+ int i;
+
+ /*
+ * first find the group of this node and check if we need create
+ * config maps for pins
+ */
+ grp = oxnas_pinctrl_find_group_by_name(info, np->name);
+ if (!grp) {
+ dev_err(info->dev, "unable to find group for node %s\n",
+ np->name);
+ return -EINVAL;
+ }
+
+ map_num += grp->npins;
+ new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num,
+ GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+
+ *map = new_map;
+ *num_maps = map_num;
+
+ /* create mux map */
+ parent = of_get_parent(np);
+ if (!parent) {
+ devm_kfree(pctldev->dev, new_map);
+ return -EINVAL;
+ }
+ new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
+ new_map[0].data.mux.function = parent->name;
+ new_map[0].data.mux.group = np->name;
+ of_node_put(parent);
+
+ /* create config map */
+ new_map++;
+ for (i = 0; i < grp->npins; i++) {
+ new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
+ new_map[i].data.configs.group_or_pin =
+ pin_get_name(pctldev, grp->pins[i]);
+ new_map[i].data.configs.configs = &grp->pins_conf[i].conf;
+ new_map[i].data.configs.num_configs = 1;
+ }
+
+ dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",
+ (*map)->data.mux.function, (*map)->data.mux.group, map_num);
+
+ return 0;
+}
+
+static void oxnas_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+}
+
+static const struct pinctrl_ops oxnas_pctrl_ops = {
+ .get_groups_count = oxnas_get_groups_count,
+ .get_group_name = oxnas_get_group_name,
+ .get_group_pins = oxnas_get_group_pins,
+ .pin_dbg_show = oxnas_pin_dbg_show,
+ .dt_node_to_map = oxnas_dt_node_to_map,
+ .dt_free_map = oxnas_dt_free_map,
+};
+
+static void __iomem *pin_to_gpioctrl(struct oxnas_pinctrl *info,
+ unsigned int bank)
+{
+ return gpio_chips[bank]->regbase;
+}
+
+static inline int pin_to_bank(unsigned pin)
+{
+ return pin / MAX_NB_GPIO_PER_BANK;
+}
+
+static unsigned pin_to_mask(unsigned int pin)
+{
+ return 1 << pin;
+}
+
+static void oxnas_mux_disable_interrupt(void __iomem *pio, unsigned mask)
+{
+ writel(readl(pio + RE_IRQ_ENABLE) & ~mask, pio + RE_IRQ_ENABLE);
+ writel(readl(pio + FE_IRQ_ENABLE) & ~mask, pio + FE_IRQ_ENABLE);
+}
+
+static void oxnas_mux_set_func1(struct oxnas_pinctrl *ctrl,
+ unsigned bank, unsigned mask)
+{
+ if (!bank) {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL0, mask, mask);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, 0);
+ } else {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL1, mask, mask);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, 0);
+ }
+}
+
+static void oxnas_mux_set_func2(struct oxnas_pinctrl *ctrl,
+ unsigned bank, unsigned mask)
+{
+ if (!bank) {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL0, mask, mask);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL0, mask, 0);
+ } else {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL1, mask, mask);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, 0);
+ }
+}
+
+static void oxnas_mux_set_func3(struct oxnas_pinctrl *ctrl,
+ unsigned bank, unsigned mask)
+{
+ if (!bank) {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL0, mask, mask);
+ } else {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, mask);
+ }
+}
+
+static void oxnas_mux_set_gpio(struct oxnas_pinctrl *ctrl,
+ unsigned bank, unsigned mask)
+{
+ if (!bank) {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL0, mask, 0);
+ } else {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, 0);
+ }
+}
+
+static enum oxnas_mux oxnas_mux_get_func(struct regmap *regmap,
+ unsigned bank, unsigned mask)
+{
+ unsigned int val;
+
+ if (!bank) {
+ if (!regmap_read(regmap, PINMUX_PRIMARY_SEL0, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC1;
+ if (!regmap_read(regmap, PINMUX_SECONDARY_SEL0, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC2;
+ if (!regmap_read(regmap, PINMUX_TERTIARY_SEL0, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC3;
+ } else {
+ if (!regmap_read(regmap, PINMUX_PRIMARY_SEL1, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC1;
+ if (!regmap_read(regmap, PINMUX_SECONDARY_SEL1, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC2;
+ if (!regmap_read(regmap, PINMUX_TERTIARY_SEL1, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC3;
+ }
+
+ return OXNAS_PINMUX_GPIO;
+}
+
+static void oxnas_pin_dbg(const struct device *dev,
+ const struct oxnas_pmx_pin *pin)
+{
+ if (pin->mux) {
+ dev_dbg(dev,
+ "MF_%c%d configured as periph%c with conf = %lu\n",
+ pin->bank + 'A', pin->pin, pin->mux - 1 + 'A',
+ pin->conf);
+ } else {
+ dev_dbg(dev, "MF_%c%d configured as gpio with conf = %lu\n",
+ pin->bank + 'A', pin->pin, pin->conf);
+ }
+}
+
+static int pin_check_config(struct oxnas_pinctrl *info, const char *name,
+ int index, const struct oxnas_pmx_pin *pin)
+{
+ int mux;
+
+ /* check if it's a valid config */
+ if (pin->bank >= info->nbanks) {
+ dev_err(info->dev, "%s: pin conf %d bank_id %d >= nbanks %d\n",
+ name, index, pin->bank, info->nbanks);
+ return -EINVAL;
+ }
+
+ if (pin->pin >= MAX_NB_GPIO_PER_BANK) {
+ dev_err(info->dev, "%s: pin conf %d pin_bank_id %d >= %d\n",
+ name, index, pin->pin, MAX_NB_GPIO_PER_BANK);
+ return -EINVAL;
+ }
+ /* gpio always allowed */
+ if (!pin->mux)
+ return 0;
+
+ mux = pin->mux - 1;
+
+ if (mux >= info->nmux) {
+ dev_err(info->dev, "%s: pin conf %d mux_id %d >= nmux %d\n",
+ name, index, mux, info->nmux);
+ return -EINVAL;
+ }
+
+ if (!(info->mux_mask[pin->bank * info->nmux + mux] & 1 << pin->pin)) {
+ dev_err(info->dev, "%s: pin conf %d mux_id %d not supported for MF_%c%d\n",
+ name, index, mux, pin->bank + 'A', pin->pin);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void oxnas_mux_gpio_enable(struct oxnas_pinctrl *ctrl,
+ int bank,
+ void __iomem *pio,
+ unsigned mask, bool input)
+{
+ oxnas_mux_set_gpio(ctrl, bank, mask);
+
+ if (input)
+ writel_relaxed(mask, pio + OUTPUT_EN_CLEAR);
+ else
+ writel_relaxed(mask, pio + OUTPUT_EN_SET);
+}
+
+static void oxnas_mux_gpio_disable(struct oxnas_pinctrl *ctrl,
+ int bank,
+ unsigned mask)
+{
+ /* when switch to other function, gpio is disabled automatically */
+}
+
+static int oxnas_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned group)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ const struct oxnas_pmx_pin *pins_conf = info->groups[group].pins_conf;
+ const struct oxnas_pmx_pin *pin;
+ uint32_t npins = info->groups[group].npins;
+ int i, ret;
+ unsigned mask;
+ void __iomem *pio;
+
+ dev_dbg(info->dev, "enable function %s group %s\n",
+ info->functions[selector].name, info->groups[group].name);
+
+ /* first check that all the pins of the group are valid with a valid
+ * parameter
+ */
+ for (i = 0; i < npins; i++) {
+ pin = &pins_conf[i];
+ ret = pin_check_config(info, info->groups[group].name, i, pin);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < npins; i++) {
+ pin = &pins_conf[i];
+ oxnas_pin_dbg(info->dev, pin);
+
+ pio = pin_to_gpioctrl(info, pin->bank);
+
+ mask = pin_to_mask(pin->pin);
+ oxnas_mux_disable_interrupt(pio, mask);
+
+ switch (pin->mux) {
+ case OXNAS_PINMUX_GPIO:
+ oxnas_mux_gpio_enable(info, pin->bank, pio, mask, 1);
+ break;
+ case OXNAS_PINMUX_FUNC1:
+ oxnas_mux_set_func1(info, pin->bank, mask);
+ break;
+ case OXNAS_PINMUX_FUNC2:
+ oxnas_mux_set_func2(info, pin->bank, mask);
+ break;
+ case OXNAS_PINMUX_FUNC3:
+ oxnas_mux_set_func3(info, pin->bank, mask);
+ break;
+ }
+ if (pin->mux)
+ oxnas_mux_gpio_disable(info, pin->bank, mask);
+ }
+
+ return 0;
+}
+
+static int oxnas_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->nfunctions;
+}
+
+static const char *oxnas_pmx_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->functions[selector].name;
+}
+
+static int oxnas_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = info->functions[selector].groups;
+ *num_groups = info->functions[selector].ngroups;
+
+ return 0;
+}
+
+static int oxnas_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+ struct oxnas_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
+ struct oxnas_gpio_chip *oxnas_chip;
+ struct gpio_chip *chip;
+ unsigned mask;
+
+ if (!range) {
+ dev_err(npct->dev, "invalid range\n");
+ return -EINVAL;
+ }
+ if (!range->gc) {
+ dev_err(npct->dev, "missing GPIO chip in range\n");
+ return -EINVAL;
+ }
+ chip = range->gc;
+ oxnas_chip = container_of(chip, struct oxnas_gpio_chip, chip);
+
+ dev_dbg(npct->dev, "enable pin %u as GPIO\n", offset);
+
+ mask = 1 << (offset - chip->base);
+
+ dev_dbg(npct->dev, "enable pin %u as MF_%c%d 0x%x\n",
+ offset, 'A' + range->id, offset - chip->base, mask);
+
+ oxnas_mux_set_gpio(npct, range->id, mask);
+
+ return 0;
+}
+
+static void oxnas_gpio_disable_free(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+ struct oxnas_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
+
+ dev_dbg(npct->dev, "disable pin %u as GPIO\n", offset);
+
+ /* Set the pin to some default state, GPIO is usually default */
+}
+
+static const struct pinmux_ops oxnas_pmx_ops = {
+ .get_functions_count = oxnas_pmx_get_funcs_count,
+ .get_function_name = oxnas_pmx_get_func_name,
+ .get_function_groups = oxnas_pmx_get_groups,
+ .set_mux = oxnas_pmx_set_mux,
+ .gpio_request_enable = oxnas_gpio_request_enable,
+ .gpio_disable_free = oxnas_gpio_disable_free,
+};
+
+static int oxnas_pinconf_get(struct pinctrl_dev *pctldev,
+ unsigned pin_id, unsigned long *config)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+
+static int oxnas_pinconf_set(struct pinctrl_dev *pctldev,
+ unsigned pin_id, unsigned long *configs,
+ unsigned num_configs)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+
+static void oxnas_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned pin_id)
+{
+
+}
+
+static void oxnas_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned group)
+{
+}
+
+static const struct pinconf_ops oxnas_pinconf_ops = {
+ .pin_config_get = oxnas_pinconf_get,
+ .pin_config_set = oxnas_pinconf_set,
+ .pin_config_dbg_show = oxnas_pinconf_dbg_show,
+ .pin_config_group_dbg_show = oxnas_pinconf_group_dbg_show,
+};
+
+static struct pinctrl_desc oxnas_pinctrl_desc = {
+ .pctlops = &oxnas_pctrl_ops,
+ .pmxops = &oxnas_pmx_ops,
+ .confops = &oxnas_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+static const char *gpio_compat = "plxtech,nas782x-gpio";
+
+static void oxnas_pinctrl_child_count(struct oxnas_pinctrl *info,
+ struct device_node *np)
+{
+ struct device_node *child;
+
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, gpio_compat)) {
+ info->nbanks++;
+ } else {
+ info->nfunctions++;
+ info->ngroups += of_get_child_count(child);
+ }
+ }
+}
+
+static int oxnas_pinctrl_mux_mask(struct oxnas_pinctrl *info,
+ struct device_node *np)
+{
+ int ret = 0;
+ int size;
+ const __be32 *list;
+
+ list = of_get_property(np, "plxtech,mux-mask", &size);
+ if (!list) {
+ dev_err(info->dev, "can not read the mux-mask of %d\n", size);
+ return -EINVAL;
+ }
+
+ size /= sizeof(*list);
+ if (!size || size % info->nbanks) {
+ dev_err(info->dev, "wrong mux mask array should be by %d\n",
+ info->nbanks);
+ return -EINVAL;
+ }
+ info->nmux = size / info->nbanks;
+
+ info->mux_mask = devm_kzalloc(info->dev, sizeof(u32) * size,
+ GFP_KERNEL);
+ if (!info->mux_mask)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "plxtech,mux-mask",
+ info->mux_mask, size);
+ if (ret)
+ dev_err(info->dev, "can not read the mux-mask of %d\n", size);
+ return ret;
+}
+
+static int oxnas_pinctrl_parse_groups(struct device_node *np,
+ struct oxnas_pin_group *grp,
+ struct oxnas_pinctrl *info, u32 index)
+{
+ struct oxnas_pmx_pin *pin;
+ int size;
+ const __be32 *list;
+ int i, j;
+
+ dev_dbg(info->dev, "group(%d): %s\n", index, np->name);
+
+ /* Initialise group */
+ grp->name = np->name;
+
+ /*
+ * the binding format is plxtech,pins = <bank pin mux CONFIG ...>,
+ * do sanity check and calculate pins number
+ */
+ list = of_get_property(np, "plxtech,pins", &size);
+ /* we do not check return since it's safe node passed down */
+ size /= sizeof(*list);
+ if (!size || size % 4) {
+ dev_err(info->dev, "wrong pins number or pins and configs should be divisible by 4\n");
+ return -EINVAL;
+ }
+
+ grp->npins = size / 4;
+ pin = grp->pins_conf = devm_kzalloc(info->dev,
+ grp->npins * sizeof(struct oxnas_pmx_pin),
+ GFP_KERNEL);
+ grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!grp->pins_conf || !grp->pins)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < size; i += 4, j++) {
+ pin->bank = be32_to_cpu(*list++);
+ pin->pin = be32_to_cpu(*list++);
+ grp->pins[j] = pin->bank * MAX_NB_GPIO_PER_BANK + pin->pin;
+ pin->mux = be32_to_cpu(*list++);
+ pin->conf = be32_to_cpu(*list++);
+
+ oxnas_pin_dbg(info->dev, pin);
+ pin++;
+ }
+
+ return 0;
+}
+
+static int oxnas_pinctrl_parse_functions(struct device_node *np,
+ struct oxnas_pinctrl *info, u32 index)
+{
+ struct device_node *child;
+ struct oxnas_pmx_func *func;
+ struct oxnas_pin_group *grp;
+ int ret;
+ static u32 grp_index;
+ u32 i = 0;
+
+ dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name);
+
+ func = &info->functions[index];
+
+ /* Initialise function */
+ func->name = np->name;
+ func->ngroups = of_get_child_count(np);
+ if (func->ngroups <= 0) {
+ dev_err(info->dev, "no groups defined\n");
+ return -EINVAL;
+ }
+ func->groups = devm_kzalloc(info->dev,
+ func->ngroups * sizeof(char *), GFP_KERNEL);
+ if (!func->groups)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, child) {
+ func->groups[i] = child->name;
+ grp = &info->groups[grp_index++];
+ ret = oxnas_pinctrl_parse_groups(child, grp, info, i++);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id oxnas_pinctrl_of_match[] = {
+ { .compatible = "plxtech,nas782x-pinctrl"},
+ { .compatible = "plxtech,ox810se-pinctrl"},
+ { /* sentinel */ }
+};
+
+static int oxnas_pinctrl_probe_dt(struct platform_device *pdev,
+ struct oxnas_pinctrl *info)
+{
+ int ret = 0;
+ int i, j;
+ uint32_t *tmp;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+
+ if (!np)
+ return -ENODEV;
+
+ info->dev = &pdev->dev;
+
+ oxnas_pinctrl_child_count(info, np);
+
+ if (info->nbanks < 1) {
+ dev_err(&pdev->dev, "you need to specify atleast one gpio-controller\n");
+ return -EINVAL;
+ }
+
+ ret = oxnas_pinctrl_mux_mask(info, np);
+ if (ret)
+ return ret;
+
+ dev_dbg(&pdev->dev, "nmux = %d\n", info->nmux);
+
+ dev_dbg(&pdev->dev, "mux-mask\n");
+ tmp = info->mux_mask;
+ for (i = 0; i < info->nbanks; i++)
+ for (j = 0; j < info->nmux; j++, tmp++)
+ dev_dbg(&pdev->dev, "%d:%d\t0x%x\n", i, j, tmp[0]);
+
+ dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
+ dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups);
+ info->functions = devm_kzalloc(&pdev->dev, info->nfunctions *
+ sizeof(struct oxnas_pmx_func),
+ GFP_KERNEL);
+ if (!info->functions)
+ return -ENOMEM;
+
+ info->groups = devm_kzalloc(&pdev->dev, info->ngroups *
+ sizeof(struct oxnas_pin_group),
+ GFP_KERNEL);
+ if (!info->groups)
+ return -ENOMEM;
+
+ dev_dbg(&pdev->dev, "nbanks = %d\n", info->nbanks);
+ dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
+ dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups);
+
+ i = 0;
+
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, gpio_compat))
+ continue;
+ ret = oxnas_pinctrl_parse_functions(child, info, i++);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse function\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int oxnas_pinctrl_probe(struct platform_device *pdev)
+{
+ struct oxnas_pinctrl *info;
+ struct pinctrl_pin_desc *pdesc;
+ int ret, i, j, k;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "plxtech,sys-ctrl");
+ if (IS_ERR(info->regmap)) {
+ dev_err(&pdev->dev, "failed to get sys ctrl regmap\n");
+ return -ENODEV;
+ }
+
+ ret = oxnas_pinctrl_probe_dt(pdev, info);
+ if (ret)
+ return ret;
+
+ /*
+ * We need all the GPIO drivers to probe FIRST, or we will not be able
+ * to obtain references to the struct gpio_chip * for them, and we
+ * need this to proceed.
+ */
+ for (i = 0; i < info->nbanks; i++) {
+ if (!gpio_chips[i]) {
+ dev_warn(&pdev->dev,
+ "GPIO chip %d not registered yet\n", i);
+ devm_kfree(&pdev->dev, info);
+ return -EPROBE_DEFER;
+ }
+ }
+
+ oxnas_pinctrl_desc.name = dev_name(&pdev->dev);
+ oxnas_pinctrl_desc.npins = info->nbanks * MAX_NB_GPIO_PER_BANK;
+ oxnas_pinctrl_desc.pins = pdesc =
+ devm_kzalloc(&pdev->dev, sizeof(*pdesc) *
+ oxnas_pinctrl_desc.npins, GFP_KERNEL);
+
+ if (!oxnas_pinctrl_desc.pins)
+ return -ENOMEM;
+
+ for (i = 0, k = 0; i < info->nbanks; i++) {
+ for (j = 0; j < MAX_NB_GPIO_PER_BANK; j++, k++) {
+ pdesc->number = k;
+ pdesc->name = kasprintf(GFP_KERNEL, "MF_%c%d", i + 'A',
+ j);
+ pdesc++;
+ }
+ }
+
+ platform_set_drvdata(pdev, info);
+ info->pctl = pinctrl_register(&oxnas_pinctrl_desc, &pdev->dev, info);
+
+ if (!info->pctl) {
+ dev_err(&pdev->dev, "could not register OXNAS pinctrl driver\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* We will handle a range of GPIO pins */
+ for (i = 0; i < info->nbanks; i++)
+ pinctrl_add_gpio_range(info->pctl, &gpio_chips[i]->range);
+
+ dev_info(&pdev->dev, "initialized OXNAS pinctrl driver\n");
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int oxnas_pinctrl_remove(struct platform_device *pdev)
+{
+ struct oxnas_pinctrl *info = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(info->pctl);
+
+ return 0;
+}
+
+static int oxnas_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ /*
+ * Map back to global GPIO space and request muxing, the direction
+ * parameter does not matter for this controller.
+ */
+ int gpio = chip->base + offset;
+ int bank = chip->base / chip->ngpio;
+
+ dev_dbg(chip->parent, "%s:%d MF_%c%d(%d)\n", __func__, __LINE__,
+ 'A' + bank, offset, gpio);
+
+ return pinctrl_request_gpio(gpio);
+}
+
+static void oxnas_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+
+ pinctrl_free_gpio(gpio);
+}
+
+static int oxnas_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+
+ writel_relaxed(BIT(offset), pio + OUTPUT_EN_CLEAR);
+ return 0;
+}
+
+static int oxnas_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+ unsigned mask = 1 << offset;
+ u32 pdsr = 0;
+
+ pdsr = readl_relaxed(pio + INPUT_VALUE);
+ return (pdsr & mask) != 0;
+}
+
+static void oxnas_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+
+ if (val)
+ writel_relaxed(BIT(offset), pio + OUTPUT_SET);
+ else
+ writel_relaxed(BIT(offset), pio + OUTPUT_CLEAR);
+}
+
+static int oxnas_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+
+ if (val)
+ writel_relaxed(BIT(offset), pio + OUTPUT_SET);
+ else
+ writel_relaxed(BIT(offset), pio + OUTPUT_CLEAR);
+
+ writel_relaxed(BIT(offset), pio + OUTPUT_EN_SET);
+
+ return 0;
+}
+
+static int oxnas_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ int virq;
+
+ if (offset < chip->ngpio)
+ virq = irq_create_mapping(oxnas_gpio->domain, offset);
+ else
+ virq = -ENXIO;
+
+ dev_dbg(chip->parent, "%s: request IRQ for GPIO %d, return %d\n",
+ chip->label, offset + chip->base, virq);
+ return virq;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void oxnas_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ int i;
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+ enum oxnas_mux mux;
+
+ for (i = 0; i < chip->ngpio; i++) {
+ unsigned pin = chip->base + i;
+ unsigned mask = pin_to_mask(pin);
+ unsigned bank = pin_to_bank(pin);
+ const char *gpio_label;
+ u32 pdsr;
+
+ gpio_label = gpiochip_is_requested(chip, i);
+ if (gpio_label) {
+ seq_printf(s, "[%s]\tGPIO%s%d: ",
+ gpio_label, chip->label, i);
+ pdsr = readl_relaxed(pio + INPUT_VALUE);
+
+ seq_printf(s, "[gpio] %s\n",
+ pdsr & mask ?
+ "set" : "clear");
+ } else {
+ mux = oxnas_mux_get_func(oxnas_gpio->regmap,
+ bank,
+ mask);
+ seq_printf(s, "\tGPIO%s%d: [func%d]\n",
+ chip->label, i, mux);
+ }
+
+ }
+}
+#else
+#define oxnas_gpio_dbg_show NULL
+#endif
+
+/* Several AIC controller irqs are dispatched through this GPIO handler.
+ * To use any AT91_PIN_* as an externally triggered IRQ, first call
+ * oxnas_set_gpio_input() then maybe enable its glitch filter.
+ * Then just request_irq() with the pin ID; it works like any ARM IRQ
+ * handler.
+ */
+
+static void gpio_irq_mask(struct irq_data *d)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = irq_data_get_irq_chip_data(d);
+ void __iomem *pio = oxnas_gpio->regbase;
+ unsigned mask = 1 << d->hwirq;
+ unsigned type = irqd_get_trigger_type(d);
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ writel(readl(pio + RE_IRQ_ENABLE) & ~mask, pio + RE_IRQ_ENABLE);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ writel(readl(pio + FE_IRQ_ENABLE) & ~mask, pio + FE_IRQ_ENABLE);
+}
+
+static void gpio_irq_unmask(struct irq_data *d)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = irq_data_get_irq_chip_data(d);
+ void __iomem *pio = oxnas_gpio->regbase;
+ unsigned mask = 1 << d->hwirq;
+ unsigned type = irqd_get_trigger_type(d);
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ writel(readl(pio + RE_IRQ_ENABLE) | mask, pio + RE_IRQ_ENABLE);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ writel(readl(pio + FE_IRQ_ENABLE) | mask, pio + FE_IRQ_ENABLE);
+}
+
+
+static int gpio_irq_type(struct irq_data *d, unsigned type)
+{
+ if ((type & IRQ_TYPE_EDGE_BOTH) == 0) {
+ pr_warn("oxnas: Unsupported type for irq %d\n",
+ gpio_to_irq(d->irq));
+ return -EINVAL;
+ }
+ /* seems no way to set trigger type without enable irq,
+ * so leave it to unmask time
+ */
+
+ return 0;
+}
+
+static struct irq_chip gpio_irqchip = {
+ .name = "GPIO",
+ .irq_disable = gpio_irq_mask,
+ .irq_mask = gpio_irq_mask,
+ .irq_unmask = gpio_irq_unmask,
+ .irq_set_type = gpio_irq_type,
+};
+
+static void gpio_irq_handler(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_data *idata = irq_desc_get_irq_data(desc);
+ struct oxnas_gpio_chip *oxnas_gpio = irq_data_get_irq_chip_data(idata);
+ void __iomem *pio = oxnas_gpio->regbase;
+ unsigned long isr;
+ int n;
+
+ chained_irq_enter(chip, desc);
+ for (;;) {
+ isr = readl_relaxed(pio + IRQ_PENDING);
+ if (!isr)
+ break;
+
+ /* acks pending interrupts */
+ writel_relaxed(isr, pio + IRQ_PENDING);
+
+ for_each_set_bit(n, &isr, BITS_PER_LONG) {
+ generic_handle_irq(irq_find_mapping(oxnas_gpio->domain,
+ n));
+ }
+ }
+ chained_irq_exit(chip, desc);
+ /* now it may re-trigger */
+}
+
+/*
+ * This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpio_lock_class;
+
+static int oxnas_gpio_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = h->host_data;
+
+ irq_set_lockdep_class(virq, &gpio_lock_class);
+
+ irq_set_chip_and_handler(virq, &gpio_irqchip, handle_edge_irq);
+
+ irq_set_chip_data(virq, oxnas_gpio);
+
+ return 0;
+}
+
+static int oxnas_gpio_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec,
+ unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_type)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = d->host_data;
+ int ret;
+ int pin = oxnas_gpio->chip.base + intspec[0];
+
+ if (WARN_ON(intsize < 2))
+ return -EINVAL;
+ *out_hwirq = intspec[0];
+ *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+
+ ret = gpio_request(pin, ctrlr->full_name);
+ if (ret)
+ return ret;
+
+ ret = gpio_direction_input(pin);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct irq_domain_ops oxnas_gpio_ops = {
+ .map = oxnas_gpio_irq_map,
+ .xlate = oxnas_gpio_irq_domain_xlate,
+};
+
+static int oxnas_gpio_of_irq_setup(struct device_node *node,
+ struct oxnas_gpio_chip *oxnas_gpio,
+ unsigned int irq)
+{
+ /* Disable irqs of this controller */
+ writel_relaxed(0, oxnas_gpio->regbase + RE_IRQ_ENABLE);
+ writel_relaxed(0, oxnas_gpio->regbase + FE_IRQ_ENABLE);
+
+ /* Setup irq domain */
+ oxnas_gpio->domain = irq_domain_add_linear(node, oxnas_gpio->chip.ngpio,
+ &oxnas_gpio_ops, oxnas_gpio);
+ if (!oxnas_gpio->domain)
+ panic("oxnas_gpio: couldn't allocate irq domain (DT).\n");
+
+ irq_set_chip_data(irq, oxnas_gpio);
+ irq_set_chained_handler(irq, gpio_irq_handler);
+
+ return 0;
+}
+
+/* This structure is replicated for each GPIO block allocated at probe time */
+static struct gpio_chip oxnas_gpio_template = {
+ .request = oxnas_gpio_request,
+ .free = oxnas_gpio_free,
+ .direction_input = oxnas_gpio_direction_input,
+ .get = oxnas_gpio_get,
+ .direction_output = oxnas_gpio_direction_output,
+ .set = oxnas_gpio_set,
+ .to_irq = oxnas_gpio_to_irq,
+ .dbg_show = oxnas_gpio_dbg_show,
+ .can_sleep = 0,
+ .ngpio = MAX_NB_GPIO_PER_BANK,
+};
+
+static const struct of_device_id oxnas_gpio_of_match[] = {
+ { .compatible = "plxtech,nas782x-gpio"},
+ { /* sentinel */ }
+};
+
+static int oxnas_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ struct oxnas_gpio_chip *oxnas_chip = NULL;
+ struct gpio_chip *chip;
+ struct pinctrl_gpio_range *range;
+ struct device_node *node = pdev->dev.of_node;
+ int ret = 0;
+ int irq, i;
+ int alias_idx = of_alias_get_id(np, "gpio");
+ uint32_t ngpio;
+ char **names;
+
+ if (WARN_ON(alias_idx >= ARRAY_SIZE(gpio_chips)))
+ return -EINVAL;
+
+ if (gpio_chips[alias_idx]) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto err;
+ }
+
+ oxnas_chip = devm_kzalloc(&pdev->dev, sizeof(*oxnas_chip), GFP_KERNEL);
+ if (!oxnas_chip) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Get pinctrl sys control regmap */
+ oxnas_chip->regmap =
+ syscon_regmap_lookup_by_phandle(of_get_parent(node),
+ "plxtech,sys-ctrl");
+ if (IS_ERR(oxnas_chip->regmap)) {
+ dev_err(&pdev->dev, "failed to get sys ctrl regmap\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ oxnas_chip->regbase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(oxnas_chip->regbase)) {
+ ret = PTR_ERR(oxnas_chip->regbase);
+ goto err;
+ }
+
+ oxnas_chip->chip = oxnas_gpio_template;
+
+ chip = &oxnas_chip->chip;
+ chip->of_node = np;
+ chip->label = dev_name(&pdev->dev);
+ chip->parent = &pdev->dev;
+ chip->owner = THIS_MODULE;
+ chip->base = alias_idx * MAX_NB_GPIO_PER_BANK;
+
+ if (!of_property_read_u32(np, "#gpio-lines", &ngpio)) {
+ if (ngpio > MAX_NB_GPIO_PER_BANK)
+ pr_err("oxnas_gpio.%d, gpio-nb >= %d failback to %d\n",
+ alias_idx, MAX_NB_GPIO_PER_BANK,
+ MAX_NB_GPIO_PER_BANK);
+ else
+ chip->ngpio = ngpio;
+ }
+
+ names = devm_kzalloc(&pdev->dev, sizeof(char *) * chip->ngpio,
+ GFP_KERNEL);
+
+ if (!names) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < chip->ngpio; i++)
+ names[i] = kasprintf(GFP_KERNEL, "MF_%c%d", alias_idx + 'A', i);
+
+ chip->names = (const char *const *)names;
+
+ range = &oxnas_chip->range;
+ range->name = chip->label;
+ range->id = alias_idx;
+ range->pin_base = range->base = range->id * MAX_NB_GPIO_PER_BANK;
+
+ range->npins = chip->ngpio;
+ range->gc = chip;
+
+ ret = gpiochip_add(chip);
+ if (ret)
+ goto err;
+
+ gpio_chips[alias_idx] = oxnas_chip;
+ gpio_banks = max(gpio_banks, alias_idx + 1);
+
+ oxnas_gpio_of_irq_setup(np, oxnas_chip, irq);
+
+ dev_info(&pdev->dev, "at address %p\n", oxnas_chip->regbase);
+
+ return 0;
+err:
+ dev_err(&pdev->dev, "Failure %i for GPIO %i\n", ret, alias_idx);
+
+ return ret;
+}
+
+static struct platform_driver oxnas_gpio_driver = {
+ .driver = {
+ .name = "gpio-oxnas",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(oxnas_gpio_of_match),
+ },
+ .probe = oxnas_gpio_probe,
+};
+
+static struct platform_driver oxnas_pinctrl_driver = {
+ .driver = {
+ .name = "pinctrl-oxnas",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(oxnas_pinctrl_of_match),
+ },
+ .probe = oxnas_pinctrl_probe,
+ .remove = oxnas_pinctrl_remove,
+};
+
+static int __init oxnas_pinctrl_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&oxnas_gpio_driver);
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&oxnas_pinctrl_driver);
+}
+arch_initcall(oxnas_pinctrl_init);
+
+static void __exit oxnas_pinctrl_exit(void)
+{
+ platform_driver_unregister(&oxnas_pinctrl_driver);
+}
+
+module_exit(oxnas_pinctrl_exit);
--
1.9.1

2016-03-03 11:49:43

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH 12/17] arm: Add new mach-oxnas

On Thu, Mar 03, 2016 at 12:40:05PM +0100, Neil Armstrong wrote:
> Add mach-oxnas directory containing Kconfig and generic oxnas
> handling for future SoC specific features like system reset.

Looking at the current contents of oxnas.c, do you actually need it?
What happens if you boot your with your DT without this file being
built?

--
RMK's Patch system: http://www.arm.linux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

2016-03-03 11:49:25

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 06/17] reset: Add PLX Technology Reset Controller driver

Add System reset controller driver for PLX Technology OXNAS SoC Family.

CC: Ma Haijun <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/reset/Kconfig | 4 ++
drivers/reset/Makefile | 1 +
drivers/reset/reset-oxnas.c | 149 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 154 insertions(+)
create mode 100644 drivers/reset/reset-oxnas.c

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index df37212..f0ea63b 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -12,5 +12,9 @@ menuconfig RESET_CONTROLLER

If unsure, say no.

+config RESET_OXNAS
+ bool
+ select MFD_SYSCON
+
source "drivers/reset/sti/Kconfig"
source "drivers/reset/hisilicon/Kconfig"
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 4d7178e..97e04c5 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_ARCH_STI) += sti/
obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o
obj-$(CONFIG_ATH79) += reset-ath79.o
+obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
diff --git a/drivers/reset/reset-oxnas.c b/drivers/reset/reset-oxnas.c
new file mode 100644
index 0000000..d0ab670
--- /dev/null
+++ b/drivers/reset/reset-oxnas.c
@@ -0,0 +1,149 @@
+/*
+ * drivers/reset/reset-oxnas.c
+ *
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ * Copyright (C) 2014 Ma Haijun <[email protected]>
+ * Copyright (C) 2009 Oxford Semiconductor Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+/* Regmap offsets */
+#define RST_SET_REGOFFSET 0x34
+#define RST_CLR_REGOFFSET 0x38
+
+struct oxnas_reset {
+ struct regmap *regmap;
+ struct reset_controller_dev rcdev;
+};
+
+static int oxnas_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct oxnas_reset *data =
+ container_of(rcdev, struct oxnas_reset, rcdev);
+
+ regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id));
+ msleep(50);
+ regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id));
+
+ return 0;
+}
+
+static int oxnas_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct oxnas_reset *data =
+ container_of(rcdev, struct oxnas_reset, rcdev);
+
+ regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id));
+
+ return 0;
+}
+
+static int oxnas_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct oxnas_reset *data =
+ container_of(rcdev, struct oxnas_reset, rcdev);
+
+ regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id));
+
+ return 0;
+}
+
+static struct reset_control_ops oxnas_reset_ops = {
+ .reset = oxnas_reset_reset,
+ .assert = oxnas_reset_assert,
+ .deassert = oxnas_reset_deassert,
+};
+
+static const struct of_device_id oxnas_reset_dt_ids[] = {
+ { .compatible = "plxtech,nas782x-reset", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, oxnas_reset_dt_ids);
+
+static int oxnas_reset_probe(struct platform_device *pdev)
+{
+ struct oxnas_reset *data;
+ struct device *parent;
+
+ parent = pdev->dev.parent;
+ if (!parent) {
+ dev_err(&pdev->dev, "no parent\n");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = syscon_node_to_regmap(parent->of_node);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&pdev->dev, "failed to get parent regmap\n");
+ return -ENODEV;
+ }
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.nr_resets = 32;
+ data->rcdev.ops = &oxnas_reset_ops;
+ data->rcdev.of_node = pdev->dev.of_node;
+ reset_controller_register(&data->rcdev);
+
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+}
+
+static int oxnas_reset_remove(struct platform_device *pdev)
+{
+ struct oxnas_reset *data = platform_get_drvdata(pdev);
+
+ reset_controller_unregister(&data->rcdev);
+
+ return 0;
+}
+
+static struct platform_driver oxnas_reset_driver = {
+ .probe = oxnas_reset_probe,
+ .remove = oxnas_reset_remove,
+ .driver = {
+ .name = "oxnas-reset",
+ .owner = THIS_MODULE,
+ .of_match_table = oxnas_reset_dt_ids,
+ },
+};
+
+static int __init oxnas_reset_init(void)
+{
+ return platform_driver_probe(&oxnas_reset_driver,
+ oxnas_reset_probe);
+}
+
+/*
+ * Reset controller does not support probe deferral, so it has to be
+ * initialized before any user, in particular, PCIE uses subsys_initcall.
+ */
+arch_initcall(oxnas_reset_init);
--
1.9.1

2016-03-03 11:51:47

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 09/17] dt-bindings: Add PLX Technology OXNAS Standard Clocks bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
.../devicetree/bindings/clock/plxtech,stdclk.txt | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/plxtech,stdclk.txt

diff --git a/Documentation/devicetree/bindings/clock/plxtech,stdclk.txt b/Documentation/devicetree/bindings/clock/plxtech,stdclk.txt
new file mode 100644
index 0000000..46465c6
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/plxtech,stdclk.txt
@@ -0,0 +1,24 @@
+PLX Technology OXNAS SoC Family Standard Clocks
+================================================
+
+Please also refer to clock-bindings.txt in this directory for common clock
+bindings usage.
+
+Required properties:
+- compatible: Should be "plxtech,ox810se-stdclk" or "plxtech,nas782x-stdclk"
+- #clock-cells: 1, see below
+
+Parent node should have the following properties :
+- compatible: Should be "plxtech,ox810se-sys-ctrl", "syscon", "simple-mfd"
+
+example:
+
+sys: sys-ctrl@000000 {
+ compatible = "plxtech,ox810se-sys-ctrl", "syscon", "simple-mfd";
+ reg = <0x000000 0x100000>;
+
+ stdclk: stdclk {
+ compatible = "plxtech,ox810se-stdclk", "plxtech,nas782x-stdclk";
+ #reset-cells = <1>;
+ };
+};
--
1.9.1

2016-03-03 11:52:06

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 08/17] clk: Add PLX Technology OXNAS Standard Clocks

Add PLX Technology OXNAS SoC Family Standard Clocks support.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/clk/Kconfig | 6 ++
drivers/clk/Makefile | 1 +
drivers/clk/clk-oxnas.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 166 insertions(+)
create mode 100644 drivers/clk/clk-oxnas.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index eca8e01..b75ef5c 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -192,6 +192,12 @@ config COMMON_CLK_PXA
---help---
Sypport for the Marvell PXA SoC.

+config COMMON_CLK_OXNAS
+ def_bool COMMON_CLK
+ select MFD_SYSCON
+ ---help---
+ Sypport for the OXNAS SoC Family clocks.
+
config COMMON_CLK_CDCE706
tristate "Clock driver for TI CDCE706 clock synthesizer"
depends on I2C
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index bae4be6..a5d45d8 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_ARCH_MB86S7X) += clk-mb86s7x.o
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
+obj-$(CONFIG_COMMON_CLK_OXNAS) += clk-oxnas.o
obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o
obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
diff --git a/drivers/clk/clk-oxnas.c b/drivers/clk/clk-oxnas.c
new file mode 100644
index 0000000..c4b903f
--- /dev/null
+++ b/drivers/clk/clk-oxnas.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 Broadcom
+ * Copyright (C) 2012 Stephen Warren
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/stringify.h>
+#include <linux/reset.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+/* Standard regmap gate clocks */
+struct clk_std {
+ struct clk_hw hw;
+ signed char bit;
+ struct regmap *regmap;
+};
+
+/* Regmap offsets */
+#define CLK_STAT_REGOFFSET 0x24
+#define CLK_SET_REGOFFSET 0x2c
+#define CLK_CLR_REGOFFSET 0x30
+
+#define NUM_STD_CLKS 10
+#define to_stdclk(_hw) container_of(_hw, struct clk_std, hw)
+
+static int std_clk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_std *std = to_stdclk(hw);
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(std->regmap, CLK_STAT_REGOFFSET, &val);
+ if (ret < 0)
+ return ret;
+
+ return val & BIT(std->bit);
+}
+
+static int std_clk_enable(struct clk_hw *hw)
+{
+ struct clk_std *std = to_stdclk(hw);
+
+ regmap_write(std->regmap, CLK_SET_REGOFFSET, BIT(std->bit));
+
+ return 0;
+}
+
+static void std_clk_disable(struct clk_hw *hw)
+{
+ struct clk_std *std = to_stdclk(hw);
+
+ regmap_write(std->regmap, CLK_CLR_REGOFFSET, BIT(std->bit));
+}
+
+static struct clk_ops std_clk_ops = {
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .is_enabled = std_clk_is_enabled,
+};
+
+static const char *const std_clk_parents[] = {
+ "oscillator",
+};
+
+static const char *const eth_parents[] = {
+ "gmacclk",
+};
+
+#define DECLARE_STD_CLKP(__clk, __bit, __parent) \
+static struct clk_init_data clk_##__clk##_init = { \
+ .name = __stringify(__clk), \
+ .ops = &std_clk_ops, \
+ .parent_names = __parent, \
+ .num_parents = ARRAY_SIZE(__parent), \
+}; \
+ \
+static struct clk_std clk_##__clk = { \
+ .bit = __bit, \
+ .hw = { \
+ .init = &clk_##__clk##_init, \
+ }, \
+}
+
+#define DECLARE_STD_CLK(__clk, __bit) DECLARE_STD_CLKP(__clk, __bit, \
+ std_clk_parents)
+
+DECLARE_STD_CLK(leon, 0);
+DECLARE_STD_CLK(dma_sgdma, 1);
+DECLARE_STD_CLK(cipher, 2);
+/* DECLARE_STD_CLK(sd, 3); - Do not touch DDR clock */
+DECLARE_STD_CLK(sata, 4);
+DECLARE_STD_CLK(audio, 5);
+DECLARE_STD_CLK(usbmph, 6);
+DECLARE_STD_CLKP(etha, 7, eth_parents);
+DECLARE_STD_CLK(pciea, 8);
+DECLARE_STD_CLK(nand, 9);
+
+static struct clk_hw *std_clk_hw_tbl[] = {
+ &clk_leon.hw,
+ &clk_dma_sgdma.hw,
+ &clk_cipher.hw,
+ &clk_sata.hw,
+ &clk_audio.hw,
+ &clk_usbmph.hw,
+ &clk_etha.hw,
+ &clk_pciea.hw,
+ &clk_nand.hw,
+};
+
+static struct clk *std_clk_tbl[ARRAY_SIZE(std_clk_hw_tbl)];
+
+static struct clk_onecell_data std_clk_data;
+
+static void __init oxnas_init_stdclk(struct device_node *np)
+{
+ int i;
+ struct regmap *regmap = syscon_node_to_regmap(of_get_parent(np));
+
+ if (!regmap)
+ panic("failed to have parent regmap\n");
+
+ for (i = 0; i < ARRAY_SIZE(std_clk_hw_tbl); i++) {
+ struct clk_std *std = container_of(std_clk_hw_tbl[i],
+ struct clk_std, hw);
+
+ if (WARN_ON(!std))
+ return;
+ std->regmap = regmap;
+
+ std_clk_tbl[i] = clk_register(NULL, std_clk_hw_tbl[i]);
+ if (WARN_ON(IS_ERR(std_clk_tbl[i])))
+ return;
+ }
+
+ std_clk_data.clks = std_clk_tbl;
+ std_clk_data.clk_num = ARRAY_SIZE(std_clk_tbl);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &std_clk_data);
+}
+CLK_OF_DECLARE(oxnas_pllstd, "plxtech,ox810se-stdclk", oxnas_init_stdclk);
--
1.9.1

2016-03-03 11:52:44

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 05/17] dt-bindings: Add PLX Technology RPS Timer bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
.../devicetree/bindings/timer/plxtech,rps-timer.txt | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/plxtech,rps-timer.txt

diff --git a/Documentation/devicetree/bindings/timer/plxtech,rps-timer.txt b/Documentation/devicetree/bindings/timer/plxtech,rps-timer.txt
new file mode 100644
index 0000000..bc7cb33
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/plxtech,rps-timer.txt
@@ -0,0 +1,17 @@
+PLX Technology OXNAS SoCs Family RPS Timer
+==========================================
+
+Required properties:
+- compatible: Should be "plxtech,nas782x-rps-timer"
+- reg : Specifies base physical address and size of the registers.
+- interrupts : The interrupt of the first timer
+- clocks : The phandle of the timer clock source
+
+example:
+
+timer0: timer@200 {
+ compatible = "plxtech,nas782x-rps-timer";
+ reg = <0x200 0x40>;
+ clocks = <&rpsclk>;
+ interrupts = <4 5>;
+};
--
1.9.1

2016-03-03 11:52:42

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 07/17] dt-bindings: Add PLX Technology Reset Controller bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
.../devicetree/bindings/reset/plxtech,reset.txt | 25 ++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 Documentation/devicetree/bindings/reset/plxtech,reset.txt

diff --git a/Documentation/devicetree/bindings/reset/plxtech,reset.txt b/Documentation/devicetree/bindings/reset/plxtech,reset.txt
new file mode 100644
index 0000000..e99648d
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/plxtech,reset.txt
@@ -0,0 +1,25 @@
+PLX Technology OXNAS SoC Family RESET Controller
+================================================
+
+Please also refer to reset.txt in this directory for common reset
+controller binding usage.
+
+Required properties:
+- compatible: Should be "plxtech,nas782x-reset"
+- #reset-cells: 1, see below
+
+Parent node should have the following properties :
+- compatible: Should be "plxtech,ox810se-sys-ctrl", "syscon", "simple-mfd"
+
+example:
+
+sys: sys-ctrl@000000 {
+ compatible = "plxtech,ox810se-sys-ctrl", "syscon", "simple-mfd";
+ reg = <0x000000 0x100000>;
+
+ reset: reset-controller {
+ compatible = "plxtech,nas782x-reset";
+ #reset-cells = <1>;
+
+ };
+};
--
1.9.1

2016-03-03 11:53:23

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

Add PLX Technology RPS IRQ Controller as irqchip driver.

CC: Ma Haijun <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/irqchip/Kconfig | 5 ++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-rps.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 134 insertions(+)
create mode 100644 drivers/irqchip/irq-rps.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index fb50911..7892c1a 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -135,6 +135,11 @@ config PIC32_EVIC
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN

+config PLXTECH_RPS
+ bool
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+
config RENESAS_INTC_IRQPIN
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 18caacb..3eec3a0 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_I8259) += irq-i8259.o
obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o
obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
+obj-$(CONFIG_PLXTECH_RPS) += irq-rps.o
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
diff --git a/drivers/irqchip/irq-rps.c b/drivers/irqchip/irq-rps.c
new file mode 100644
index 0000000..bcd4a31
--- /dev/null
+++ b/drivers/irqchip/irq-rps.c
@@ -0,0 +1,128 @@
+/*
+ * drivers/irqchip/irq-rps.c
+ *
+ * Copyright (C) 2009 Oxford Semiconductor Ltd
+ * Copyright (C) 2013 Ma Haijun <[email protected]>
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/version.h>
+#include <linux/irqchip.h>
+
+#include <asm/exception.h>
+
+struct rps_chip_data {
+ void __iomem *base;
+ struct irq_domain *domain;
+} rps_data;
+
+enum {
+ RPS_IRQ_COUNT = 32,
+
+ RPS_STATUS = 0,
+ RPS_RAW_STATUS = 4,
+ RPS_UNMASK = 8,
+ RPS_MASK = 0xc,
+};
+
+/* Routines to acknowledge, disable and enable interrupts */
+static void rps_mask_irq(struct irq_data *d)
+{
+ u32 mask = BIT(d->hwirq);
+
+ iowrite32(mask, rps_data.base + RPS_MASK);
+}
+
+static void rps_unmask_irq(struct irq_data *d)
+{
+ u32 mask = BIT(d->hwirq);
+
+ iowrite32(mask, rps_data.base + RPS_UNMASK);
+}
+
+static void rps_ack_irq(struct irq_data *d)
+{
+ /* NOP */
+}
+
+static void __exception_irq_entry handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat;
+ int hwirq;
+
+ irqstat = ioread32(rps_data.base + RPS_STATUS);
+ hwirq = __ffs(irqstat);
+
+ do {
+ handle_IRQ(irq_find_mapping(rps_data.domain, hwirq), regs);
+
+ irqstat = ioread32(rps_data.base + RPS_STATUS);
+ hwirq = __ffs(irqstat);
+ } while (irqstat);
+}
+
+int __init rps_of_init(struct device_node *node, struct device_node *parent)
+{
+ int ret;
+ struct irq_chip_generic *gc;
+
+ if (WARN_ON(!node))
+ return -ENODEV;
+
+ rps_data.base = of_iomap(node, 0);
+ WARN(!rps_data.base, "unable to map rps registers\n");
+
+ rps_data.domain = irq_domain_add_linear(node, RPS_IRQ_COUNT,
+ &irq_generic_chip_ops,
+ NULL);
+ if (!rps_data.domain) {
+ pr_err("%s: could add irq domain\n",
+ node->full_name);
+ return -ENOMEM;
+ }
+
+ ret = irq_alloc_domain_generic_chips(rps_data.domain, RPS_IRQ_COUNT, 1,
+ "RPS", handle_level_irq,
+ 0, 0, IRQ_GC_INIT_NESTED_LOCK);
+ if (ret) {
+ pr_err("%s: could not allocate generic chip\n",
+ node->full_name);
+ irq_domain_remove(rps_data.domain);
+ return -EINVAL;
+ }
+
+ gc = irq_get_domain_generic_chip(rps_data.domain, 0);
+ gc->chip_types[0].chip.irq_ack = rps_ack_irq;
+ gc->chip_types[0].chip.irq_mask = rps_mask_irq;
+ gc->chip_types[0].chip.irq_unmask = rps_unmask_irq;
+
+ /* Disable all IRQs */
+ iowrite32(~0, rps_data.base + RPS_MASK);
+
+ set_handle_irq(handle_irq);
+
+ pr_info("Registered %d rps interrupts\n", RPS_IRQ_COUNT);
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(nas782x, "plxtech,nas782x-rps", rps_of_init);
--
1.9.1

2016-03-03 12:15:51

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 14/17] arm: boot: dts: Add PLX Technology OX810SE dtsi

On Thursday 03 March 2016 12:40:07 Neil Armstrong wrote:
> +
> + aliases {
> + serial0 = &uart0;
> + serial1 = &uart1;
> + serial2 = &uart2;
> + serial3 = &uart3;
> + gpio0 = &gpio0;
> + gpio1 = &gpio1;
> + };
>
Please put the aliases in the per-board file and list only the
devices that are actually connected (in case of uart)

> + uart0: uart@200000 {

Make this serial@200000, to follow the standard naming convention

> +
> + reset: reset-controller {
> + compatible = "plxtech,nas782x-reset";
> + #reset-cells = <1>;
> + };
> +
> + stdclk: stdclk {
> + compatible = "plxtech,ox810se-stdclk", "plxtech,nas782x-stdclk";
> + #clock-cells = <1>;
> + };
> + };

Please change the compatible strings to have no 'x' wildcards in them, but
instead use a specific model.

Regarding the vendor prefixes, my understanding is that "ox810se" was the name
of the chip from Oxford Semiconductor, while nas7820 is a product name from
PLX. I think it would be logical to use "oxford" as the vendor prefix
for anything with a ox810se or ox820 ID in it rather than plxtech.

Note that both of them are now historic, as PLX itself got bought by
Avago and they seem to be discontinuing both the PLX and Oxfor brand
names.

Arnd

2016-03-03 12:24:11

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 00/17] Add Initial support for PLX Technology OX810SE

On Thursday 03 March 2016 12:39:53 Neil Armstrong wrote:
> This serie adds initial support (IRQ, Timer, GPIO, Reset, Serial, Clocks) for
> the PLX Technology OX810SE used in the well-known Western Digital My Book
> World Edition Network Attached Storage device.
>
> Extended support for SATA, DMA and Ethernet will come in further patches.
>
> Upstream support for following devices like the OX820SE is welcome !
>

Looks very nice overall. I think with the timing, it's a way too
late for 4.6 now, but there should be no problem merging this for 4.7.
I assume it's not urgent on your end, as this seems to be a somewhat
historic platform.

I had no idea there was an ARM9 based predecessor of OX820SE, so I was
surprised to see this one instead. How closely related are they?

Arnd

2016-03-03 12:36:19

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 00/17] Add Initial support for PLX Technology OX810SE

On 03/03/2016 01:23 PM, Arnd Bergmann wrote:
> Looks very nice overall. I think with the timing, it's a way too
> late for 4.6 now, but there should be no problem merging this for 4.7.
> I assume it's not urgent on your end, as this seems to be a somewhat
> historic platform.

Yes, it's not urgent, I will certainly post a v2 following your device tree comments.

> I had no idea there was an ARM9 based predecessor of OX820SE, so I was
> surprised to see this one instead. How closely related are they?
They seem very close, the only differences seems to be in the MP CORE, the SATA
IP and the pinctrl.

>
> Arnd
>

Thanks !

Neil

2016-03-03 12:37:29

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 12/17] arm: Add new mach-oxnas

On 03/03/2016 12:49 PM, Russell King - ARM Linux wrote:
> Looking at the current contents of oxnas.c, do you actually need it?
> What happens if you boot your with your DT without this file being
> built?
>

No, it's useless for now, it boots perfectly without.
It was only a base for adding the reset management in a next patchset.

Neil

2016-03-03 12:57:26

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 12/17] arm: Add new mach-oxnas

On Thursday 03 March 2016 12:40:05 Neil Armstrong wrote:
> +
> +config MACH_OX810SE
> + bool "Support OX810SE Based Products"
> + select CPU_ARM926T
> + select PLXTECH_RPS
> + select CLKSRC_RPS_TIMER
> + select RESET_OXNAS
> + select COMMON_CLK_OXNAS
> + select PINCTRL_OXNAS

Please sort these alphabetically

> +
> +static void __init oxnas_init(void)
> +{
> + pr_info("OXNAS Device Tree boot\n");
> +
> + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
> +}

I think you should at least remove this function, as the pr_info is not
needed and the rest is the default.

As Russell mentioned, the entire file is not really needed either, but
so far we have left the trivial per-platform files in place generally.

Arnd

2016-03-03 13:01:18

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

Neil,

On 03/03/16 11:39, Neil Armstrong wrote:
> Add PLX Technology RPS IRQ Controller as irqchip driver.
>
> CC: Ma Haijun <[email protected]>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/irqchip/Kconfig | 5 ++
> drivers/irqchip/Makefile | 1 +
> drivers/irqchip/irq-rps.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 134 insertions(+)
> create mode 100644 drivers/irqchip/irq-rps.c
>
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index fb50911..7892c1a 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -135,6 +135,11 @@ config PIC32_EVIC
> select GENERIC_IRQ_CHIP
> select IRQ_DOMAIN
>
> +config PLXTECH_RPS
> + bool
> + select GENERIC_IRQ_CHIP
> + select IRQ_DOMAIN
> +
> config RENESAS_INTC_IRQPIN
> bool
> select IRQ_DOMAIN
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 18caacb..3eec3a0 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_I8259) += irq-i8259.o
> obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
> obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o
> obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
> +obj-$(CONFIG_PLXTECH_RPS) += irq-rps.o
> obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
> obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
> obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
> diff --git a/drivers/irqchip/irq-rps.c b/drivers/irqchip/irq-rps.c
> new file mode 100644
> index 0000000..bcd4a31
> --- /dev/null
> +++ b/drivers/irqchip/irq-rps.c
> @@ -0,0 +1,128 @@
> +/*
> + * drivers/irqchip/irq-rps.c
> + *
> + * Copyright (C) 2009 Oxford Semiconductor Ltd
> + * Copyright (C) 2013 Ma Haijun <[email protected]>
> + * Copyright (C) 2016 Neil Armstrong <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/irqdomain.h>
> +#include <linux/irq.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/version.h>
> +#include <linux/irqchip.h>
> +
> +#include <asm/exception.h>
> +
> +struct rps_chip_data {
> + void __iomem *base;
> + struct irq_domain *domain;
> +} rps_data;
> +
> +enum {
> + RPS_IRQ_COUNT = 32,
> +
> + RPS_STATUS = 0,
> + RPS_RAW_STATUS = 4,
> + RPS_UNMASK = 8,
> + RPS_MASK = 0xc,
> +};

As much as I hate macros (despite making a living out of writing
complicated/braindead ones), shoving random and unrelated values in an
enum doesn't make much sense. Please convert this to a set of #defines.

> +
> +/* Routines to acknowledge, disable and enable interrupts */
> +static void rps_mask_irq(struct irq_data *d)
> +{
> + u32 mask = BIT(d->hwirq);
> +
> + iowrite32(mask, rps_data.base + RPS_MASK);

I do question the use of iowrite32 here (and its ioread32 pendent
anywhere else), as it actually translates in a writel, which contains a
memory barrier. Do you have any case that requires the use of such a
barrier? if not, consider switching to relaxed accessors (which are the

> +}
> +
> +static void rps_unmask_irq(struct irq_data *d)
> +{
> + u32 mask = BIT(d->hwirq);
> +
> + iowrite32(mask, rps_data.base + RPS_UNMASK);
> +}
> +
> +static void rps_ack_irq(struct irq_data *d)
> +{
> + /* NOP */
> +}

If that's a nop, you probably don't need it, see below.

> +
> +static void __exception_irq_entry handle_irq(struct pt_regs *regs)
> +{
> + u32 irqstat;
> + int hwirq;
> +
> + irqstat = ioread32(rps_data.base + RPS_STATUS);
> + hwirq = __ffs(irqstat);
> +
> + do {
> + handle_IRQ(irq_find_mapping(rps_data.domain, hwirq), regs);

Please use handle_domain_irq() which will do the right thing (and save
you from RCU shouting at you).

> +
> + irqstat = ioread32(rps_data.base + RPS_STATUS);
> + hwirq = __ffs(irqstat);
> + } while (irqstat);
> +}

Can you get more that a single bit set in one read from the status
register? If so, you'd be better off handling all the pending interrupts
before reading from the MMIO again, since that's a slow operation.

> +
> +int __init rps_of_init(struct device_node *node, struct device_node *parent)
> +{
> + int ret;
> + struct irq_chip_generic *gc;
> +
> + if (WARN_ON(!node))
> + return -ENODEV;
> +
> + rps_data.base = of_iomap(node, 0);
> + WARN(!rps_data.base, "unable to map rps registers\n");
> +
> + rps_data.domain = irq_domain_add_linear(node, RPS_IRQ_COUNT,
> + &irq_generic_chip_ops,
> + NULL);
> + if (!rps_data.domain) {
> + pr_err("%s: could add irq domain\n",
> + node->full_name);
> + return -ENOMEM;
> + }
> +
> + ret = irq_alloc_domain_generic_chips(rps_data.domain, RPS_IRQ_COUNT, 1,
> + "RPS", handle_level_irq,

Given that all your interrupts are level triggered...

> + 0, 0, IRQ_GC_INIT_NESTED_LOCK);
> + if (ret) {
> + pr_err("%s: could not allocate generic chip\n",
> + node->full_name);
> + irq_domain_remove(rps_data.domain);
> + return -EINVAL;
> + }
> +
> + gc = irq_get_domain_generic_chip(rps_data.domain, 0);
> + gc->chip_types[0].chip.irq_ack = rps_ack_irq;

... I believe you can loose this callback, it is never used by the handler.

> + gc->chip_types[0].chip.irq_mask = rps_mask_irq;
> + gc->chip_types[0].chip.irq_unmask = rps_unmask_irq;
> +
> + /* Disable all IRQs */
> + iowrite32(~0, rps_data.base + RPS_MASK);
> +
> + set_handle_irq(handle_irq);
> +
> + pr_info("Registered %d rps interrupts\n", RPS_IRQ_COUNT);

Given that this is always the same value, I don't think this is a very
useful message...

> +
> + return 0;
> +}
> +
> +IRQCHIP_DECLARE(nas782x, "plxtech,nas782x-rps", rps_of_init);
>

Thanks,

M.
--
Jazz is not dead. It just smells funny...

2016-03-03 13:08:58

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

On Thursday 03 March 2016 13:01:13 Marc Zyngier wrote:
> > +/* Routines to acknowledge, disable and enable interrupts */
> > +static void rps_mask_irq(struct irq_data *d)
> > +{
> > + u32 mask = BIT(d->hwirq);
> > +
> > + iowrite32(mask, rps_data.base + RPS_MASK);
>
> I do question the use of iowrite32 here (and its ioread32 pendent
> anywhere else), as it actually translates in a writel, which contains a
> memory barrier. Do you have any case that requires the use of such a
> barrier? if not, consider switching to relaxed accessors (which are the
>

I really ask everyone to do the opposite: we have seen several drivers
blindlessly using the relaxed accessors and actually introducing bugs
that way, so I'd rather see the readl/writel ones used by default.

In any performance critical code, it's reasonable to take a closer
look and use the relaxed version with an added comment explaining
why it's safe there.

Arnd

2016-03-03 13:30:10

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH 12/17] arm: Add new mach-oxnas

On Thu, Mar 03, 2016 at 01:56:56PM +0100, Arnd Bergmann wrote:
> As Russell mentioned, the entire file is not really needed either, but
> so far we have left the trivial per-platform files in place generally.

I think that's something we should be looking to remove: if the
per-platform files give no benefit, then there is no point in
having them, and they just increase the size of the code base.

--
RMK's Patch system: http://www.arm.linux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

2016-03-03 13:37:07

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

On Thu, Mar 03, 2016 at 02:08:19PM +0100, Arnd Bergmann wrote:
> On Thursday 03 March 2016 13:01:13 Marc Zyngier wrote:
> > > +/* Routines to acknowledge, disable and enable interrupts */
> > > +static void rps_mask_irq(struct irq_data *d)
> > > +{
> > > + u32 mask = BIT(d->hwirq);
> > > +
> > > + iowrite32(mask, rps_data.base + RPS_MASK);
> >
> > I do question the use of iowrite32 here (and its ioread32 pendent
> > anywhere else), as it actually translates in a writel, which contains a
> > memory barrier. Do you have any case that requires the use of such a
> > barrier? if not, consider switching to relaxed accessors (which are the
> >
>
> I really ask everyone to do the opposite: we have seen several drivers
> blindlessly using the relaxed accessors and actually introducing bugs
> that way, so I'd rather see the readl/writel ones used by default.

I actually agree with Marc - we have far too many drivers using the
barriered IO accessors, which are really very expensive on 32-bit
ARM.

For most ARM systems, the rules are quite simple: a write which causes
DMA memory to be accessed by the device must be using the barriered
IO accessor, and a read from a DMA status register must be too.
Everything else need not be. Barriered IO accessors are only about
access ordering.

That's independent of whether you need a read-back to ensure that the
write has hit the hardware: that's a completely different problem, and
one which is harder for people to understand and get right. (Eg, for
interrupt registers.)

--
RMK's Patch system: http://www.arm.linux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

2016-03-03 13:39:18

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 14/17] arm: boot: dts: Add PLX Technology OX810SE dtsi

On 03/03/2016 01:15 PM, Arnd Bergmann wrote:
> On Thursday 03 March 2016 12:40:07 Neil Armstrong wrote:
>> +
>> + aliases {
>> + serial0 = &uart0;
>> + serial1 = &uart1;
>> + serial2 = &uart2;
>> + serial3 = &uart3;
>> + gpio0 = &gpio0;
>> + gpio1 = &gpio1;
>> + };
>>
> Please put the aliases in the per-board file and list only the
> devices that are actually connected (in case of uart)
Done.

>
>> + uart0: uart@200000 {
>
> Make this serial@200000, to follow the standard naming convention
Done.

>
>> +
>> + reset: reset-controller {
>> + compatible = "plxtech,nas782x-reset";
>> + #reset-cells = <1>;
>> + };
>> +
>> + stdclk: stdclk {
>> + compatible = "plxtech,ox810se-stdclk", "plxtech,nas782x-stdclk";
>> + #clock-cells = <1>;
>> + };
>> + };
>
> Please change the compatible strings to have no 'x' wildcards in them, but
> instead use a specific model.
Ok, I switched to only oxsemi,ox810se-* strings.
>
> Regarding the vendor prefixes, my understanding is that "ox810se" was the name
> of the chip from Oxford Semiconductor, while nas7820 is a product name from
> PLX. I think it would be logical to use "oxford" as the vendor prefix
> for anything with a ox810se or ox820 ID in it rather than plxtech.
Ok, I switched to oxsemi but keeped plxtech and added oxsemi in the vendor prefixes.

>
> Note that both of them are now historic, as PLX itself got bought by
> Avago and they seem to be discontinuing both the PLX and Oxfor brand
> names.
Yes, it's kind of a mess. The ox820 has been rebranded to plx7821, but not the ox810...
>
> Arnd
>

Thanks,
Neil

2016-03-03 13:40:56

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 12/17] arm: Add new mach-oxnas

On Thursday 03 March 2016 13:29:56 Russell King - ARM Linux wrote:
> On Thu, Mar 03, 2016 at 01:56:56PM +0100, Arnd Bergmann wrote:
> > As Russell mentioned, the entire file is not really needed either, but
> > so far we have left the trivial per-platform files in place generally.
>
> I think that's something we should be looking to remove: if the
> per-platform files give no benefit, then there is no point in
> having them, and they just increase the size of the code base.
>

I agree, I think the main reason it hasn't been done yet is that
nobody has thought in detail about whether or how to keep
the last remaining piece of information (the machine name as
printed in /proc/cpuinfo) when removing the machine descriptors.

If we want to keep that around, we could probably have a global
lookup table of root node compatible strings, or we decide to
use the contents of the "model" property instead. This has been
discussed several times in the pasts, without any real consensus.

Arnd

2016-03-03 14:18:33

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 06/17] reset: Add PLX Technology Reset Controller driver

Hi Neil,

Am Donnerstag, den 03.03.2016, 12:39 +0100 schrieb Neil Armstrong:
> Add System reset controller driver for PLX Technology OXNAS SoC Family.
>
> CC: Ma Haijun <[email protected]>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/reset/Kconfig | 4 ++
> drivers/reset/Makefile | 1 +
> drivers/reset/reset-oxnas.c | 149 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 154 insertions(+)
> create mode 100644 drivers/reset/reset-oxnas.c
>
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> index df37212..f0ea63b 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -12,5 +12,9 @@ menuconfig RESET_CONTROLLER
>
> If unsure, say no.
>
> +config RESET_OXNAS
> + bool
> + select MFD_SYSCON

I'd prefer not to select MFD_SYSCON here, but rather let ARCH_OXNAS do
that.

> source "drivers/reset/sti/Kconfig"
> source "drivers/reset/hisilicon/Kconfig"
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index 4d7178e..97e04c5 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -7,3 +7,4 @@ obj-$(CONFIG_ARCH_STI) += sti/
> obj-$(CONFIG_ARCH_HISI) += hisilicon/
> obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o
> obj-$(CONFIG_ATH79) += reset-ath79.o
> +obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
> diff --git a/drivers/reset/reset-oxnas.c b/drivers/reset/reset-oxnas.c
> new file mode 100644
> index 0000000..d0ab670
> --- /dev/null
> +++ b/drivers/reset/reset-oxnas.c
> @@ -0,0 +1,149 @@
> +/*
> + * drivers/reset/reset-oxnas.c
> + *
> + * Copyright (C) 2016 Neil Armstrong <[email protected]>
> + * Copyright (C) 2014 Ma Haijun <[email protected]>
> + * Copyright (C) 2009 Oxford Semiconductor Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +#include <linux/err.h>
> +#include <linux/io.h>

Is there any need to include linux/io.h ?

> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset-controller.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/types.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +
> +/* Regmap offsets */
> +#define RST_SET_REGOFFSET 0x34
> +#define RST_CLR_REGOFFSET 0x38
> +
> +struct oxnas_reset {
> + struct regmap *regmap;
> + struct reset_controller_dev rcdev;
> +};
> +
> +static int oxnas_reset_reset(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + struct oxnas_reset *data =
> + container_of(rcdev, struct oxnas_reset, rcdev);
> +
> + regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id));
> + msleep(50);

Is this the right delay for all of the resets in this register?
If not, I'd drop the .reset callback.

> + regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id));
> +
> + return 0;
> +}
> +
> +static int oxnas_reset_assert(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + struct oxnas_reset *data =
> + container_of(rcdev, struct oxnas_reset, rcdev);
> +
> + regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id));
> +
> + return 0;
> +}
> +
> +static int oxnas_reset_deassert(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + struct oxnas_reset *data =
> + container_of(rcdev, struct oxnas_reset, rcdev);
> +
> + regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id));
> +
> + return 0;
> +}
> +
> +static struct reset_control_ops oxnas_reset_ops = {

const

> + .reset = oxnas_reset_reset,
> + .assert = oxnas_reset_assert,
> + .deassert = oxnas_reset_deassert,
> +};
> +
> +static const struct of_device_id oxnas_reset_dt_ids[] = {
> + { .compatible = "plxtech,nas782x-reset", },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, oxnas_reset_dt_ids);
> +
> +static int oxnas_reset_probe(struct platform_device *pdev)
> +{
> + struct oxnas_reset *data;
> + struct device *parent;
> +
> + parent = pdev->dev.parent;
> + if (!parent) {
> + dev_err(&pdev->dev, "no parent\n");

Can this even happen?

> + return -ENODEV;
> + }
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->regmap = syscon_node_to_regmap(parent->of_node);
> + if (IS_ERR(data->regmap)) {
> + dev_err(&pdev->dev, "failed to get parent regmap\n");
> + return -ENODEV;

Better print the error code and return it.

> + }
> +
> + data->rcdev.owner = THIS_MODULE;
> + data->rcdev.nr_resets = 32;
> + data->rcdev.ops = &oxnas_reset_ops;
> + data->rcdev.of_node = pdev->dev.of_node;
> + reset_controller_register(&data->rcdev);

Move this down a bit:

> +
> + platform_set_drvdata(pdev, data);
> +
> + return 0;

and
return reset_controller_register(&data->rcdev);
here.

> +}
> +
> +static int oxnas_reset_remove(struct platform_device *pdev)
> +{
> + struct oxnas_reset *data = platform_get_drvdata(pdev);
> +
> + reset_controller_unregister(&data->rcdev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver oxnas_reset_driver = {
> + .probe = oxnas_reset_probe,
> + .remove = oxnas_reset_remove,
> + .driver = {
> + .name = "oxnas-reset",
> + .owner = THIS_MODULE,

The .owner field is overwritten by __platform_driver_register() anyway,
just drop it.

> + .of_match_table = oxnas_reset_dt_ids,
> + },
> +};
> +
> +static int __init oxnas_reset_init(void)
> +{
> + return platform_driver_probe(&oxnas_reset_driver,
> + oxnas_reset_probe);
> +}
> +
> +/*
> + * Reset controller does not support probe deferral, so it has to be
> + * initialized before any user, in particular, PCIE uses subsys_initcall.
> + */
> +arch_initcall(oxnas_reset_init);

That doesn't sound right. (of_)reset_control_get return -EPROBE_DEFER if
the rcdev isn't found in the list. Could you elaborate on this?

regards
Philipp

2016-03-03 14:21:53

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 07/17] dt-bindings: Add PLX Technology Reset Controller bindings

Am Donnerstag, den 03.03.2016, 12:40 +0100 schrieb Neil Armstrong:
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> .../devicetree/bindings/reset/plxtech,reset.txt | 25 ++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/reset/plxtech,reset.txt
>
> diff --git a/Documentation/devicetree/bindings/reset/plxtech,reset.txt b/Documentation/devicetree/bindings/reset/plxtech,reset.txt
> new file mode 100644
> index 0000000..e99648d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/reset/plxtech,reset.txt
> @@ -0,0 +1,25 @@
> +PLX Technology OXNAS SoC Family RESET Controller
> +================================================
> +
> +Please also refer to reset.txt in this directory for common reset
> +controller binding usage.
> +
> +Required properties:
> +- compatible: Should be "plxtech,nas782x-reset"
> +- #reset-cells: 1, see below
> +
> +Parent node should have the following properties :
> +- compatible: Should be "plxtech,ox810se-sys-ctrl", "syscon", "simple-mfd"
> +
> +example:
> +
> +sys: sys-ctrl@000000 {
> + compatible = "plxtech,ox810se-sys-ctrl", "syscon", "simple-mfd";
> + reg = <0x000000 0x100000>;
> +
> + reset: reset-controller {
> + compatible = "plxtech,nas782x-reset";
> + #reset-cells = <1>;
> +
> + };
> +};

Is there a list of the reset bits in this register?

regards
Philipp

2016-03-03 14:24:55

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 07/17] dt-bindings: Add PLX Technology Reset Controller bindings

On 03/03/2016 03:21 PM, Philipp Zabel wrote:
> Am Donnerstag, den 03.03.2016, 12:40 +0100 schrieb Neil Armstrong:
>> Signed-off-by: Neil Armstrong <[email protected]>
>
> Is there a list of the reset bits in this register?
>
> regards
> Philipp
>

Yes, should I add it to the bindings ?

Neil

2016-03-03 14:29:44

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 06/17] reset: Add PLX Technology Reset Controller driver

On 03/03/2016 03:18 PM, Philipp Zabel wrote:
> Hi Neil,
>
>> +config RESET_OXNAS
>> + bool
>> + select MFD_SYSCON
>
> I'd prefer not to select MFD_SYSCON here, but rather let ARCH_OXNAS do
> that.
>
OK.

>> +#include <linux/io.h>
>
> Is there any need to include linux/io.h ?

No, dropping.

>> +static int oxnas_reset_reset(struct reset_controller_dev *rcdev,
>> + unsigned long id)
>> +{
>> + struct oxnas_reset *data =
>> + container_of(rcdev, struct oxnas_reset, rcdev);
>> +
>> + regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id));
>> + msleep(50);
>
> Is this the right delay for all of the resets in this register?
> If not, I'd drop the .reset callback.
>
The delay is not strictly necessary, but better to avoid any HW issues.
And the .reset callback is needed since reset_control_reset
does not assert -> deassert as fallback.

>> + regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id));
>> +
>> + return 0;
>> +}
>> +
>> +static int oxnas_reset_assert(struct reset_controller_dev *rcdev,
>> + unsigned long id)
>> +{
>> + struct oxnas_reset *data =
>> + container_of(rcdev, struct oxnas_reset, rcdev);
>> +
>> + regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id));
>> +
>> + return 0;
>> +}
>> +
>> +static int oxnas_reset_deassert(struct reset_controller_dev *rcdev,
>> + unsigned long id)
>> +{
>> + struct oxnas_reset *data =
>> + container_of(rcdev, struct oxnas_reset, rcdev);
>> +
>> + regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id));
>> +
>> + return 0;
>> +}
>> +
>> +static struct reset_control_ops oxnas_reset_ops = {
>
> const
>
Something checkpatch should report...

>> + .reset = oxnas_reset_reset,
>> + .assert = oxnas_reset_assert,
>> + .deassert = oxnas_reset_deassert,
>> +};
>> +
>> +static const struct of_device_id oxnas_reset_dt_ids[] = {
>> + { .compatible = "plxtech,nas782x-reset", },
>> + { /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, oxnas_reset_dt_ids);
>> +
>> +static int oxnas_reset_probe(struct platform_device *pdev)
>> +{
>> + struct oxnas_reset *data;
>> + struct device *parent;
>> +
>> + parent = pdev->dev.parent;
>> + if (!parent) {
>> + dev_err(&pdev->dev, "no parent\n");
>
> Can this even happen?
>
It's to make sure parent->of_node is valid for syscon_node_to_regmap.

>> + return -ENODEV;
>> + }
>> +
>> + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
>> + if (!data)
>> + return -ENOMEM;
>> +
>> + data->regmap = syscon_node_to_regmap(parent->of_node);
>> + if (IS_ERR(data->regmap)) {
>> + dev_err(&pdev->dev, "failed to get parent regmap\n");
>> + return -ENODEV;
>
> Better print the error code and return it.
>
Good point.

>> + }
>> +
>> + data->rcdev.owner = THIS_MODULE;
>> + data->rcdev.nr_resets = 32;
>> + data->rcdev.ops = &oxnas_reset_ops;
>> + data->rcdev.of_node = pdev->dev.of_node;
>> + reset_controller_register(&data->rcdev);
>
> Move this down a bit:
>
>> +
>> + platform_set_drvdata(pdev, data);
>> +
>> + return 0;
>
> and
> return reset_controller_register(&data->rcdev);
> here.
>
Yes, sound better...

>> +static struct platform_driver oxnas_reset_driver = {
>> + .probe = oxnas_reset_probe,
>> + .remove = oxnas_reset_remove,
>> + .driver = {
>> + .name = "oxnas-reset",
>> + .owner = THIS_MODULE,
>
> The .owner field is overwritten by __platform_driver_register() anyway,
> just drop it.
OK

>> +/*
>> + * Reset controller does not support probe deferral, so it has to be
>> + * initialized before any user, in particular, PCIE uses subsys_initcall.
>> + */
>> +arch_initcall(oxnas_reset_init);
>
> That doesn't sound right. (of_)reset_control_get return -EPROBE_DEFER if
> the rcdev isn't found in the list. Could you elaborate on this?
It was an old change, I will put back the generic module platform init.

>
> regards
> Philipp
>
Thanks,

Neil

2016-03-03 14:31:10

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 07/17] dt-bindings: Add PLX Technology Reset Controller bindings

Am Donnerstag, den 03.03.2016, 15:24 +0100 schrieb Neil Armstrong:
> On 03/03/2016 03:21 PM, Philipp Zabel wrote:
> > Am Donnerstag, den 03.03.2016, 12:40 +0100 schrieb Neil Armstrong:
> >> Signed-off-by: Neil Armstrong <[email protected]>
> >
> > Is there a list of the reset bits in this register?
> >
> > regards
> > Philipp
> >
>
> Yes, should I add it to the bindings ?

Yes, please. Either that or add a header file with #defines to
include/dt-bindings/reset and use it in the dtsi.

regards
Philipp

2016-03-03 14:53:53

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH 03/17] dt-bindings: Add PLX Technology RPS IRQ Controller bindings

On Thu, Mar 03, 2016 at 12:39:56PM +0100, Neil Armstrong wrote:
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> .../bindings/interrupt-controller/plxtech,rps-irq.txt | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/interrupt-controller/plxtech,rps-irq.txt
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/plxtech,rps-irq.txt b/Documentation/devicetree/bindings/interrupt-controller/plxtech,rps-irq.txt
> new file mode 100644
> index 0000000..db117a0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/plxtech,rps-irq.txt

...

> +- compatible: Should be "plxtech,nas782x-rps"

Hi Neil

It would be nice to be consistent with the naming.

Maybe also the filename of the driver itself should be changed. You
had an interesting sorting problem in the Makefile, which a consistent
name would help with.

Andrew

2016-03-03 14:57:24

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 03/17] dt-bindings: Add PLX Technology RPS IRQ Controller bindings

Hi Andrew,

On 03/03/2016 03:53 PM, Andrew Lunn wrote:
>
>> +- compatible: Should be "plxtech,nas782x-rps"
>
> Hi Neil
>
> It would be nice to be consistent with the naming.
The compatible is now oxsemi,os810se-rps-irq which is consistent with the timer.

>
> Maybe also the filename of the driver itself should be changed. You
> had an interesting sorting problem in the Makefile, which a consistent
> name would help with.
Yes, I will add -irq to the driver name and _IRQ to the config name.

>
> Andrew
>
Thanks,
Neil

2016-03-03 15:00:50

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 06/17] reset: Add PLX Technology Reset Controller driver

Am Donnerstag, den 03.03.2016, 15:29 +0100 schrieb Neil Armstrong:
> >> +static int oxnas_reset_reset(struct reset_controller_dev *rcdev,
> >> + unsigned long id)
> >> +{
> >> + struct oxnas_reset *data =
> >> + container_of(rcdev, struct oxnas_reset, rcdev);
> >> +
> >> + regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id));
> >> + msleep(50);
> >
> > Is this the right delay for all of the resets in this register?
> > If not, I'd drop the .reset callback.
> >
> The delay is not strictly necessary, but better to avoid any HW issues.

Ok, maybe add a comment.

> And the .reset callback is needed since reset_control_reset
> does not assert -> deassert as fallback.

That's because some controllers don't even have manual
assertion/deassertion, and for some reset lines the drivers better know
the timing or they want to do other stuff while the reset is asserted.

[...]
> >> +static struct reset_control_ops oxnas_reset_ops = {
> >
> > const
> >
> Something checkpatch should report...

This is new in any case. rcdev->ops was not const* until recently.

> >> + .reset = oxnas_reset_reset,
> >> + .assert = oxnas_reset_assert,
> >> + .deassert = oxnas_reset_deassert,
> >> +};
> >> +
> >> +static const struct of_device_id oxnas_reset_dt_ids[] = {
> >> + { .compatible = "plxtech,nas782x-reset", },
> >> + { /* sentinel */ },
> >> +};
> >> +MODULE_DEVICE_TABLE(of, oxnas_reset_dt_ids);
> >> +
> >> +static int oxnas_reset_probe(struct platform_device *pdev)
> >> +{
> >> + struct oxnas_reset *data;
> >> + struct device *parent;
> >> +
> >> + parent = pdev->dev.parent;
> >> + if (!parent) {
> >> + dev_err(&pdev->dev, "no parent\n");
> >
> > Can this even happen?
> >
> It's to make sure parent->of_node is valid for syscon_node_to_regmap.

Since this is a platform device probed via device tree,
pdev->dev.parent should always be set (see of_device_alloc()).

regards
Philipp

2016-03-03 15:02:20

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 01/17] dt-bindings: vendor-prefixes: Add PLX Technology

Am Donnerstag, den 03.03.2016, 12:39 +0100 schrieb Neil Armstrong:
> Add PLX Technology vendor prefix.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index 72e2c5a..03970fb 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -178,6 +178,7 @@ picochip Picochip Ltd
> plathome Plat'Home Co., Ltd.
> plda PLDA
> pixcir PIXCIR MICROELECTRONICS Co., Ltd
> +plxtech PLX Technology, Inc.

The PLX Technology NASDAQ symbol used to be PLXT.
Personally, I prefer the more verbose name, just pointing out that
"plxt," might also be an option.

regards
Philipp

2016-03-03 15:06:07

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH 03/17] dt-bindings: Add PLX Technology RPS IRQ Controller bindings

On Thu, Mar 03, 2016 at 03:57:10PM +0100, Neil Armstrong wrote:
> Hi Andrew,
>
> On 03/03/2016 03:53 PM, Andrew Lunn wrote:
> >
> >> +- compatible: Should be "plxtech,nas782x-rps"
> >
> > Hi Neil
> >
> > It would be nice to be consistent with the naming.
> The compatible is now oxsemi,os810se-rps-irq which is consistent with the timer.

Ah, i had not read that far down the thread yet.

Thanks
Andrew

2016-03-03 15:36:00

by Ma Haijun

[permalink] [raw]
Subject: RE: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

Hi Neil,

Glad to see the mainline efforts of this SoC family.

Previously, I did not really understand what this "RPS" stood for.
After some digging(1)., now I believe it means ARM's Reference Peripheral
Specification
though the spec itself seems not publicly available, the peripheral specs
are accessible.
The interrupt controller is an AMBA Interrupt Controller(2) and the timers
are
AMBA Timer(3). Besides, ARM Dual-Timer Module (SP804)(4) looks like an
extended version
of the AMBA timer

1)
http://infocenter.arm.com/help/topic/com.arm.doc.dai0030a/DAI0030A_sw_int_ap
psnote.pdf
2) http://infocenter.arm.com/help/topic/com.arm.doc.ddi0047d/DDI0047.pdf
3) http://infocenter.arm.com/help/topic/com.arm.doc.ddi0049c/AMBA_Timer.pdf
4) http://infocenter.arm.com/help/topic/com.arm.doc.ddi0271d/DDI0271.pdf

Regards,
Haijun

-----Original Message-----
From: Neil Armstrong [mailto:[email protected]]
Subject: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

Add PLX Technology RPS IRQ Controller as irqchip driver.

CC: Ma Haijun <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/irqchip/Kconfig | 5 ++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-rps.c | 128
++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 134 insertions(+)
create mode 100644 drivers/irqchip/irq-rps.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index
fb50911..7892c1a 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -135,6 +135,11 @@ config PIC32_EVIC
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN

+config PLXTECH_RPS
+ bool
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+
config RENESAS_INTC_IRQPIN
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index
18caacb..3eec3a0 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_I8259) += irq-i8259.o
obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o
obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
+obj-$(CONFIG_PLXTECH_RPS) += irq-rps.o
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
diff --git a/drivers/irqchip/irq-rps.c b/drivers/irqchip/irq-rps.c new file
mode 100644 index 0000000..bcd4a31
--- /dev/null
+++ b/drivers/irqchip/irq-rps.c
@@ -0,0 +1,128 @@
+/*
+ * drivers/irqchip/irq-rps.c
+ *
+ * Copyright (C) 2009 Oxford Semiconductor Ltd
+ * Copyright (C) 2013 Ma Haijun <[email protected]>
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/version.h>
+#include <linux/irqchip.h>
+
+#include <asm/exception.h>
+
+struct rps_chip_data {
+ void __iomem *base;
+ struct irq_domain *domain;
+} rps_data;
+
+enum {
+ RPS_IRQ_COUNT = 32,
+
+ RPS_STATUS = 0,
+ RPS_RAW_STATUS = 4,
+ RPS_UNMASK = 8,
+ RPS_MASK = 0xc,
+};
+
+/* Routines to acknowledge, disable and enable interrupts */ static
+void rps_mask_irq(struct irq_data *d) {
+ u32 mask = BIT(d->hwirq);
+
+ iowrite32(mask, rps_data.base + RPS_MASK); }
+
+static void rps_unmask_irq(struct irq_data *d) {
+ u32 mask = BIT(d->hwirq);
+
+ iowrite32(mask, rps_data.base + RPS_UNMASK); }
+
+static void rps_ack_irq(struct irq_data *d) {
+ /* NOP */
+}
+
+static void __exception_irq_entry handle_irq(struct pt_regs *regs) {
+ u32 irqstat;
+ int hwirq;
+
+ irqstat = ioread32(rps_data.base + RPS_STATUS);
+ hwirq = __ffs(irqstat);
+
+ do {
+ handle_IRQ(irq_find_mapping(rps_data.domain, hwirq), regs);
+
+ irqstat = ioread32(rps_data.base + RPS_STATUS);
+ hwirq = __ffs(irqstat);
+ } while (irqstat);
+}
+
+int __init rps_of_init(struct device_node *node, struct device_node
+*parent) {
+ int ret;
+ struct irq_chip_generic *gc;
+
+ if (WARN_ON(!node))
+ return -ENODEV;
+
+ rps_data.base = of_iomap(node, 0);
+ WARN(!rps_data.base, "unable to map rps registers\n");
+
+ rps_data.domain = irq_domain_add_linear(node, RPS_IRQ_COUNT,
+ &irq_generic_chip_ops,
+ NULL);
+ if (!rps_data.domain) {
+ pr_err("%s: could add irq domain\n",
+ node->full_name);
+ return -ENOMEM;
+ }
+
+ ret = irq_alloc_domain_generic_chips(rps_data.domain, RPS_IRQ_COUNT,
1,
+ "RPS", handle_level_irq,
+ 0, 0, IRQ_GC_INIT_NESTED_LOCK);
+ if (ret) {
+ pr_err("%s: could not allocate generic chip\n",
+ node->full_name);
+ irq_domain_remove(rps_data.domain);
+ return -EINVAL;
+ }
+
+ gc = irq_get_domain_generic_chip(rps_data.domain, 0);
+ gc->chip_types[0].chip.irq_ack = rps_ack_irq;
+ gc->chip_types[0].chip.irq_mask = rps_mask_irq;
+ gc->chip_types[0].chip.irq_unmask = rps_unmask_irq;
+
+ /* Disable all IRQs */
+ iowrite32(~0, rps_data.base + RPS_MASK);
+
+ set_handle_irq(handle_irq);
+
+ pr_info("Registered %d rps interrupts\n", RPS_IRQ_COUNT);
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(nas782x, "plxtech,nas782x-rps", rps_of_init);
--
1.9.1

2016-03-03 16:56:21

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

On 03/03/2016 04:32 PM, Ma Haijun wrote:
> Hi Neil,
>
> Glad to see the mainline efforts of this SoC family.
>
> Previously, I did not really understand what this "RPS" stood for.
> After some digging(1)., now I believe it means ARM's Reference Peripheral
> Specification
> though the spec itself seems not publicly available, the peripheral specs
> are accessible.
> The interrupt controller is an AMBA Interrupt Controller(2) and the timers
> are
> AMBA Timer(3). Besides, ARM Dual-Timer Module (SP804)(4) looks like an
> extended version
> of the AMBA timer
>
> 1)
> http://infocenter.arm.com/help/topic/com.arm.doc.dai0030a/DAI0030A_sw_int_ap
> psnote.pdf
> 2) http://infocenter.arm.com/help/topic/com.arm.doc.ddi0047d/DDI0047.pdf
> 3) http://infocenter.arm.com/help/topic/com.arm.doc.ddi0049c/AMBA_Timer.pdf
> 4) http://infocenter.arm.com/help/topic/com.arm.doc.ddi0271d/DDI0271.pdf
>
> Regards,
> Haijun

Hi Haijun,

Thanks for the tips !

Indeed the timer looks like the timer-sp804 actually upstream, I'll try this driver before posting a v2.

Concerning the IRQ controller, I did not find any similar drivers.

Marc, Arnd, should I create a specific "arm RPS irq controller" instead ?

Neil

2016-03-03 17:17:44

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

On 03/03/16 16:56, Neil Armstrong wrote:
> On 03/03/2016 04:32 PM, Ma Haijun wrote:
>> Hi Neil,
>>
>> Glad to see the mainline efforts of this SoC family.
>>
>> Previously, I did not really understand what this "RPS" stood for.
>> After some digging(1)., now I believe it means ARM's Reference Peripheral
>> Specification
>> though the spec itself seems not publicly available, the peripheral specs
>> are accessible.
>> The interrupt controller is an AMBA Interrupt Controller(2) and the timers
>> are
>> AMBA Timer(3). Besides, ARM Dual-Timer Module (SP804)(4) looks like an
>> extended version
>> of the AMBA timer
>>
>> 1)
>> http://infocenter.arm.com/help/topic/com.arm.doc.dai0030a/DAI0030A_sw_int_ap
>> psnote.pdf
>> 2) http://infocenter.arm.com/help/topic/com.arm.doc.ddi0047d/DDI0047.pdf
>> 3) http://infocenter.arm.com/help/topic/com.arm.doc.ddi0049c/AMBA_Timer.pdf
>> 4) http://infocenter.arm.com/help/topic/com.arm.doc.ddi0271d/DDI0271.pdf
>>
>> Regards,
>> Haijun
>
> Hi Haijun,
>
> Thanks for the tips !
>
> Indeed the timer looks like the timer-sp804 actually upstream, I'll try this driver before posting a v2.
>
> Concerning the IRQ controller, I did not find any similar drivers.
>
> Marc, Arnd, should I create a specific "arm RPS irq controller" instead ?

Why not - I wonder which HW this controller got slapped on... It doesn't
even have a cryptic name! ;-)

Thanks,

M.
--
Jazz is not dead. It just smells funny...

2016-03-03 17:33:34

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

On Thursday 03 March 2016 13:36:49 Russell King - ARM Linux wrote:
> On Thu, Mar 03, 2016 at 02:08:19PM +0100, Arnd Bergmann wrote:
> > On Thursday 03 March 2016 13:01:13 Marc Zyngier wrote:
> > > > +/* Routines to acknowledge, disable and enable interrupts */
> > > > +static void rps_mask_irq(struct irq_data *d)
> > > > +{
> > > > + u32 mask = BIT(d->hwirq);
> > > > +
> > > > + iowrite32(mask, rps_data.base + RPS_MASK);
> > >
> > > I do question the use of iowrite32 here (and its ioread32 pendent
> > > anywhere else), as it actually translates in a writel, which contains a
> > > memory barrier. Do you have any case that requires the use of such a
> > > barrier? if not, consider switching to relaxed accessors (which are the
> > >
> >
> > I really ask everyone to do the opposite: we have seen several drivers
> > blindlessly using the relaxed accessors and actually introducing bugs
> > that way, so I'd rather see the readl/writel ones used by default.
>
> I actually agree with Marc - we have far too many drivers using the
> barriered IO accessors, which are really very expensive on 32-bit
> ARM.
>
> For most ARM systems, the rules are quite simple: a write which causes
> DMA memory to be accessed by the device must be using the barriered
> IO accessor, and a read from a DMA status register must be too.
> Everything else need not be. Barriered IO accessors are only about
> access ordering.

My main worry is really about code getting copied from drivers that
are fine with just relaxed accessors into other drivers by developers
that have never heard about the difference and just want to follow
best practices.

Arnd

2016-03-04 02:25:37

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH 08/17] clk: Add PLX Technology OXNAS Standard Clocks

On 03/03, Neil Armstrong wrote:
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index eca8e01..b75ef5c 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -192,6 +192,12 @@ config COMMON_CLK_PXA
> ---help---
> Sypport for the Marvell PXA SoC.
>
> +config COMMON_CLK_OXNAS
> + def_bool COMMON_CLK
> + select MFD_SYSCON

So this is always built if I have the common clk framework
enabled? Not good.

> + ---help---
> + Sypport for the OXNAS SoC Family clocks.
> +
> config COMMON_CLK_CDCE706
> tristate "Clock driver for TI CDCE706 clock synthesizer"
> depends on I2C
> diff --git a/drivers/clk/clk-oxnas.c b/drivers/clk/clk-oxnas.c
> new file mode 100644
> index 0000000..c4b903f
> --- /dev/null
> +++ b/drivers/clk/clk-oxnas.c
> @@ -0,0 +1,159 @@
> +/*
> + * Copyright (C) 2010 Broadcom
> + * Copyright (C) 2012 Stephen Warren
> + * Copyright (C) 2016 Neil Armstrong <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>

Are either of these includes used?

> +#include <linux/clk-provider.h>
> +#include <linux/of.h>
> +#include <linux/delay.h>

Is this include used?

> +#include <linux/stringify.h>
> +#include <linux/reset.h>

Is this include used?

> +#include <linux/io.h>

Is this include used?

> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>

#include <linux/kernel.h> for container_of?

> +
> +/* Standard regmap gate clocks */
> +struct clk_std {
> + struct clk_hw hw;
> + signed char bit;
> + struct regmap *regmap;
> +};
> +
> +/* Regmap offsets */
> +#define CLK_STAT_REGOFFSET 0x24
> +#define CLK_SET_REGOFFSET 0x2c
> +#define CLK_CLR_REGOFFSET 0x30
> +
> +#define NUM_STD_CLKS 10
> +#define to_stdclk(_hw) container_of(_hw, struct clk_std, hw)
> +
> +static int std_clk_is_enabled(struct clk_hw *hw)
> +{
> + struct clk_std *std = to_stdclk(hw);
> + int ret;
> + unsigned int val;
> +
> + ret = regmap_read(std->regmap, CLK_STAT_REGOFFSET, &val);
> + if (ret < 0)
> + return ret;
> +
> + return val & BIT(std->bit);
> +}
> +
> +static int std_clk_enable(struct clk_hw *hw)
> +{
> + struct clk_std *std = to_stdclk(hw);
> +
> + regmap_write(std->regmap, CLK_SET_REGOFFSET, BIT(std->bit));

I hope the regmap is fast_io? Otherwise this is scheduling while
atomic.

> +
> + return 0;
> +}
> +
> +static void std_clk_disable(struct clk_hw *hw)
> +{
> + struct clk_std *std = to_stdclk(hw);
> +
> + regmap_write(std->regmap, CLK_CLR_REGOFFSET, BIT(std->bit));
> +}
> +
> +static struct clk_ops std_clk_ops = {

const?

> + .enable = std_clk_enable,
> + .disable = std_clk_disable,
> + .is_enabled = std_clk_is_enabled,
> +};
> +
[..]
> +
> +static struct clk_hw *std_clk_hw_tbl[] = {

const?

> + &clk_leon.hw,
> + &clk_dma_sgdma.hw,
> + &clk_cipher.hw,
> + &clk_sata.hw,
> + &clk_audio.hw,
> + &clk_usbmph.hw,
> + &clk_etha.hw,
> + &clk_pciea.hw,
> + &clk_nand.hw,
> +};
> +
> +static struct clk *std_clk_tbl[ARRAY_SIZE(std_clk_hw_tbl)];
> +
> +static struct clk_onecell_data std_clk_data;

These are pretty generic. Perhaps oxnas_clk_data and
oxnas_clk_hw_tbl?

> +
> +static void __init oxnas_init_stdclk(struct device_node *np)
> +{
> + int i;
> + struct regmap *regmap = syscon_node_to_regmap(of_get_parent(np));
> +
> + if (!regmap)
> + panic("failed to have parent regmap\n");
> +
> + for (i = 0; i < ARRAY_SIZE(std_clk_hw_tbl); i++) {
> + struct clk_std *std = container_of(std_clk_hw_tbl[i],
> + struct clk_std, hw);
> +
> + if (WARN_ON(!std))
> + return;
> + std->regmap = regmap;
> +
> + std_clk_tbl[i] = clk_register(NULL, std_clk_hw_tbl[i]);
> + if (WARN_ON(IS_ERR(std_clk_tbl[i])))
> + return;
> + }
> +
> + std_clk_data.clks = std_clk_tbl;
> + std_clk_data.clk_num = ARRAY_SIZE(std_clk_tbl);
> +
> + of_clk_add_provider(np, of_clk_src_onecell_get, &std_clk_data);
> +}
> +CLK_OF_DECLARE(oxnas_pllstd, "plxtech,ox810se-stdclk", oxnas_init_stdclk);

Can this be a platform driver instead?

Is there a binding for this compatible?

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

2016-03-04 11:10:25

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 02/17] irqchip: Add PLX Technology RPS IRQ Controller

On 03/03/2016 06:17 PM, Marc Zyngier wrote:
> On 03/03/16 16:56, Neil Armstrong wrote:
>> On 03/03/2016 04:32 PM, Ma Haijun wrote:
>> Hi Haijun,
>>
>> Thanks for the tips !
>>
>> Indeed the timer looks like the timer-sp804 actually upstream, I'll try this driver before posting a v2.
>>
>> Concerning the IRQ controller, I did not find any similar drivers.
>>
>> Marc, Arnd, should I create a specific "arm RPS irq controller" instead ?
>
> Why not - I wonder which HW this controller got slapped on... It doesn't
> even have a cryptic name! ;-)
>
> Thanks,
>
> M.
>

Actually, it seems we already have an implementation in irq-versatile-fpga, it matches !

Adding a single "arm,rps-irq" compatible string should work.

Neil

2016-03-05 04:29:40

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH 01/17] dt-bindings: vendor-prefixes: Add PLX Technology

On Thu, Mar 03, 2016 at 04:02:15PM +0100, Philipp Zabel wrote:
> Am Donnerstag, den 03.03.2016, 12:39 +0100 schrieb Neil Armstrong:
> > Add PLX Technology vendor prefix.
> >
> > Signed-off-by: Neil Armstrong <[email protected]>
> > ---
> > Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> > 1 file changed, 1 insertion(+)
> >
> > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > index 72e2c5a..03970fb 100644
> > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > @@ -178,6 +178,7 @@ picochip Picochip Ltd
> > plathome Plat'Home Co., Ltd.
> > plda PLDA
> > pixcir PIXCIR MICROELECTRONICS Co., Ltd
> > +plxtech PLX Technology, Inc.
>
> The PLX Technology NASDAQ symbol used to be PLXT.

And is no longer listed? If not, then plxtech is fine. Otherwise, use
plxt.

Can you fix the alphabetizing while you are at it.

Rob

2016-03-05 04:29:49

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH 16/17] dt-bindings: Add Western Digital to vendor prefixes

On Thu, Mar 03, 2016 at 12:40:09PM +0100, Neil Armstrong wrote:
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> 1 file changed, 1 insertion(+)

Acked-by: Rob Herring <[email protected]>

2016-03-07 09:55:16

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 01/17] dt-bindings: vendor-prefixes: Add PLX Technology

Am Freitag, den 04.03.2016, 22:29 -0600 schrieb Rob Herring:
> On Thu, Mar 03, 2016 at 04:02:15PM +0100, Philipp Zabel wrote:
> > Am Donnerstag, den 03.03.2016, 12:39 +0100 schrieb Neil Armstrong:
> > > Add PLX Technology vendor prefix.
> > >
> > > Signed-off-by: Neil Armstrong <[email protected]>
> > > ---
> > > Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> > > 1 file changed, 1 insertion(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > > index 72e2c5a..03970fb 100644
> > > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> > > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > > @@ -178,6 +178,7 @@ picochip Picochip Ltd
> > > plathome Plat'Home Co., Ltd.
> > > plda PLDA
> > > pixcir PIXCIR MICROELECTRONICS Co., Ltd
> > > +plxtech PLX Technology, Inc.
> >
> > The PLX Technology NASDAQ symbol used to be PLXT.
>
> And is no longer listed? If not, then plxtech is fine. Otherwise, use
> plxt.

PLXT was listed until August 2014, when Avago (now Broadcom Limited)
acquired PLX Technology.

regards
Philipp

2016-03-07 11:24:53

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 08/17] clk: Add PLX Technology OXNAS Standard Clocks

On 03/04/2016 03:25 AM, Stephen Boyd wrote:
> On 03/03, Neil Armstrong wrote:
>> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
>> index eca8e01..b75ef5c 100644
>> --- a/drivers/clk/Kconfig
>> +++ b/drivers/clk/Kconfig
>> @@ -192,6 +192,12 @@ config COMMON_CLK_PXA
>> ---help---
>> Sypport for the Marvell PXA SoC.
>>
>> +config COMMON_CLK_OXNAS
>> + def_bool COMMON_CLK
>> + select MFD_SYSCON
>
> So this is always built if I have the common clk framework
> enabled? Not good.
Fixed.

>> +#include <linux/clk.h>
>> +#include <linux/clkdev.h>
>
> Are either of these includes used?
>
>> +#include <linux/clk-provider.h>
>> +#include <linux/of.h>
>> +#include <linux/delay.h>
>
> Is this include used?
>
>> +#include <linux/stringify.h>
>> +#include <linux/reset.h>
>
> Is this include used?
>
>> +#include <linux/io.h>
>
> Is this include used?
>
>> +#include <linux/regmap.h>
>> +#include <linux/mfd/syscon.h>
>
> #include <linux/kernel.h> for container_of?

Fixed an cleaned up, thanks.


>> +static int std_clk_enable(struct clk_hw *hw)
>> +{
>> + struct clk_std *std = to_stdclk(hw);
>> +
>> + regmap_write(std->regmap, CLK_SET_REGOFFSET, BIT(std->bit));
>
> I hope the regmap is fast_io? Otherwise this is scheduling while
> atomic.
Yes, but due to the nature of the registers, I can't use the clk-regmap module.

>> +
>> + return 0;
>> +}
>> +
>> +static void std_clk_disable(struct clk_hw *hw)
>> +{
>> + struct clk_std *std = to_stdclk(hw);
>> +
>> + regmap_write(std->regmap, CLK_CLR_REGOFFSET, BIT(std->bit));
>> +}
>> +
>> +static struct clk_ops std_clk_ops = {
>
> const?
>
>> + .enable = std_clk_enable,
>> + .disable = std_clk_disable,
>> + .is_enabled = std_clk_is_enabled,
>> +};
>> +
> [..]
>> +
>> +static struct clk_hw *std_clk_hw_tbl[] = {
>
> const?
>
>> + &clk_leon.hw,
>> + &clk_dma_sgdma.hw,
>> + &clk_cipher.hw,
>> + &clk_sata.hw,
>> + &clk_audio.hw,
>> + &clk_usbmph.hw,
>> + &clk_etha.hw,
>> + &clk_pciea.hw,
>> + &clk_nand.hw,
>> +};
>> +
>> +static struct clk *std_clk_tbl[ARRAY_SIZE(std_clk_hw_tbl)];
>> +
>> +static struct clk_onecell_data std_clk_data;
>
> These are pretty generic. Perhaps oxnas_clk_data and
> oxnas_clk_hw_tbl?
>
>> +
>> +static void __init oxnas_init_stdclk(struct device_node *np)
>> +{
>> + int i;
>> + struct regmap *regmap = syscon_node_to_regmap(of_get_parent(np));
>> +
>> + if (!regmap)
>> + panic("failed to have parent regmap\n");
>> +
>> + for (i = 0; i < ARRAY_SIZE(std_clk_hw_tbl); i++) {
>> + struct clk_std *std = container_of(std_clk_hw_tbl[i],
>> + struct clk_std, hw);
>> +
>> + if (WARN_ON(!std))
>> + return;
>> + std->regmap = regmap;
>> +
>> + std_clk_tbl[i] = clk_register(NULL, std_clk_hw_tbl[i]);
>> + if (WARN_ON(IS_ERR(std_clk_tbl[i])))
>> + return;
>> + }
>> +
>> + std_clk_data.clks = std_clk_tbl;
>> + std_clk_data.clk_num = ARRAY_SIZE(std_clk_tbl);
>> +
>> + of_clk_add_provider(np, of_clk_src_onecell_get, &std_clk_data);
>> +}
>> +CLK_OF_DECLARE(oxnas_pllstd, "plxtech,ox810se-stdclk", oxnas_init_stdclk);
>
> Can this be a platform driver instead?
>
> Is there a binding for this compatible?

I refactored the driver to be platform driver and cleaned up the structure to be const and allocate the clocks at probe.

The bindings was in a separate patch, I forgot to CC linux-clk :
http://lkml.kernel.org/r/[email protected]

In the meantime I added the indices description in the bindings.

Neil


2016-03-09 10:25:00

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 00/18] Add Initial support for PLX Technology OX810SE

This serie adds initial support (IRQ, Timer, GPIO, Reset, Serial, Clocks) for
the PLX Technology OX810SE used in the well-known Western Digital My Book
World Edition Network Attached Storage device.

Extended support for SATA, DMA and Ethernet will come in further patches.

Upstream support for following devices like the OX820SE is welcome !

v2 changes :
- switch all compatible string to oxsemi,ox820se
- add oxsemi to prefixes
- switch to versatile-fpga interrupt controller with new compatible
- switch to sp804 timer with new timer width property
- cleanup of mach-oxnas (removal of generic oxnas.c)
- cleanup of standard clock to a platform driver

v1 : http://lkml.kernel.org/r/[email protected]

Neil Armstrong (18):
clocksource: sp804: Add support for non-32bit width counter
dt-bindings: timer: sp804: add timer-width property
irqchip: versatile-fpga: add new arm,rps-irq compatible
dt-bindings: irq: arm,versatile-fpga: add arm,rps-irq compatible
string
dt-bindings: vendor-prefixes: Add PLX Technology
dt-bindings: Add Oxford Semiconductors to vendor prefixes
reset: Add PLX Technology Reset Controller driver
dt-bindings: Add PLX Technology Reset Controller bindings
clk: Add PLX Technology OXNAS Standard Clocks
dt-bindings: Add PLX Technology OXNAS Standard Clocks bindings
pinctrl: Add PLX Technology OXNAS pinctrl and gpio driver
dt-bindings: Add PLX Technology OXNAS pinctrl and gpio bindings
arm: Add new mach-oxnas
arm: Add build support for mach-oxnas
arm: boot: dts: Add PLX Technology OX810SE dtsi
dt-bindings: Add OXNAS bindings
dt-bindings: Add Western Digital to vendor prefixes
arm: boot: dts: Add Western Digital My Book World Edition device tree

Documentation/devicetree/bindings/arm/oxnas.txt | 9 +
.../devicetree/bindings/clock/plxtech,stdclk.txt | 35 +
.../devicetree/bindings/gpio/gpio_oxnas.txt | 27 +
.../arm,versatile-fpga-irq.txt | 2 +-
.../bindings/pinctrl/plxtech,pinctrl.txt | 100 ++
.../devicetree/bindings/reset/plxtech,reset.txt | 58 +
.../devicetree/bindings/timer/arm,sp804.txt | 2 +
.../devicetree/bindings/vendor-prefixes.txt | 5 +-
arch/arm/Kconfig | 2 +
arch/arm/Makefile | 1 +
arch/arm/boot/dts/Makefile | 2 +
arch/arm/boot/dts/ox810se.dtsi | 273 ++++
arch/arm/boot/dts/wd-mbwe.dts | 112 ++
arch/arm/mach-oxnas/Kconfig | 25 +
arch/arm/mach-oxnas/Makefile | 1 +
drivers/clk/Kconfig | 6 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-oxnas.c | 202 +++
drivers/clocksource/timer-sp804.c | 38 +-
drivers/irqchip/irq-versatile-fpga.c | 1 +
drivers/pinctrl/Kconfig | 9 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-oxnas.c | 1392 ++++++++++++++++++++
drivers/reset/Kconfig | 3 +
drivers/reset/Makefile | 1 +
drivers/reset/reset-oxnas.c | 136 ++
include/clocksource/timer-sp804.h | 11 +-
27 files changed, 2437 insertions(+), 18 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/oxnas.txt
create mode 100644 Documentation/devicetree/bindings/clock/plxtech,stdclk.txt
create mode 100644 Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
create mode 100644 Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt
create mode 100644 Documentation/devicetree/bindings/reset/plxtech,reset.txt
create mode 100644 arch/arm/boot/dts/ox810se.dtsi
create mode 100644 arch/arm/boot/dts/wd-mbwe.dts
create mode 100644 arch/arm/mach-oxnas/Kconfig
create mode 100644 arch/arm/mach-oxnas/Makefile
create mode 100644 drivers/clk/clk-oxnas.c
create mode 100644 drivers/pinctrl/pinctrl-oxnas.c
create mode 100644 drivers/reset/reset-oxnas.c

--
1.9.1

2016-03-09 10:25:08

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 02/18] dt-bindings: timer: sp804: add timer-width property

Add timer-width optional property to specify a different vendor
specific timer counter bit-width.

Signed-off-by: Neil Armstrong <[email protected]>
---
Documentation/devicetree/bindings/timer/arm,sp804.txt | 2 ++
1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/timer/arm,sp804.txt b/Documentation/devicetree/bindings/timer/arm,sp804.txt
index 5cd8eee7..141e143 100644
--- a/Documentation/devicetree/bindings/timer/arm,sp804.txt
+++ b/Documentation/devicetree/bindings/timer/arm,sp804.txt
@@ -17,6 +17,8 @@ Optional properties:
- arm,sp804-has-irq = <#>: In the case of only 1 timer irq line connected, this
specifies if the irq connection is for timer 1 or timer 2. A value of 1
or 2 should be used.
+- arm,timer-width: Should contain the width in number of bits of the counter,
+ is considered by default 32 but can be changed for vendor variants.

Example:

--
1.9.1

2016-03-09 10:25:16

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 01/18] clocksource: sp804: Add support for non-32bit width counter

Some vendor variants can implement norrower counter width, add
an optional DT property changing the clocksource width and the
clockevent mask, but keeping 32bit as default for legacy interface.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/clocksource/timer-sp804.c | 38 +++++++++++++++++++++++++++-----------
include/clocksource/timer-sp804.h | 11 ++++++-----
2 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c
index 5f45b9a..8acf524 100644
--- a/drivers/clocksource/timer-sp804.c
+++ b/drivers/clocksource/timer-sp804.c
@@ -80,7 +80,8 @@ void __init sp804_timer_disable(void __iomem *base)
void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
const char *name,
struct clk *clk,
- int use_sched_clock)
+ int use_sched_clock,
+ unsigned width)
{
long rate;

@@ -93,6 +94,9 @@ void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
}
}

+ if (!width || width > 32)
+ width = 32;
+
rate = sp804_get_clock_rate(clk);

if (rate < 0)
@@ -106,11 +110,11 @@ void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
base + TIMER_CTRL);

clocksource_mmio_init(base + TIMER_VALUE, name,
- rate, 200, 32, clocksource_mmio_readl_down);
+ rate, 200, width, clocksource_mmio_readl_down);

if (use_sched_clock) {
sched_clock_base = base;
- sched_clock_register(sp804_read, 32, rate);
+ sched_clock_register(sp804_read, width, rate);
}
}

@@ -186,7 +190,9 @@ static struct irqaction sp804_timer_irq = {
.dev_id = &sp804_clockevent,
};

-void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name)
+void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq,
+ struct clk *clk, const char *name,
+ unsigned width)
{
struct clock_event_device *evt = &sp804_clockevent;
long rate;
@@ -199,6 +205,9 @@ void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struc
return;
}

+ if (!width || width > 32)
+ width = 32;
+
rate = sp804_get_clock_rate(clk);
if (rate < 0)
return;
@@ -212,7 +221,7 @@ void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struc
writel(0, base + TIMER_CTRL);

setup_irq(irq, &sp804_timer_irq);
- clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
+ clockevents_config_and_register(evt, rate, 0xf, GENMASK(width-1, 0));
}

static void __init sp804_of_init(struct device_node *np)
@@ -223,6 +232,7 @@ static void __init sp804_of_init(struct device_node *np)
u32 irq_num = 0;
struct clk *clk1, *clk2;
const char *name = of_get_property(np, "compatible", NULL);
+ u32 width = 32;

base = of_iomap(np, 0);
if (WARN_ON(!base))
@@ -254,14 +264,19 @@ static void __init sp804_of_init(struct device_node *np)
if (irq <= 0)
goto err;

+ /* Some vendor variants can have a different counter width */
+ of_property_read_u32(np, "arm,timer-width", &width);
+
of_property_read_u32(np, "arm,sp804-has-irq", &irq_num);
if (irq_num == 2) {
- __sp804_clockevents_init(base + TIMER_2_BASE, irq, clk2, name);
- __sp804_clocksource_and_sched_clock_init(base, name, clk1, 1);
+ __sp804_clockevents_init(base + TIMER_2_BASE, irq,
+ clk2, name, width);
+ __sp804_clocksource_and_sched_clock_init(base, name,
+ clk1, 1, width);
} else {
- __sp804_clockevents_init(base, irq, clk1 , name);
+ __sp804_clockevents_init(base, irq, clk1, name, width);
__sp804_clocksource_and_sched_clock_init(base + TIMER_2_BASE,
- name, clk2, 1);
+ name, clk2, 1, width);
}
initialized = true;

@@ -293,13 +308,14 @@ static void __init integrator_cp_of_init(struct device_node *np)
goto err;

if (!init_count)
- __sp804_clocksource_and_sched_clock_init(base, name, clk, 0);
+ __sp804_clocksource_and_sched_clock_init(base, name,
+ clk, 0, 32);
else {
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0)
goto err;

- __sp804_clockevents_init(base, irq, clk, name);
+ __sp804_clockevents_init(base, irq, clk, name, 32);
}

init_count++;
diff --git a/include/clocksource/timer-sp804.h b/include/clocksource/timer-sp804.h
index 1f8a1ca..ad71fcb 100644
--- a/include/clocksource/timer-sp804.h
+++ b/include/clocksource/timer-sp804.h
@@ -4,25 +4,26 @@
struct clk;

void __sp804_clocksource_and_sched_clock_init(void __iomem *,
- const char *, struct clk *, int);
+ const char *, struct clk *,
+ int, unsigned);
void __sp804_clockevents_init(void __iomem *, unsigned int,
- struct clk *, const char *);
+ struct clk *, const char *, unsigned);
void sp804_timer_disable(void __iomem *);

static inline void sp804_clocksource_init(void __iomem *base, const char *name)
{
- __sp804_clocksource_and_sched_clock_init(base, name, NULL, 0);
+ __sp804_clocksource_and_sched_clock_init(base, name, NULL, 0, 32);
}

static inline void sp804_clocksource_and_sched_clock_init(void __iomem *base,
const char *name)
{
- __sp804_clocksource_and_sched_clock_init(base, name, NULL, 1);
+ __sp804_clocksource_and_sched_clock_init(base, name, NULL, 1, 32);
}

static inline void sp804_clockevents_init(void __iomem *base, unsigned int irq, const char *name)
{
- __sp804_clockevents_init(base, irq, NULL, name);
+ __sp804_clockevents_init(base, irq, NULL, name, 32);

}
#endif
--
1.9.1

2016-03-09 10:25:22

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 04/18] dt-bindings: irq: arm,versatile-fpga: add arm,rps-irq compatible string

Under the OX810SE, this same controller is used as "Reference Peripheral
Specification" Interrupt Controller, so add new compatible string.

Signed-off-by: Neil Armstrong <[email protected]>
---
.../devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt
index c9cf605..2fe78d5 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt
@@ -6,7 +6,7 @@ controllers are OR:ed together and fed to the CPU tile's IRQ input. Each
instance can handle up to 32 interrupts.

Required properties:
-- compatible: "arm,versatile-fpga-irq"
+- compatible: "arm,versatile-fpga-irq" or "arm,rps-irq"
- interrupt-controller: Identifies the node as an interrupt controller
- #interrupt-cells: The number of cells to define the interrupts. Must be 1
as the FPGA IRQ controller has no configuration options for interrupt
--
1.9.1

2016-03-09 10:25:32

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 03/18] irqchip: versatile-fpga: add new arm,rps-irq compatible

Under the OX810SE, this exact same interface is used as "Reference Peripheral
Specification" Interrupt Controller, so add a new compatible string.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/irqchip/irq-versatile-fpga.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c
index 598ab3f..389b8e2 100644
--- a/drivers/irqchip/irq-versatile-fpga.c
+++ b/drivers/irqchip/irq-versatile-fpga.c
@@ -227,4 +227,5 @@ int __init fpga_irq_of_init(struct device_node *node,
}
IRQCHIP_DECLARE(arm_fpga, "arm,versatile-fpga-irq", fpga_irq_of_init);
IRQCHIP_DECLARE(arm_fpga_sic, "arm,versatile-sic", fpga_irq_of_init);
+IRQCHIP_DECLARE(arm_rps, "arm,rps-irq", fpga_irq_of_init);
#endif
--
1.9.1

2016-03-09 10:26:03

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 14/18] arm: Add build support for mach-oxnas

Add Kconfig and Makefile support for mach-oxnas.

Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm/Kconfig | 2 ++
arch/arm/Makefile | 1 +
2 files changed, 3 insertions(+)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4f799e5..2025fc9 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -801,6 +801,8 @@ source "arch/arm/plat-pxa/Kconfig"

source "arch/arm/mach-mmp/Kconfig"

+source "arch/arm/mach-oxnas/Kconfig"
+
source "arch/arm/mach-qcom/Kconfig"

source "arch/arm/mach-realview/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index fe25410..0e96c63 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -194,6 +194,7 @@ machine-$(CONFIG_ARCH_NSPIRE) += nspire
machine-$(CONFIG_ARCH_OMAP1) += omap1
machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
machine-$(CONFIG_ARCH_ORION5X) += orion5x
+machine-$(CONFIG_ARCH_OXNAS) += oxnas
machine-$(CONFIG_ARCH_PICOXCELL) += picoxcell
machine-$(CONFIG_ARCH_PXA) += pxa
machine-$(CONFIG_ARCH_QCOM) += qcom
--
1.9.1

2016-03-09 10:26:08

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 16/18] dt-bindings: Add OXNAS bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
Documentation/devicetree/bindings/arm/oxnas.txt | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/oxnas.txt

diff --git a/Documentation/devicetree/bindings/arm/oxnas.txt b/Documentation/devicetree/bindings/arm/oxnas.txt
new file mode 100644
index 0000000..f6032d2
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/oxnas.txt
@@ -0,0 +1,9 @@
+PLX Technology OXNAS SoCs Family device tree bindings
+-------------------------------------------
+
+Boards with the OX810SE Soc SoC shall have the following properties:
+ Required root node property:
+ compatible: "oxsemi,ox810se"
+
+Board compatible values:
+ - "wd,mbwe" (OX810SE)
--
1.9.1

2016-03-09 10:26:20

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 17/18] dt-bindings: Add Western Digital to vendor prefixes

Acked-by: Rob Herring <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 188671f..968d3f4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -249,6 +249,7 @@ via VIA Technologies, Inc.
virtio Virtual I/O Device Specification, developed by the OASIS consortium
vivante Vivante Corporation
voipac Voipac Technologies s.r.o.
+wd Western Digital Corp.
wexler Wexler
winbond Winbond Electronics corp.
wlf Wolfson Microelectronics
--
1.9.1

2016-03-09 10:26:14

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 13/18] arm: Add new mach-oxnas

Add mach-oxnas directory containing Kconfig.

Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm/mach-oxnas/Kconfig | 25 +++++++++++++++++++++++++
arch/arm/mach-oxnas/Makefile | 1 +
2 files changed, 26 insertions(+)
create mode 100644 arch/arm/mach-oxnas/Kconfig
create mode 100644 arch/arm/mach-oxnas/Makefile

diff --git a/arch/arm/mach-oxnas/Kconfig b/arch/arm/mach-oxnas/Kconfig
new file mode 100644
index 0000000..63cba044
--- /dev/null
+++ b/arch/arm/mach-oxnas/Kconfig
@@ -0,0 +1,25 @@
+menuconfig ARCH_OXNAS
+ bool "PLX Technology OXNAS Family SoCs"
+ select ARCH_REQUIRE_GPIOLIB
+ select ARCH_HAS_RESET_CONTROLLER
+ select PINCTRL
+ depends on ARCH_MULTI_V5
+ help
+ Support for OxNas SoC family developed by PLX Technology.
+ (Formely Oxford Semiconductor)
+
+if ARCH_OXNAS
+
+config MACH_OX810SE
+ bool "Support OX810SE Based Products"
+ select ARM_TIMER_SP804
+ select COMMON_CLK_OXNAS
+ select CPU_ARM926T
+ select MFD_SYSCON
+ select PINCTRL_OXNAS
+ select RESET_OXNAS
+ select VERSATILE_FPGA_IRQ
+ help
+ Include Support for the Oxford Semiconductor OX810SE SoC Based Products.
+
+endif
diff --git a/arch/arm/mach-oxnas/Makefile b/arch/arm/mach-oxnas/Makefile
new file mode 100644
index 0000000..d98d860
--- /dev/null
+++ b/arch/arm/mach-oxnas/Makefile
@@ -0,0 +1 @@
+# Nothing Yet
--
1.9.1

2016-03-09 10:27:00

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 15/18] arm: boot: dts: Add PLX Technology OX810SE dtsi

Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm/boot/dts/ox810se.dtsi | 273 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 273 insertions(+)
create mode 100644 arch/arm/boot/dts/ox810se.dtsi

diff --git a/arch/arm/boot/dts/ox810se.dtsi b/arch/arm/boot/dts/ox810se.dtsi
new file mode 100644
index 0000000..5852a57
--- /dev/null
+++ b/arch/arm/boot/dts/ox810se.dtsi
@@ -0,0 +1,273 @@
+/*
+ * ox810se.dtsi - Device tree file for Oxford Semiconductor OX810SE SoC
+ *
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ compatible = "oxsemi,ox810se";
+
+ cpus {
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ cpu {
+ device_type = "cpu";
+ compatible = "arm,arm926ej-s";
+ clocks = <&armclk>;
+ };
+ };
+
+ memory {
+ /* Max 256MB @ 0x48000000 */
+ reg = <0x48000000 0x10000000>;
+ };
+
+ clocks {
+ osc: oscillator {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>;
+ };
+
+ gmacclk: gmacclk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <125000000>;
+ };
+
+ rpsclk: rpsclk {
+ compatible = "fixed-factor-clock";
+ #clock-cells = <0>;
+ clock-div = <1>;
+ clock-mult = <1>;
+ clocks = <&osc>;
+ };
+
+ pll400: pll400 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <733333333>;
+ };
+
+ sysclk: sysclk {
+ compatible = "fixed-factor-clock";
+ #clock-cells = <0>;
+ clock-div = <4>;
+ clock-mult = <1>;
+ clocks = <&pll400>;
+ };
+
+ armclk: armclk {
+ compatible = "fixed-factor-clock";
+ #clock-cells = <0>;
+ clock-div = <2>;
+ clock-mult = <1>;
+ clocks = <&pll400>;
+ };
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges;
+ interrupt-parent = <&intc>;
+
+ apb-bridge@44000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges = <0 0x44000000 0x1000000>;
+
+ pinctrl: pinctrl {
+ compatible = "oxsemi,ox810se-pinctrl", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ /* Regmap for sys registers */
+ plxtech,sys-ctrl = <&sys>;
+
+ /* Default, all-open mux-map */
+ plxtech,mux-mask = <
+ 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
+ 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
+ >;
+
+ gpio0: gpio@000000 {
+ compatible = "oxsemi,ox810se-gpio";
+ reg = <0x000000 0x100000>;
+ interrupts = <21>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #gpio-lines = <32>;
+ };
+
+ gpio1: gpio@100000 {
+ compatible = "oxsemi,ox810se-gpio";
+ reg = <0x100000 0x100000>;
+ interrupts = <22>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #gpio-lines = <3>;
+ };
+
+ uart0 {
+ pinctrl_uart0: uart0 {
+ plxtech,pins = <0 31 3 0
+ 0 32 3 0>;
+ };
+ pinctrl_uart0_modem: uart0_modem {
+ plxtech,pins = <0 27 3 0
+ 0 28 3 0
+ 0 29 3 0
+ 0 30 3 0
+ 0 33 3 0
+ 0 34 3 0>;
+ };
+ };
+
+ uart1 {
+ pinctrl_uart1: uart1 {
+ plxtech,pins = <0 20 3 0
+ 0 22 3 0>;
+ };
+ pinctrl_uart1_modem: uart1_modem {
+ plxtech,pins = <0 8 3 0
+ 0 9 3 0
+ 0 23 3 0
+ 0 24 3 0
+ 0 25 3 0
+ 0 26 3 0>;
+ };
+ };
+
+ uart2 {
+ pinctrl_uart2: uart2 {
+ plxtech,pins = <0 6 3 0
+ 0 7 3 0>;
+ };
+ pinctrl_uart2_modem: uart2_modem {
+ plxtech,pins = <0 0 3 0
+ 0 1 3 0
+ 0 2 3 0
+ 0 3 3 0
+ 0 4 3 0
+ 0 5 3 0>;
+ };
+ };
+ };
+
+ uart0: serial@200000 {
+ compatible = "ns16550a";
+ reg = <0x200000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <23>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 17>;
+ };
+
+ uart1: serial@300000 {
+ compatible = "ns16550a";
+ reg = <0x300000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <24>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 18>;
+ };
+
+ uart2: serial@900000 {
+ compatible = "ns16550a";
+ reg = <0x900000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <29>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 22>;
+ };
+
+ uart3: serial@a00000 {
+ compatible = "ns16550a";
+ reg = <0xa00000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <30>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 23>;
+ };
+ };
+
+ apb-bridge@45000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges = <0 0x45000000 0x1000000>;
+
+ sys: sys-ctrl@000000 {
+ compatible = "oxsemi,ox810se-sys-ctrl", "syscon", "simple-mfd";
+ reg = <0x000000 0x100000>;
+
+ reset: reset-controller {
+ compatible = "oxsemi,ox810se-reset";
+ #reset-cells = <1>;
+ };
+
+ stdclk: stdclk {
+ compatible = "oxsemi,ox810se-stdclk";
+ #clock-cells = <1>;
+ };
+ };
+
+ rps@300000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges = <0 0x300000 0x100000>;
+
+ intc: interrupt-controller@0 {
+ compatible = "arm,rps-irq";
+ interrupt-controller;
+ reg = <0 0x200>;
+ #interrupt-cells = <1>;
+ valid-mask = <0xFFFFFFFF>;
+ clear-mask = <0>;
+ };
+
+ timer0: timer@200 {
+ compatible = "arm,sp804";
+ reg = <0x200 0x40>;
+ clocks = <&rpsclk>;
+ interrupts = <4 5>;
+ arm,timer-width = <24>;
+ };
+ };
+ };
+ };
+};
--
1.9.1

2016-03-09 10:27:10

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 18/18] arm: boot: dts: Add Western Digital My Book World Edition device tree

Add Western Digital My Book World Edition device tree based on
PLX Technology OX810SE SoC.

Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm/boot/dts/Makefile | 2 +
arch/arm/boot/dts/wd-mbwe.dts | 112 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 114 insertions(+)
create mode 100644 arch/arm/boot/dts/wd-mbwe.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index a4a6d70..0395674 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -520,6 +520,8 @@ dtb-$(CONFIG_ARCH_ORION5X) += \
orion5x-rd88f5182-nas.dtb
dtb-$(CONFIG_ARCH_PRIMA2) += \
prima2-evb.dtb
+dtb-$(CONFIG_ARCH_OXNAS) += \
+ wd-mbwe.dtb
dtb-$(CONFIG_ARCH_QCOM) += \
qcom-apq8064-cm-qs600.dtb \
qcom-apq8064-ifc6410.dtb \
diff --git a/arch/arm/boot/dts/wd-mbwe.dts b/arch/arm/boot/dts/wd-mbwe.dts
new file mode 100644
index 0000000..ac3250a
--- /dev/null
+++ b/arch/arm/boot/dts/wd-mbwe.dts
@@ -0,0 +1,112 @@
+/*
+ * wd-mbwe.dtsi - Device tree file for Western Digital My Book World Edition
+ *
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/dts-v1/;
+#include "ox810se.dtsi"
+
+/ {
+ model = "Western Digital My Book World Edition";
+
+ compatible = "wd,mbwe", "oxsemi,ox810se";
+
+ chosen {
+ bootargs = "console=ttyS1,115200n8 earlyprintk=serial";
+ };
+
+ memory {
+ /* 128Mbytes DDR */
+ reg = <0x48000000 0x8000000>;
+ };
+
+ aliases {
+ serial1 = &uart1;
+ gpio0 = &gpio0;
+ gpio1 = &gpio1;
+ };
+
+ gpio-keys-polled {
+ compatible = "gpio-keys-polled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ poll-interval = <100>;
+
+ power {
+ label = "power";
+ gpios = <&gpio0 0 1>;
+ linux,code = <0x198>;
+ };
+
+ recovery {
+ label = "recovery";
+ gpios = <&gpio0 4 1>;
+ linux,code = <0xab>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ a0 {
+ label = "activity0";
+ gpios = <&gpio0 25 0>;
+ default-state = "keep";
+ };
+
+ a1 {
+ label = "activity1";
+ gpios = <&gpio0 26 0>;
+ default-state = "keep";
+ };
+
+ a2 {
+ label = "activity2";
+ gpios = <&gpio0 5 0>;
+ default-state = "keep";
+ };
+
+ a3 {
+ label = "activity3";
+ gpios = <&gpio0 6 0>;
+ default-state = "keep";
+ };
+
+ a4 {
+ label = "activity4";
+ gpios = <&gpio0 7 0>;
+ default-state = "keep";
+ };
+
+ a5 {
+ label = "activity5";
+ gpios = <&gpio1 2 0>;
+ default-state = "keep";
+ };
+ };
+
+ i2c-gpio {
+ compatible = "i2c-gpio";
+ gpios = <&gpio0 3 0 /* sda */
+ &gpio0 2 0 /* scl */
+ >;
+ i2c-gpio,delay-us = <2>; /* ~100 kHz */
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ rtc0: rtc@48 {
+ compatible = "st,m41t00";
+ reg = <0x68>;
+ };
+ };
+};
+
+&uart1 {
+ status = "okay";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart1>;
+};
--
1.9.1

2016-03-09 10:27:15

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 12/18] dt-bindings: Add PLX Technology OXNAS pinctrl and gpio bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
.../devicetree/bindings/gpio/gpio_oxnas.txt | 27 ++++++
.../bindings/pinctrl/plxtech,pinctrl.txt | 100 +++++++++++++++++++++
2 files changed, 127 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
create mode 100644 Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt

diff --git a/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt b/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
new file mode 100644
index 0000000..cbb03c4
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
@@ -0,0 +1,27 @@
+PLX Technology OXNAS SoC GPIO Controller
+==========================================
+
+Required properties:
+- compatible: "oxsemi,ox810se-gpio".
+- reg: Should contain GPIO controller registers location and length
+- interrupts: Should be the port interrupt shared by all the pins.
+- #gpio-cells: Should be two. The first cell is the pin number and
+ the second cell is used to specify optional parameters (currently
+ unused).
+- gpio-controller: Marks the device node as a GPIO controller.
+
+optional properties:
+- #gpio-lines: Number of gpio if absent 32.
+
+
+Example:
+ gpio0: gpio@000000 {
+ compatible = "oxsemi,ox810se-gpio";
+ reg = <0x000000 0x100000>;
+ interrupts = <21>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #gpio-lines = <32>;
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt
new file mode 100644
index 0000000..0c5051a
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt
@@ -0,0 +1,100 @@
+PLX Technology OXNAS SoC Pinmux Controller
+==========================================
+
+The OXNAS Pinmux Controller, enables the IC to share one PAD to several
+functional blocks. The sharing is done by multiplexing the PAD input/output
+signals. For each PAD there are up to 8 muxing options (called periph modes).
+Since different modules require different PAD settings
+(like pull up, keeper, etc) the contoller controls also the PAD settings
+parameters.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+OXNAS pin configuration node is a node of a group of pins which can be
+used for a specific device or function. This node represents both mux and config
+of the pins in that group. The 'pins' selects the function mode(also named pin
+mode) this pin can work on and the 'config' configures various pad settings
+such as pull-up, multi drive, etc.
+
+Required properties for iomux controller:
+- compatible: "oxsemi,ox810se-pinctrl"
+- plxtech,mux-mask: array of mask (periph per bank) to describe if a pin can be
+ configured in this periph mode. All the periph and bank need to be describe.
+- plxtech,sys-ctrl: a phandle to the system controller syscon node
+
+How to create such array:
+
+Each column will represent the possible peripheral of the pinctrl
+Each line will represent a pio bank
+
+For example :
+Peripheral: 2 ( A and B)
+Bank: 2 (A, B and C)
+=>
+
+ /* A B */
+ 0xffffffff 0xffc00c3b /* pioA */
+ 0xffffffff 0x7fff3ccf /* pioB */
+
+For each peripheral/bank we will descibe in a u32 if a pin can be
+configured in it by putting 1 to the pin bit (1 << pin)
+
+Required properties for pin configuration node:
+- plxtech,pins: 4 integers array, represents a group of pins mux and config
+ setting. The format is plxtech,pins = <PIN_BANK PIN_BANK_NUM PERIPH CONFIG>.
+ The PERIPH 0 means gpio, PERIPH 1 is periph A, PERIPH 2 is periph B...
+ PIN_BANK 0 is pioA, PIN_BANK 1 is pioB...
+
+Bits used for CONFIG:
+ - None Yet
+
+Examples:
+
+pinctrl: pinctrl {
+ compatible = "oxsemi,ox810se-pinctrl", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ /* Regmap for sys registers */
+ plxtech,sys-ctrl = <&sys>;
+
+ /* Default, all-open mux-map */
+ plxtech,mux-mask = <
+ 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
+ 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
+ >;
+
+ uart0 {
+ pinctrl_uart0: uart0 {
+ plxtech,pins = <0 31 3 0
+ 0 32 3 0>;
+ };
+ pinctrl_uart0_modem: uart0_modem {
+ plxtech,pins = <0 27 3 0
+ 0 28 3 0
+ 0 29 3 0
+ 0 30 3 0
+ 0 33 3 0
+ 0 34 3 0>;
+ };
+ };
+};
+
+uart0: uart@200000 {
+ compatible = "ns16550a";
+ reg = <0x200000 0x100000>;
+ clocks = <&sysclk>;
+ interrupts = <23>;
+ reg-shift = <0>;
+ fifo-size = <16>;
+ reg-io-width = <1>;
+ current-speed = <115200>;
+ no-loopback-test;
+ status = "disabled";
+ resets = <&reset 17>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_uart0>;
+};
--
1.9.1

2016-03-09 10:27:23

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 11/18] pinctrl: Add PLX Technology OXNAS pinctrl and gpio driver

Add pinctrl and gprio control support to PLX Technology OXNAS SoC Family

CC: Ma Haijun <[email protected]>
CC: Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/pinctrl/Kconfig | 9 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-oxnas.c | 1392 +++++++++++++++++++++++++++++++++++++++
3 files changed, 1402 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-oxnas.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 99a4c10..1d0c513 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -129,6 +129,15 @@ config PINCTRL_MESON
select OF_GPIO
select REGMAP_MMIO

+config PINCTRL_OXNAS
+ bool
+ depends on OF
+ select PINMUX
+ select PINCONF
+ select GPIOLIB
+ select OF_GPIO
+ select MFD_SYSCON
+
config PINCTRL_ROCKCHIP
bool
select PINMUX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index bf1b5ca..3351d10 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_PINCTRL_AMD) += pinctrl-amd.o
obj-$(CONFIG_PINCTRL_DIGICOLOR) += pinctrl-digicolor.o
obj-$(CONFIG_PINCTRL_FALCON) += pinctrl-falcon.o
obj-$(CONFIG_PINCTRL_MESON) += meson/
+obj-$(CONFIG_PINCTRL_OXNAS) += pinctrl-oxnas.o
obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o
obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
diff --git a/drivers/pinctrl/pinctrl-oxnas.c b/drivers/pinctrl/pinctrl-oxnas.c
new file mode 100644
index 0000000..f35032a
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-oxnas.c
@@ -0,0 +1,1392 @@
+/*
+ * PLX Technology OXNAS Pinctrl driver based on at91 pinctrl driver
+ *
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ * Copyright (C) 2013 Ma Hajun <[email protected]>
+ * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+/* Since we request GPIOs from ourself */
+#include <linux/pinctrl/consumer.h>
+#include <linux/version.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "core.h"
+
+#define MAX_NB_GPIO_PER_BANK 32
+#define MAX_GPIO_BANKS 2
+
+struct oxnas_gpio_chip {
+ struct gpio_chip chip;
+ struct pinctrl_gpio_range range;
+ void __iomem *regbase; /* GPIOA/B virtual address */
+ struct irq_domain *domain; /* associated irq domain */
+ struct regmap *regmap;
+};
+
+#define to_oxnas_gpio_chip(c) container_of(c, struct oxnas_gpio_chip, chip)
+
+static struct oxnas_gpio_chip *gpio_chips[MAX_GPIO_BANKS];
+
+static int gpio_banks;
+
+/**
+ * struct oxnas_pmx_func - describes pinmux functions
+ * @name: the name of this specific function
+ * @groups: corresponding pin groups
+ * @ngroups: the number of groups
+ */
+struct oxnas_pmx_func {
+ const char *name;
+ const char **groups;
+ unsigned ngroups;
+};
+
+enum oxnas_mux {
+ OXNAS_PINMUX_GPIO,
+ OXNAS_PINMUX_FUNC1,
+ OXNAS_PINMUX_FUNC2,
+ OXNAS_PINMUX_FUNC3,
+};
+
+enum {
+ INPUT_VALUE = 0,
+ OUTPUT_ENABLE = 4,
+ IRQ_PENDING = 0xC,
+ OUTPUT_VALUE = 0x10,
+ OUTPUT_SET = 0x14,
+ OUTPUT_CLEAR = 0x18,
+ OUTPUT_EN_SET = 0x1C,
+ OUTPUT_EN_CLEAR = 0x20,
+ RE_IRQ_ENABLE = 0x28, /* rising edge */
+ FE_IRQ_ENABLE = 0x2C, /* falling edge */
+ RE_IRQ_PENDING = 0x30, /* rising edge */
+ FE_IRQ_PENDING = 0x34, /* falling edge */
+};
+
+enum {
+ PINMUX_PRIMARY_SEL0 = 0x0c,
+ PINMUX_PRIMARY_SEL1 = 0x10,
+ PINMUX_SECONDARY_SEL0 = 0x14,
+ PINMUX_SECONDARY_SEL1 = 0x18,
+ PINMUX_TERTIARY_SEL0 = 0x8c,
+ PINMUX_TERTIARY_SEL1 = 0x90,
+};
+
+/**
+ * struct oxnas_pmx_pin - describes an pin mux
+ * @bank: the bank of the pin
+ * @pin: the pin number in the @bank
+ * @mux: the mux mode : gpio or periph_x of the pin i.e. alternate function.
+ * @conf: the configuration of the pin: PULL_UP, MULTIDRIVE etc...
+ */
+struct oxnas_pmx_pin {
+ uint32_t bank;
+ uint32_t pin;
+ enum oxnas_mux mux;
+ unsigned long conf;
+};
+
+/**
+ * struct oxnas_pin_group - describes an pin group
+ * @name: the name of this specific pin group
+ * @pins_conf: the mux mode for each pin in this group. The size of this
+ * array is the same as pins.
+ * @pins: an array of discrete physical pins used in this group, taken
+ * from the driver-local pin enumeration space
+ * @npins: the number of pins in this group array, i.e. the number of
+ * elements in .pins so we can iterate over that array
+ */
+struct oxnas_pin_group {
+ const char *name;
+ struct oxnas_pmx_pin *pins_conf;
+ unsigned int *pins;
+ unsigned npins;
+};
+
+struct oxnas_pinctrl {
+ struct device *dev;
+ struct pinctrl_dev *pctl;
+ struct regmap *regmap;
+
+ int nbanks;
+
+ uint32_t *mux_mask;
+ int nmux;
+
+ struct oxnas_pmx_func *functions;
+ int nfunctions;
+
+ struct oxnas_pin_group *groups;
+ int ngroups;
+};
+
+static const inline struct oxnas_pin_group *oxnas_pinctrl_find_group_by_name(
+ const struct oxnas_pinctrl *info,
+ const char *name)
+{
+ const struct oxnas_pin_group *grp = NULL;
+ int i;
+
+ for (i = 0; i < info->ngroups; i++) {
+ if (strcmp(info->groups[i].name, name))
+ continue;
+
+ grp = &info->groups[i];
+ dev_dbg(info->dev, "%s: %d 0:%d\n", name, grp->npins,
+ grp->pins[0]);
+ break;
+ }
+
+ return grp;
+}
+
+static int oxnas_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->ngroups;
+}
+
+static const char *oxnas_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->groups[selector].name;
+}
+
+static int oxnas_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
+ const unsigned **pins,
+ unsigned *npins)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ if (selector >= info->ngroups)
+ return -EINVAL;
+
+ *pins = info->groups[selector].pins;
+ *npins = info->groups[selector].npins;
+
+ return 0;
+}
+
+static void oxnas_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
+ unsigned offset)
+{
+ seq_printf(s, "%s", dev_name(pctldev->dev));
+}
+
+static int oxnas_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ const struct oxnas_pin_group *grp;
+ struct pinctrl_map *new_map;
+ struct device_node *parent;
+ int map_num = 1;
+ int i;
+
+ /*
+ * first find the group of this node and check if we need create
+ * config maps for pins
+ */
+ grp = oxnas_pinctrl_find_group_by_name(info, np->name);
+ if (!grp) {
+ dev_err(info->dev, "unable to find group for node %s\n",
+ np->name);
+ return -EINVAL;
+ }
+
+ map_num += grp->npins;
+ new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num,
+ GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+
+ *map = new_map;
+ *num_maps = map_num;
+
+ /* create mux map */
+ parent = of_get_parent(np);
+ if (!parent) {
+ devm_kfree(pctldev->dev, new_map);
+ return -EINVAL;
+ }
+ new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
+ new_map[0].data.mux.function = parent->name;
+ new_map[0].data.mux.group = np->name;
+ of_node_put(parent);
+
+ /* create config map */
+ new_map++;
+ for (i = 0; i < grp->npins; i++) {
+ new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
+ new_map[i].data.configs.group_or_pin =
+ pin_get_name(pctldev, grp->pins[i]);
+ new_map[i].data.configs.configs = &grp->pins_conf[i].conf;
+ new_map[i].data.configs.num_configs = 1;
+ }
+
+ dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",
+ (*map)->data.mux.function, (*map)->data.mux.group, map_num);
+
+ return 0;
+}
+
+static void oxnas_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+}
+
+static const struct pinctrl_ops oxnas_pctrl_ops = {
+ .get_groups_count = oxnas_get_groups_count,
+ .get_group_name = oxnas_get_group_name,
+ .get_group_pins = oxnas_get_group_pins,
+ .pin_dbg_show = oxnas_pin_dbg_show,
+ .dt_node_to_map = oxnas_dt_node_to_map,
+ .dt_free_map = oxnas_dt_free_map,
+};
+
+static void __iomem *pin_to_gpioctrl(struct oxnas_pinctrl *info,
+ unsigned int bank)
+{
+ return gpio_chips[bank]->regbase;
+}
+
+static inline int pin_to_bank(unsigned pin)
+{
+ return pin / MAX_NB_GPIO_PER_BANK;
+}
+
+static unsigned pin_to_mask(unsigned int pin)
+{
+ return 1 << pin;
+}
+
+static void oxnas_mux_disable_interrupt(void __iomem *pio, unsigned mask)
+{
+ writel(readl(pio + RE_IRQ_ENABLE) & ~mask, pio + RE_IRQ_ENABLE);
+ writel(readl(pio + FE_IRQ_ENABLE) & ~mask, pio + FE_IRQ_ENABLE);
+}
+
+static void oxnas_mux_set_func1(struct oxnas_pinctrl *ctrl,
+ unsigned bank, unsigned mask)
+{
+ if (!bank) {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL0, mask, mask);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, 0);
+ } else {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL1, mask, mask);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, 0);
+ }
+}
+
+static void oxnas_mux_set_func2(struct oxnas_pinctrl *ctrl,
+ unsigned bank, unsigned mask)
+{
+ if (!bank) {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL0, mask, mask);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL0, mask, 0);
+ } else {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL1, mask, mask);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, 0);
+ }
+}
+
+static void oxnas_mux_set_func3(struct oxnas_pinctrl *ctrl,
+ unsigned bank, unsigned mask)
+{
+ if (!bank) {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL0, mask, mask);
+ } else {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, mask);
+ }
+}
+
+static void oxnas_mux_set_gpio(struct oxnas_pinctrl *ctrl,
+ unsigned bank, unsigned mask)
+{
+ if (!bank) {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL0, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL0, mask, 0);
+ } else {
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_PRIMARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_SECONDARY_SEL1, mask, 0);
+ regmap_write_bits(ctrl->regmap,
+ PINMUX_TERTIARY_SEL1, mask, 0);
+ }
+}
+
+static enum oxnas_mux oxnas_mux_get_func(struct regmap *regmap,
+ unsigned bank, unsigned mask)
+{
+ unsigned int val;
+
+ if (!bank) {
+ if (!regmap_read(regmap, PINMUX_PRIMARY_SEL0, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC1;
+ if (!regmap_read(regmap, PINMUX_SECONDARY_SEL0, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC2;
+ if (!regmap_read(regmap, PINMUX_TERTIARY_SEL0, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC3;
+ } else {
+ if (!regmap_read(regmap, PINMUX_PRIMARY_SEL1, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC1;
+ if (!regmap_read(regmap, PINMUX_SECONDARY_SEL1, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC2;
+ if (!regmap_read(regmap, PINMUX_TERTIARY_SEL1, &val) &&
+ (val & mask))
+ return OXNAS_PINMUX_FUNC3;
+ }
+
+ return OXNAS_PINMUX_GPIO;
+}
+
+static void oxnas_pin_dbg(const struct device *dev,
+ const struct oxnas_pmx_pin *pin)
+{
+ if (pin->mux) {
+ dev_dbg(dev,
+ "MF_%c%d configured as periph%c with conf = %lu\n",
+ pin->bank + 'A', pin->pin, pin->mux - 1 + 'A',
+ pin->conf);
+ } else {
+ dev_dbg(dev, "MF_%c%d configured as gpio with conf = %lu\n",
+ pin->bank + 'A', pin->pin, pin->conf);
+ }
+}
+
+static int pin_check_config(struct oxnas_pinctrl *info, const char *name,
+ int index, const struct oxnas_pmx_pin *pin)
+{
+ int mux;
+
+ /* check if it's a valid config */
+ if (pin->bank >= info->nbanks) {
+ dev_err(info->dev, "%s: pin conf %d bank_id %d >= nbanks %d\n",
+ name, index, pin->bank, info->nbanks);
+ return -EINVAL;
+ }
+
+ if (pin->pin >= MAX_NB_GPIO_PER_BANK) {
+ dev_err(info->dev, "%s: pin conf %d pin_bank_id %d >= %d\n",
+ name, index, pin->pin, MAX_NB_GPIO_PER_BANK);
+ return -EINVAL;
+ }
+ /* gpio always allowed */
+ if (!pin->mux)
+ return 0;
+
+ mux = pin->mux - 1;
+
+ if (mux >= info->nmux) {
+ dev_err(info->dev, "%s: pin conf %d mux_id %d >= nmux %d\n",
+ name, index, mux, info->nmux);
+ return -EINVAL;
+ }
+
+ if (!(info->mux_mask[pin->bank * info->nmux + mux] & 1 << pin->pin)) {
+ dev_err(info->dev, "%s: pin conf %d mux_id %d not supported for MF_%c%d\n",
+ name, index, mux, pin->bank + 'A', pin->pin);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void oxnas_mux_gpio_enable(struct oxnas_pinctrl *ctrl,
+ int bank,
+ void __iomem *pio,
+ unsigned mask, bool input)
+{
+ oxnas_mux_set_gpio(ctrl, bank, mask);
+
+ if (input)
+ writel_relaxed(mask, pio + OUTPUT_EN_CLEAR);
+ else
+ writel_relaxed(mask, pio + OUTPUT_EN_SET);
+}
+
+static void oxnas_mux_gpio_disable(struct oxnas_pinctrl *ctrl,
+ int bank,
+ unsigned mask)
+{
+ /* when switch to other function, gpio is disabled automatically */
+}
+
+static int oxnas_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned group)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ const struct oxnas_pmx_pin *pins_conf = info->groups[group].pins_conf;
+ const struct oxnas_pmx_pin *pin;
+ uint32_t npins = info->groups[group].npins;
+ int i, ret;
+ unsigned mask;
+ void __iomem *pio;
+
+ dev_dbg(info->dev, "enable function %s group %s\n",
+ info->functions[selector].name, info->groups[group].name);
+
+ /* first check that all the pins of the group are valid with a valid
+ * parameter
+ */
+ for (i = 0; i < npins; i++) {
+ pin = &pins_conf[i];
+ ret = pin_check_config(info, info->groups[group].name, i, pin);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < npins; i++) {
+ pin = &pins_conf[i];
+ oxnas_pin_dbg(info->dev, pin);
+
+ pio = pin_to_gpioctrl(info, pin->bank);
+
+ mask = pin_to_mask(pin->pin);
+ oxnas_mux_disable_interrupt(pio, mask);
+
+ switch (pin->mux) {
+ case OXNAS_PINMUX_GPIO:
+ oxnas_mux_gpio_enable(info, pin->bank, pio, mask, 1);
+ break;
+ case OXNAS_PINMUX_FUNC1:
+ oxnas_mux_set_func1(info, pin->bank, mask);
+ break;
+ case OXNAS_PINMUX_FUNC2:
+ oxnas_mux_set_func2(info, pin->bank, mask);
+ break;
+ case OXNAS_PINMUX_FUNC3:
+ oxnas_mux_set_func3(info, pin->bank, mask);
+ break;
+ }
+ if (pin->mux)
+ oxnas_mux_gpio_disable(info, pin->bank, mask);
+ }
+
+ return 0;
+}
+
+static int oxnas_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->nfunctions;
+}
+
+static const char *oxnas_pmx_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->functions[selector].name;
+}
+
+static int oxnas_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct oxnas_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = info->functions[selector].groups;
+ *num_groups = info->functions[selector].ngroups;
+
+ return 0;
+}
+
+static int oxnas_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+ struct oxnas_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
+ struct oxnas_gpio_chip *oxnas_chip;
+ struct gpio_chip *chip;
+ unsigned mask;
+
+ if (!range) {
+ dev_err(npct->dev, "invalid range\n");
+ return -EINVAL;
+ }
+ if (!range->gc) {
+ dev_err(npct->dev, "missing GPIO chip in range\n");
+ return -EINVAL;
+ }
+ chip = range->gc;
+ oxnas_chip = container_of(chip, struct oxnas_gpio_chip, chip);
+
+ dev_dbg(npct->dev, "enable pin %u as GPIO\n", offset);
+
+ mask = 1 << (offset - chip->base);
+
+ dev_dbg(npct->dev, "enable pin %u as MF_%c%d 0x%x\n",
+ offset, 'A' + range->id, offset - chip->base, mask);
+
+ oxnas_mux_set_gpio(npct, range->id, mask);
+
+ return 0;
+}
+
+static void oxnas_gpio_disable_free(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+ struct oxnas_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev);
+
+ dev_dbg(npct->dev, "disable pin %u as GPIO\n", offset);
+
+ /* Set the pin to some default state, GPIO is usually default */
+}
+
+static const struct pinmux_ops oxnas_pmx_ops = {
+ .get_functions_count = oxnas_pmx_get_funcs_count,
+ .get_function_name = oxnas_pmx_get_func_name,
+ .get_function_groups = oxnas_pmx_get_groups,
+ .set_mux = oxnas_pmx_set_mux,
+ .gpio_request_enable = oxnas_gpio_request_enable,
+ .gpio_disable_free = oxnas_gpio_disable_free,
+};
+
+static int oxnas_pinconf_get(struct pinctrl_dev *pctldev,
+ unsigned pin_id, unsigned long *config)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+
+static int oxnas_pinconf_set(struct pinctrl_dev *pctldev,
+ unsigned pin_id, unsigned long *configs,
+ unsigned num_configs)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+
+static void oxnas_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned pin_id)
+{
+
+}
+
+static void oxnas_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned group)
+{
+}
+
+static const struct pinconf_ops oxnas_pinconf_ops = {
+ .pin_config_get = oxnas_pinconf_get,
+ .pin_config_set = oxnas_pinconf_set,
+ .pin_config_dbg_show = oxnas_pinconf_dbg_show,
+ .pin_config_group_dbg_show = oxnas_pinconf_group_dbg_show,
+};
+
+static struct pinctrl_desc oxnas_pinctrl_desc = {
+ .pctlops = &oxnas_pctrl_ops,
+ .pmxops = &oxnas_pmx_ops,
+ .confops = &oxnas_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+static const char *gpio_compat = "oxsemi,ox810se-gpio";
+
+static void oxnas_pinctrl_child_count(struct oxnas_pinctrl *info,
+ struct device_node *np)
+{
+ struct device_node *child;
+
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, gpio_compat)) {
+ info->nbanks++;
+ } else {
+ info->nfunctions++;
+ info->ngroups += of_get_child_count(child);
+ }
+ }
+}
+
+static int oxnas_pinctrl_mux_mask(struct oxnas_pinctrl *info,
+ struct device_node *np)
+{
+ int ret = 0;
+ int size;
+ const __be32 *list;
+
+ list = of_get_property(np, "plxtech,mux-mask", &size);
+ if (!list) {
+ dev_err(info->dev, "can not read the mux-mask of %d\n", size);
+ return -EINVAL;
+ }
+
+ size /= sizeof(*list);
+ if (!size || size % info->nbanks) {
+ dev_err(info->dev, "wrong mux mask array should be by %d\n",
+ info->nbanks);
+ return -EINVAL;
+ }
+ info->nmux = size / info->nbanks;
+
+ info->mux_mask = devm_kzalloc(info->dev, sizeof(u32) * size,
+ GFP_KERNEL);
+ if (!info->mux_mask)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "plxtech,mux-mask",
+ info->mux_mask, size);
+ if (ret)
+ dev_err(info->dev, "can not read the mux-mask of %d\n", size);
+ return ret;
+}
+
+static int oxnas_pinctrl_parse_groups(struct device_node *np,
+ struct oxnas_pin_group *grp,
+ struct oxnas_pinctrl *info, u32 index)
+{
+ struct oxnas_pmx_pin *pin;
+ int size;
+ const __be32 *list;
+ int i, j;
+
+ dev_dbg(info->dev, "group(%d): %s\n", index, np->name);
+
+ /* Initialise group */
+ grp->name = np->name;
+
+ /*
+ * the binding format is plxtech,pins = <bank pin mux CONFIG ...>,
+ * do sanity check and calculate pins number
+ */
+ list = of_get_property(np, "plxtech,pins", &size);
+ /* we do not check return since it's safe node passed down */
+ size /= sizeof(*list);
+ if (!size || size % 4) {
+ dev_err(info->dev, "wrong pins number or pins and configs should be divisible by 4\n");
+ return -EINVAL;
+ }
+
+ grp->npins = size / 4;
+ pin = grp->pins_conf = devm_kzalloc(info->dev,
+ grp->npins * sizeof(struct oxnas_pmx_pin),
+ GFP_KERNEL);
+ grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!grp->pins_conf || !grp->pins)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < size; i += 4, j++) {
+ pin->bank = be32_to_cpu(*list++);
+ pin->pin = be32_to_cpu(*list++);
+ grp->pins[j] = pin->bank * MAX_NB_GPIO_PER_BANK + pin->pin;
+ pin->mux = be32_to_cpu(*list++);
+ pin->conf = be32_to_cpu(*list++);
+
+ oxnas_pin_dbg(info->dev, pin);
+ pin++;
+ }
+
+ return 0;
+}
+
+static int oxnas_pinctrl_parse_functions(struct device_node *np,
+ struct oxnas_pinctrl *info, u32 index)
+{
+ struct device_node *child;
+ struct oxnas_pmx_func *func;
+ struct oxnas_pin_group *grp;
+ int ret;
+ static u32 grp_index;
+ u32 i = 0;
+
+ dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name);
+
+ func = &info->functions[index];
+
+ /* Initialise function */
+ func->name = np->name;
+ func->ngroups = of_get_child_count(np);
+ if (func->ngroups <= 0) {
+ dev_err(info->dev, "no groups defined\n");
+ return -EINVAL;
+ }
+ func->groups = devm_kzalloc(info->dev,
+ func->ngroups * sizeof(char *), GFP_KERNEL);
+ if (!func->groups)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, child) {
+ func->groups[i] = child->name;
+ grp = &info->groups[grp_index++];
+ ret = oxnas_pinctrl_parse_groups(child, grp, info, i++);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id oxnas_pinctrl_of_match[] = {
+ { .compatible = "oxsemi,ox810se-pinctrl"},
+ { /* sentinel */ }
+};
+
+static int oxnas_pinctrl_probe_dt(struct platform_device *pdev,
+ struct oxnas_pinctrl *info)
+{
+ int ret = 0;
+ int i, j;
+ uint32_t *tmp;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+
+ if (!np)
+ return -ENODEV;
+
+ info->dev = &pdev->dev;
+
+ oxnas_pinctrl_child_count(info, np);
+
+ if (info->nbanks < 1) {
+ dev_err(&pdev->dev, "you need to specify atleast one gpio-controller\n");
+ return -EINVAL;
+ }
+
+ ret = oxnas_pinctrl_mux_mask(info, np);
+ if (ret)
+ return ret;
+
+ dev_dbg(&pdev->dev, "nmux = %d\n", info->nmux);
+
+ dev_dbg(&pdev->dev, "mux-mask\n");
+ tmp = info->mux_mask;
+ for (i = 0; i < info->nbanks; i++)
+ for (j = 0; j < info->nmux; j++, tmp++)
+ dev_dbg(&pdev->dev, "%d:%d\t0x%x\n", i, j, tmp[0]);
+
+ dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
+ dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups);
+ info->functions = devm_kzalloc(&pdev->dev, info->nfunctions *
+ sizeof(struct oxnas_pmx_func),
+ GFP_KERNEL);
+ if (!info->functions)
+ return -ENOMEM;
+
+ info->groups = devm_kzalloc(&pdev->dev, info->ngroups *
+ sizeof(struct oxnas_pin_group),
+ GFP_KERNEL);
+ if (!info->groups)
+ return -ENOMEM;
+
+ dev_dbg(&pdev->dev, "nbanks = %d\n", info->nbanks);
+ dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
+ dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups);
+
+ i = 0;
+
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, gpio_compat))
+ continue;
+ ret = oxnas_pinctrl_parse_functions(child, info, i++);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse function\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int oxnas_pinctrl_probe(struct platform_device *pdev)
+{
+ struct oxnas_pinctrl *info;
+ struct pinctrl_pin_desc *pdesc;
+ int ret, i, j, k;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "plxtech,sys-ctrl");
+ if (IS_ERR(info->regmap)) {
+ dev_err(&pdev->dev, "failed to get sys ctrl regmap\n");
+ return -ENODEV;
+ }
+
+ ret = oxnas_pinctrl_probe_dt(pdev, info);
+ if (ret)
+ return ret;
+
+ /*
+ * We need all the GPIO drivers to probe FIRST, or we will not be able
+ * to obtain references to the struct gpio_chip * for them, and we
+ * need this to proceed.
+ */
+ for (i = 0; i < info->nbanks; i++) {
+ if (!gpio_chips[i]) {
+ dev_warn(&pdev->dev,
+ "GPIO chip %d not registered yet\n", i);
+ devm_kfree(&pdev->dev, info);
+ return -EPROBE_DEFER;
+ }
+ }
+
+ oxnas_pinctrl_desc.name = dev_name(&pdev->dev);
+ oxnas_pinctrl_desc.npins = info->nbanks * MAX_NB_GPIO_PER_BANK;
+ oxnas_pinctrl_desc.pins = pdesc =
+ devm_kzalloc(&pdev->dev, sizeof(*pdesc) *
+ oxnas_pinctrl_desc.npins, GFP_KERNEL);
+
+ if (!oxnas_pinctrl_desc.pins)
+ return -ENOMEM;
+
+ for (i = 0, k = 0; i < info->nbanks; i++) {
+ for (j = 0; j < MAX_NB_GPIO_PER_BANK; j++, k++) {
+ pdesc->number = k;
+ pdesc->name = kasprintf(GFP_KERNEL, "MF_%c%d", i + 'A',
+ j);
+ pdesc++;
+ }
+ }
+
+ platform_set_drvdata(pdev, info);
+ info->pctl = pinctrl_register(&oxnas_pinctrl_desc, &pdev->dev, info);
+
+ if (!info->pctl) {
+ dev_err(&pdev->dev, "could not register OXNAS pinctrl driver\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* We will handle a range of GPIO pins */
+ for (i = 0; i < info->nbanks; i++)
+ pinctrl_add_gpio_range(info->pctl, &gpio_chips[i]->range);
+
+ dev_info(&pdev->dev, "initialized OXNAS pinctrl driver\n");
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int oxnas_pinctrl_remove(struct platform_device *pdev)
+{
+ struct oxnas_pinctrl *info = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(info->pctl);
+
+ return 0;
+}
+
+static int oxnas_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ /*
+ * Map back to global GPIO space and request muxing, the direction
+ * parameter does not matter for this controller.
+ */
+ int gpio = chip->base + offset;
+ int bank = chip->base / chip->ngpio;
+
+ dev_dbg(chip->parent, "%s:%d MF_%c%d(%d)\n", __func__, __LINE__,
+ 'A' + bank, offset, gpio);
+
+ return pinctrl_request_gpio(gpio);
+}
+
+static void oxnas_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+
+ pinctrl_free_gpio(gpio);
+}
+
+static int oxnas_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+
+ writel_relaxed(BIT(offset), pio + OUTPUT_EN_CLEAR);
+ return 0;
+}
+
+static int oxnas_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+ unsigned mask = 1 << offset;
+ u32 pdsr = 0;
+
+ pdsr = readl_relaxed(pio + INPUT_VALUE);
+ return (pdsr & mask) != 0;
+}
+
+static void oxnas_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+
+ if (val)
+ writel_relaxed(BIT(offset), pio + OUTPUT_SET);
+ else
+ writel_relaxed(BIT(offset), pio + OUTPUT_CLEAR);
+}
+
+static int oxnas_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+
+ if (val)
+ writel_relaxed(BIT(offset), pio + OUTPUT_SET);
+ else
+ writel_relaxed(BIT(offset), pio + OUTPUT_CLEAR);
+
+ writel_relaxed(BIT(offset), pio + OUTPUT_EN_SET);
+
+ return 0;
+}
+
+static int oxnas_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ int virq;
+
+ if (offset < chip->ngpio)
+ virq = irq_create_mapping(oxnas_gpio->domain, offset);
+ else
+ virq = -ENXIO;
+
+ dev_dbg(chip->parent, "%s: request IRQ for GPIO %d, return %d\n",
+ chip->label, offset + chip->base, virq);
+ return virq;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void oxnas_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ int i;
+ struct oxnas_gpio_chip *oxnas_gpio = to_oxnas_gpio_chip(chip);
+ void __iomem *pio = oxnas_gpio->regbase;
+ enum oxnas_mux mux;
+
+ for (i = 0; i < chip->ngpio; i++) {
+ unsigned pin = chip->base + i;
+ unsigned mask = pin_to_mask(pin);
+ unsigned bank = pin_to_bank(pin);
+ const char *gpio_label;
+ u32 pdsr;
+
+ gpio_label = gpiochip_is_requested(chip, i);
+ if (gpio_label) {
+ seq_printf(s, "[%s]\tGPIO%s%d: ",
+ gpio_label, chip->label, i);
+ pdsr = readl_relaxed(pio + INPUT_VALUE);
+
+ seq_printf(s, "[gpio] %s\n",
+ pdsr & mask ?
+ "set" : "clear");
+ } else {
+ mux = oxnas_mux_get_func(oxnas_gpio->regmap,
+ bank,
+ mask);
+ seq_printf(s, "\tGPIO%s%d: [func%d]\n",
+ chip->label, i, mux);
+ }
+
+ }
+}
+#else
+#define oxnas_gpio_dbg_show NULL
+#endif
+
+/* Several AIC controller irqs are dispatched through this GPIO handler.
+ * To use any AT91_PIN_* as an externally triggered IRQ, first call
+ * oxnas_set_gpio_input() then maybe enable its glitch filter.
+ * Then just request_irq() with the pin ID; it works like any ARM IRQ
+ * handler.
+ */
+
+static void gpio_irq_mask(struct irq_data *d)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = irq_data_get_irq_chip_data(d);
+ void __iomem *pio = oxnas_gpio->regbase;
+ unsigned mask = 1 << d->hwirq;
+ unsigned type = irqd_get_trigger_type(d);
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ writel(readl(pio + RE_IRQ_ENABLE) & ~mask, pio + RE_IRQ_ENABLE);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ writel(readl(pio + FE_IRQ_ENABLE) & ~mask, pio + FE_IRQ_ENABLE);
+}
+
+static void gpio_irq_unmask(struct irq_data *d)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = irq_data_get_irq_chip_data(d);
+ void __iomem *pio = oxnas_gpio->regbase;
+ unsigned mask = 1 << d->hwirq;
+ unsigned type = irqd_get_trigger_type(d);
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ writel(readl(pio + RE_IRQ_ENABLE) | mask, pio + RE_IRQ_ENABLE);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ writel(readl(pio + FE_IRQ_ENABLE) | mask, pio + FE_IRQ_ENABLE);
+}
+
+
+static int gpio_irq_type(struct irq_data *d, unsigned type)
+{
+ if ((type & IRQ_TYPE_EDGE_BOTH) == 0) {
+ pr_warn("oxnas: Unsupported type for irq %d\n",
+ gpio_to_irq(d->irq));
+ return -EINVAL;
+ }
+ /* seems no way to set trigger type without enable irq,
+ * so leave it to unmask time
+ */
+
+ return 0;
+}
+
+static struct irq_chip gpio_irqchip = {
+ .name = "GPIO",
+ .irq_disable = gpio_irq_mask,
+ .irq_mask = gpio_irq_mask,
+ .irq_unmask = gpio_irq_unmask,
+ .irq_set_type = gpio_irq_type,
+};
+
+static void gpio_irq_handler(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_data *idata = irq_desc_get_irq_data(desc);
+ struct oxnas_gpio_chip *oxnas_gpio = irq_data_get_irq_chip_data(idata);
+ void __iomem *pio = oxnas_gpio->regbase;
+ unsigned long isr;
+ int n;
+
+ chained_irq_enter(chip, desc);
+ for (;;) {
+ isr = readl_relaxed(pio + IRQ_PENDING);
+ if (!isr)
+ break;
+
+ /* acks pending interrupts */
+ writel_relaxed(isr, pio + IRQ_PENDING);
+
+ for_each_set_bit(n, &isr, BITS_PER_LONG) {
+ generic_handle_irq(irq_find_mapping(oxnas_gpio->domain,
+ n));
+ }
+ }
+ chained_irq_exit(chip, desc);
+ /* now it may re-trigger */
+}
+
+/*
+ * This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpio_lock_class;
+
+static int oxnas_gpio_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = h->host_data;
+
+ irq_set_lockdep_class(virq, &gpio_lock_class);
+
+ irq_set_chip_and_handler(virq, &gpio_irqchip, handle_edge_irq);
+
+ irq_set_chip_data(virq, oxnas_gpio);
+
+ return 0;
+}
+
+static int oxnas_gpio_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec,
+ unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_type)
+{
+ struct oxnas_gpio_chip *oxnas_gpio = d->host_data;
+ int ret;
+ int pin = oxnas_gpio->chip.base + intspec[0];
+
+ if (WARN_ON(intsize < 2))
+ return -EINVAL;
+ *out_hwirq = intspec[0];
+ *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+
+ ret = gpio_request(pin, ctrlr->full_name);
+ if (ret)
+ return ret;
+
+ ret = gpio_direction_input(pin);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct irq_domain_ops oxnas_gpio_ops = {
+ .map = oxnas_gpio_irq_map,
+ .xlate = oxnas_gpio_irq_domain_xlate,
+};
+
+static int oxnas_gpio_of_irq_setup(struct device_node *node,
+ struct oxnas_gpio_chip *oxnas_gpio,
+ unsigned int irq)
+{
+ /* Disable irqs of this controller */
+ writel_relaxed(0, oxnas_gpio->regbase + RE_IRQ_ENABLE);
+ writel_relaxed(0, oxnas_gpio->regbase + FE_IRQ_ENABLE);
+
+ /* Setup irq domain */
+ oxnas_gpio->domain = irq_domain_add_linear(node, oxnas_gpio->chip.ngpio,
+ &oxnas_gpio_ops, oxnas_gpio);
+ if (!oxnas_gpio->domain)
+ panic("oxnas_gpio: couldn't allocate irq domain (DT).\n");
+
+ irq_set_chip_data(irq, oxnas_gpio);
+ irq_set_chained_handler(irq, gpio_irq_handler);
+
+ return 0;
+}
+
+/* This structure is replicated for each GPIO block allocated at probe time */
+static struct gpio_chip oxnas_gpio_template = {
+ .request = oxnas_gpio_request,
+ .free = oxnas_gpio_free,
+ .direction_input = oxnas_gpio_direction_input,
+ .get = oxnas_gpio_get,
+ .direction_output = oxnas_gpio_direction_output,
+ .set = oxnas_gpio_set,
+ .to_irq = oxnas_gpio_to_irq,
+ .dbg_show = oxnas_gpio_dbg_show,
+ .can_sleep = 0,
+ .ngpio = MAX_NB_GPIO_PER_BANK,
+};
+
+static const struct of_device_id oxnas_gpio_of_match[] = {
+ { .compatible = "oxsemi,ox810se-gpio"},
+ { /* sentinel */ }
+};
+
+static int oxnas_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ struct oxnas_gpio_chip *oxnas_chip = NULL;
+ struct gpio_chip *chip;
+ struct pinctrl_gpio_range *range;
+ struct device_node *node = pdev->dev.of_node;
+ int ret = 0;
+ int irq, i;
+ int alias_idx = of_alias_get_id(np, "gpio");
+ uint32_t ngpio;
+ char **names;
+
+ if (WARN_ON(alias_idx >= ARRAY_SIZE(gpio_chips)))
+ return -EINVAL;
+
+ if (gpio_chips[alias_idx]) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto err;
+ }
+
+ oxnas_chip = devm_kzalloc(&pdev->dev, sizeof(*oxnas_chip), GFP_KERNEL);
+ if (!oxnas_chip) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Get pinctrl sys control regmap */
+ oxnas_chip->regmap =
+ syscon_regmap_lookup_by_phandle(of_get_parent(node),
+ "plxtech,sys-ctrl");
+ if (IS_ERR(oxnas_chip->regmap)) {
+ dev_err(&pdev->dev, "failed to get sys ctrl regmap\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ oxnas_chip->regbase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(oxnas_chip->regbase)) {
+ ret = PTR_ERR(oxnas_chip->regbase);
+ goto err;
+ }
+
+ oxnas_chip->chip = oxnas_gpio_template;
+
+ chip = &oxnas_chip->chip;
+ chip->of_node = np;
+ chip->label = dev_name(&pdev->dev);
+ chip->parent = &pdev->dev;
+ chip->owner = THIS_MODULE;
+ chip->base = alias_idx * MAX_NB_GPIO_PER_BANK;
+
+ if (!of_property_read_u32(np, "#gpio-lines", &ngpio)) {
+ if (ngpio > MAX_NB_GPIO_PER_BANK)
+ pr_err("oxnas_gpio.%d, gpio-nb >= %d failback to %d\n",
+ alias_idx, MAX_NB_GPIO_PER_BANK,
+ MAX_NB_GPIO_PER_BANK);
+ else
+ chip->ngpio = ngpio;
+ }
+
+ names = devm_kzalloc(&pdev->dev, sizeof(char *) * chip->ngpio,
+ GFP_KERNEL);
+
+ if (!names) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < chip->ngpio; i++)
+ names[i] = kasprintf(GFP_KERNEL, "MF_%c%d", alias_idx + 'A', i);
+
+ chip->names = (const char *const *)names;
+
+ range = &oxnas_chip->range;
+ range->name = chip->label;
+ range->id = alias_idx;
+ range->pin_base = range->base = range->id * MAX_NB_GPIO_PER_BANK;
+
+ range->npins = chip->ngpio;
+ range->gc = chip;
+
+ ret = gpiochip_add(chip);
+ if (ret)
+ goto err;
+
+ gpio_chips[alias_idx] = oxnas_chip;
+ gpio_banks = max(gpio_banks, alias_idx + 1);
+
+ oxnas_gpio_of_irq_setup(np, oxnas_chip, irq);
+
+ dev_info(&pdev->dev, "at address %p\n", oxnas_chip->regbase);
+
+ return 0;
+err:
+ dev_err(&pdev->dev, "Failure %i for GPIO %i\n", ret, alias_idx);
+
+ return ret;
+}
+
+static struct platform_driver oxnas_gpio_driver = {
+ .driver = {
+ .name = "gpio-oxnas",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(oxnas_gpio_of_match),
+ },
+ .probe = oxnas_gpio_probe,
+};
+
+static struct platform_driver oxnas_pinctrl_driver = {
+ .driver = {
+ .name = "pinctrl-oxnas",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(oxnas_pinctrl_of_match),
+ },
+ .probe = oxnas_pinctrl_probe,
+ .remove = oxnas_pinctrl_remove,
+};
+
+static int __init oxnas_pinctrl_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&oxnas_gpio_driver);
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&oxnas_pinctrl_driver);
+}
+arch_initcall(oxnas_pinctrl_init);
+
+static void __exit oxnas_pinctrl_exit(void)
+{
+ platform_driver_unregister(&oxnas_pinctrl_driver);
+}
+
+module_exit(oxnas_pinctrl_exit);
--
1.9.1

2016-03-09 10:27:32

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 10/18] dt-bindings: Add PLX Technology OXNAS Standard Clocks bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
.../devicetree/bindings/clock/plxtech,stdclk.txt | 35 ++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/plxtech,stdclk.txt

diff --git a/Documentation/devicetree/bindings/clock/plxtech,stdclk.txt b/Documentation/devicetree/bindings/clock/plxtech,stdclk.txt
new file mode 100644
index 0000000..c60b459
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/plxtech,stdclk.txt
@@ -0,0 +1,35 @@
+PLX Technology OXNAS SoC Family Standard Clocks
+================================================
+
+Please also refer to clock-bindings.txt in this directory for common clock
+bindings usage.
+
+Required properties:
+- compatible: Should be "oxsemi,ox810se-stdclk"
+- #clock-cells: 1, see below
+
+Parent node should have the following properties :
+- compatible: Should be "oxsemi,ox810se-sys-ctrl", "syscon", "simple-mfd"
+
+For OX810SE, the clock indices are :
+ - 0: LEON
+ - 1: DMA_SGDMA
+ - 2: CIPHER
+ - 3: SATA
+ - 4: AUDIO
+ - 5: USBMPH
+ - 6: ETHA
+ - 7: PCIA
+ - 8: NAND
+
+example:
+
+sys: sys-ctrl@000000 {
+ compatible = "oxsemi,ox810se-sys-ctrl", "syscon", "simple-mfd";
+ reg = <0x000000 0x100000>;
+
+ stdclk: stdclk {
+ compatible = "oxsemi,ox810se-stdclk";
+ #clock-cells = <1>;
+ };
+};
--
1.9.1

2016-03-09 10:27:49

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 08/18] dt-bindings: Add PLX Technology Reset Controller bindings

Signed-off-by: Neil Armstrong <[email protected]>
---
.../devicetree/bindings/reset/plxtech,reset.txt | 58 ++++++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 Documentation/devicetree/bindings/reset/plxtech,reset.txt

diff --git a/Documentation/devicetree/bindings/reset/plxtech,reset.txt b/Documentation/devicetree/bindings/reset/plxtech,reset.txt
new file mode 100644
index 0000000..581c974
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/plxtech,reset.txt
@@ -0,0 +1,58 @@
+PLX Technology OXNAS SoC Family RESET Controller
+================================================
+
+Please also refer to reset.txt in this directory for common reset
+controller binding usage.
+
+Required properties:
+- compatible: Should be "oxsemi,ox810se-reset"
+- #reset-cells: 1, see below
+
+Parent node should have the following properties :
+- compatible: Should be "oxsemi,ox810se-sys-ctrl", "syscon", "simple-mfd"
+
+For OX810SE, the indices are :
+ - 0 : ARM
+ - 1 : COPRO
+ - 2 : Reserved
+ - 3 : Reserved
+ - 4 : USBHS
+ - 5 : USBHSPHY
+ - 6 : MAC
+ - 7 : PCI
+ - 8 : DMA
+ - 9 : DPE
+ - 10 : DDR
+ - 11 : SATA
+ - 12 : SATA_LINK
+ - 13 : SATA_PHY
+ - 14 : Reserved
+ - 15 : NAND
+ - 16 : GPIO
+ - 17 : UART1
+ - 18 : UART2
+ - 19 : MISC
+ - 20 : I2S
+ - 21 : AHB_MON
+ - 22 : UART3
+ - 23 : UART4
+ - 24 : SGDMA
+ - 25 : Reserved
+ - 26 : Reserved
+ - 27 : Reserved
+ - 28 : Reserved
+ - 29 : Reserved
+ - 30 : Reserved
+ - 31 : BUS
+
+example:
+
+sys: sys-ctrl@000000 {
+ compatible = "oxsemi,ox810se-sys-ctrl", "syscon", "simple-mfd";
+ reg = <0x000000 0x100000>;
+
+ reset: reset-controller {
+ compatible = "oxsemi,ox810se-reset";
+ #reset-cells = <1>;
+ };
+};
--
1.9.1

2016-03-09 10:27:37

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 09/18] clk: Add PLX Technology OXNAS Standard Clocks

Add PLX Technology OXNAS SoC Family Standard Clocks support.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/clk/Kconfig | 6 ++
drivers/clk/Makefile | 1 +
drivers/clk/clk-oxnas.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 209 insertions(+)
create mode 100644 drivers/clk/clk-oxnas.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index eca8e01..16fa822 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -192,6 +192,12 @@ config COMMON_CLK_PXA
---help---
Sypport for the Marvell PXA SoC.

+config COMMON_CLK_OXNAS
+ bool
+ select MFD_SYSCON
+ ---help---
+ Sypport for the OXNAS SoC Family clocks.
+
config COMMON_CLK_CDCE706
tristate "Clock driver for TI CDCE706 clock synthesizer"
depends on I2C
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index bae4be6..a5d45d8 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_ARCH_MB86S7X) += clk-mb86s7x.o
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
+obj-$(CONFIG_COMMON_CLK_OXNAS) += clk-oxnas.o
obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o
obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
diff --git a/drivers/clk/clk-oxnas.c b/drivers/clk/clk-oxnas.c
new file mode 100644
index 0000000..58b984b
--- /dev/null
+++ b/drivers/clk/clk-oxnas.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2010 Broadcom
+ * Copyright (C) 2012 Stephen Warren
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/stringify.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+/* Standard regmap gate clocks */
+struct clk_oxnas {
+ struct clk_hw hw;
+ signed char bit;
+ struct regmap *regmap;
+};
+
+/* Regmap offsets */
+#define CLK_STAT_REGOFFSET 0x24
+#define CLK_SET_REGOFFSET 0x2c
+#define CLK_CLR_REGOFFSET 0x30
+
+static inline struct clk_oxnas *to_clk_oxnas(struct clk_hw *hw)
+{
+ return container_of(hw, struct clk_oxnas, hw);
+}
+
+static int oxnas_clk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_oxnas *std = to_clk_oxnas(hw);
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(std->regmap, CLK_STAT_REGOFFSET, &val);
+ if (ret < 0)
+ return ret;
+
+ return val & BIT(std->bit);
+}
+
+static int oxnas_clk_enable(struct clk_hw *hw)
+{
+ struct clk_oxnas *std = to_clk_oxnas(hw);
+
+ regmap_write(std->regmap, CLK_SET_REGOFFSET, BIT(std->bit));
+
+ return 0;
+}
+
+static void oxnas_clk_disable(struct clk_hw *hw)
+{
+ struct clk_oxnas *std = to_clk_oxnas(hw);
+
+ regmap_write(std->regmap, CLK_CLR_REGOFFSET, BIT(std->bit));
+}
+
+static const struct clk_ops oxnas_clk_ops = {
+ .enable = oxnas_clk_enable,
+ .disable = oxnas_clk_disable,
+ .is_enabled = oxnas_clk_is_enabled,
+};
+
+static const char *const oxnas_clk_parents[] = {
+ "oscillator",
+};
+
+static const char *const eth_parents[] = {
+ "gmacclk",
+};
+
+#define DECLARE_STD_CLKP(__clk, __parent) \
+static const struct clk_init_data clk_##__clk##_init = { \
+ .name = __stringify(__clk), \
+ .ops = &oxnas_clk_ops, \
+ .parent_names = __parent, \
+ .num_parents = ARRAY_SIZE(__parent), \
+}
+
+#define DECLARE_STD_CLK(__clk) DECLARE_STD_CLKP(__clk, oxnas_clk_parents)
+
+/* Clk init data declaration */
+DECLARE_STD_CLK(leon);
+DECLARE_STD_CLK(dma_sgdma);
+DECLARE_STD_CLK(cipher);
+DECLARE_STD_CLK(sata);
+DECLARE_STD_CLK(audio);
+DECLARE_STD_CLK(usbmph);
+DECLARE_STD_CLKP(etha, eth_parents);
+DECLARE_STD_CLK(pciea);
+DECLARE_STD_CLK(nand);
+
+/* Bit - Name association */
+static const struct clk_init_data *clk_oxnas_init[] = {
+ [0] = &clk_leon_init,
+ [1] = &clk_dma_sgdma_init,
+ [2] = &clk_cipher_init,
+ [3] = NULL, /* Do not touch to DDR clock */
+ [4] = &clk_sata_init,
+ [5] = &clk_audio_init,
+ [6] = &clk_usbmph_init,
+ [7] = &clk_etha_init,
+ [8] = &clk_pciea_init,
+ [9] = &clk_nand_init,
+};
+
+static int oxnas_stdclk_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct regmap *regmap;
+ struct clk_oxnas *clk_oxnas;
+ struct clk_onecell_data *onecell_data;
+ struct clk **clks;
+ unsigned int clks_count = 0;
+ int i;
+
+ clk_oxnas = devm_kzalloc(&pdev->dev,
+ sizeof(*clk_oxnas)*ARRAY_SIZE(clk_oxnas_init),
+ GFP_KERNEL);
+ if (!clk_oxnas)
+ return -ENOMEM;
+
+ clks = devm_kzalloc(&pdev->dev,
+ sizeof(*clks)*ARRAY_SIZE(clk_oxnas_init),
+ GFP_KERNEL);
+ if (!clks)
+ return -ENOMEM;
+
+ onecell_data = devm_kzalloc(&pdev->dev, sizeof(*onecell_data),
+ GFP_KERNEL);
+ if (!onecell_data)
+ return -ENOMEM;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (!regmap) {
+ dev_err(&pdev->dev, "failed to have parent regmap\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(clk_oxnas_init); i++) {
+ struct clk_oxnas *_clk;
+
+ if (!clk_oxnas_init[i])
+ continue;
+
+ _clk = &clk_oxnas[i];
+ _clk->bit = i;
+ _clk->hw.init = clk_oxnas_init[i];
+ _clk->regmap = regmap;
+
+ clks[clks_count] = devm_clk_register(&pdev->dev, &_clk->hw);
+ if (WARN_ON(IS_ERR(clks[clks_count])))
+ return PTR_ERR(clks[clks_count]);
+
+ ++clks_count;
+ }
+
+ onecell_data->clks = clks;
+ onecell_data->clk_num = clks_count;
+
+ return of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data);
+}
+
+static int oxnas_stdclk_remove(struct platform_device *pdev)
+{
+ of_clk_del_provider(pdev->dev.of_node);
+
+ return 0;
+}
+
+static const struct of_device_id oxnas_stdclk_dt_ids[] = {
+ { .compatible = "oxsemi,ox810se-stdclk" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, oxnas_stdclk_dt_ids);
+
+static struct platform_driver oxnas_stdclk_driver = {
+ .probe = oxnas_stdclk_probe,
+ .remove = oxnas_stdclk_remove,
+ .driver = {
+ .name = "oxnas-stdclk",
+ .of_match_table = of_match_ptr(oxnas_stdclk_dt_ids),
+ },
+};
+
+module_platform_driver(oxnas_stdclk_driver);
--
1.9.1

2016-03-09 10:27:55

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 07/18] reset: Add PLX Technology Reset Controller driver

Add System reset controller driver for PLX Technology OXNAS SoC Family.

CC: Ma Haijun <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/reset/Kconfig | 3 +
drivers/reset/Makefile | 1 +
drivers/reset/reset-oxnas.c | 136 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 140 insertions(+)
create mode 100644 drivers/reset/reset-oxnas.c

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index df37212..0b2733d 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -12,5 +12,8 @@ menuconfig RESET_CONTROLLER

If unsure, say no.

+config RESET_OXNAS
+ bool
+
source "drivers/reset/sti/Kconfig"
source "drivers/reset/hisilicon/Kconfig"
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 4d7178e..97e04c5 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_ARCH_STI) += sti/
obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o
obj-$(CONFIG_ATH79) += reset-ath79.o
+obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
diff --git a/drivers/reset/reset-oxnas.c b/drivers/reset/reset-oxnas.c
new file mode 100644
index 0000000..9cce026
--- /dev/null
+++ b/drivers/reset/reset-oxnas.c
@@ -0,0 +1,136 @@
+/*
+ * drivers/reset/reset-oxnas.c
+ *
+ * Copyright (C) 2016 Neil Armstrong <[email protected]>
+ * Copyright (C) 2014 Ma Haijun <[email protected]>
+ * Copyright (C) 2009 Oxford Semiconductor Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+/* Regmap offsets */
+#define RST_SET_REGOFFSET 0x34
+#define RST_CLR_REGOFFSET 0x38
+
+struct oxnas_reset {
+ struct regmap *regmap;
+ struct reset_controller_dev rcdev;
+};
+
+static int oxnas_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct oxnas_reset *data =
+ container_of(rcdev, struct oxnas_reset, rcdev);
+
+ regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id));
+ msleep(50);
+ regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id));
+
+ return 0;
+}
+
+static int oxnas_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct oxnas_reset *data =
+ container_of(rcdev, struct oxnas_reset, rcdev);
+
+ regmap_write(data->regmap, RST_SET_REGOFFSET, BIT(id));
+
+ return 0;
+}
+
+static int oxnas_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct oxnas_reset *data =
+ container_of(rcdev, struct oxnas_reset, rcdev);
+
+ regmap_write(data->regmap, RST_CLR_REGOFFSET, BIT(id));
+
+ return 0;
+}
+
+static const struct reset_control_ops oxnas_reset_ops = {
+ .reset = oxnas_reset_reset,
+ .assert = oxnas_reset_assert,
+ .deassert = oxnas_reset_deassert,
+};
+
+static const struct of_device_id oxnas_reset_dt_ids[] = {
+ { .compatible = "oxsemi,ox810se-reset", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, oxnas_reset_dt_ids);
+
+static int oxnas_reset_probe(struct platform_device *pdev)
+{
+ struct oxnas_reset *data;
+ struct device *parent;
+
+ parent = pdev->dev.parent;
+ if (!parent) {
+ dev_err(&pdev->dev, "no parent\n");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = syscon_node_to_regmap(parent->of_node);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&pdev->dev, "failed to get parent regmap\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.nr_resets = 32;
+ data->rcdev.ops = &oxnas_reset_ops;
+ data->rcdev.of_node = pdev->dev.of_node;
+
+ return reset_controller_register(&data->rcdev);
+}
+
+static int oxnas_reset_remove(struct platform_device *pdev)
+{
+ struct oxnas_reset *data = platform_get_drvdata(pdev);
+
+ reset_controller_unregister(&data->rcdev);
+
+ return 0;
+}
+
+static struct platform_driver oxnas_reset_driver = {
+ .probe = oxnas_reset_probe,
+ .remove = oxnas_reset_remove,
+ .driver = {
+ .name = "oxnas-reset",
+ .of_match_table = oxnas_reset_dt_ids,
+ },
+};
+
+module_platform_driver(oxnas_reset_driver);
--
1.9.1

2016-03-09 10:28:10

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 06/18] dt-bindings: Add Oxford Semiconductors to vendor prefixes

Signed-off-by: Neil Armstrong <[email protected]>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 8c084c1..188671f 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -170,6 +170,7 @@ opencores OpenCores.org
option Option NV
ortustech Ortus Technology Co., Ltd.
ovti OmniVision Technologies
+oxsemi Oxford Semiconductors, Ltd.
panasonic Panasonic Corporation
parade Parade Technologies Inc.
pericom Pericom Technology Inc.
--
1.9.1

2016-03-09 10:28:20

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v2 05/18] dt-bindings: vendor-prefixes: Add PLX Technology

Add PLX Technology vendor prefix.
Fixed "pixdir" alphabetizing.

Signed-off-by: Neil Armstrong <[email protected]>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 72e2c5a..8c084c1 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -175,9 +175,10 @@ parade Parade Technologies Inc.
pericom Pericom Technology Inc.
phytec PHYTEC Messtechnik GmbH
picochip Picochip Ltd
+pixcir PIXCIR MICROELECTRONICS Co., Ltd
plathome Plat'Home Co., Ltd.
plda PLDA
-pixcir PIXCIR MICROELECTRONICS Co., Ltd
+plxtech PLX Technology, Inc.
pulsedlight PulsedLight, Inc
powervr PowerVR (deprecated, use img)
qca Qualcomm Atheros, Inc.
--
1.9.1

2016-03-10 14:40:38

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2 11/18] pinctrl: Add PLX Technology OXNAS pinctrl and gpio driver

Hi Neil,

[auto build test WARNING on robh/for-next]
[also build test WARNING on v4.5-rc7]
[cannot apply to next-20160310]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url: https://github.com/0day-ci/linux/commits/Neil-Armstrong/Add-Initial-support-for-PLX-Technology-OX810SE/20160309-183154
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux for-next
config: um-allmodconfig (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=um

All warnings (new ones prefixed by >>):

warning: (ST_IRQCHIP && HIP04_ETH && STMMAC_PLATFORM && DWMAC_IPQ806X && DWMAC_LPC18XX && DWMAC_ROCKCHIP && DWMAC_SOCFPGA && DWMAC_STI && TI_CPSW && PINCTRL_OXNAS && PINCTRL_ROCKCHIP && PINCTRL_DOVE && POWER_RESET_KEYSTONE && POWER_RESET_SYSCON && POWER_RESET_SYSCON_POWEROFF && S3C2410_WATCHDOG && VIDEO_OMAP3 && VIDEO_S5P_FIMC && USB_XHCI_MTK && RTC_DRV_AT91SAM9 && LPC18XX_DMAMUX && VIDEO_OMAP4 && COMMON_CLK_OXNAS && HWSPINLOCK_QCOM && ATMEL_ST && QCOM_GSBI && PHY_HI6220_USB) selects MFD_SYSCON which has unmet direct dependencies (HAS_IOMEM)

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (1.28 kB)
.config.gz (17.37 kB)
Download all attachments

2016-03-15 11:47:54

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v2 03/18] irqchip: versatile-fpga: add new arm,rps-irq compatible

On 09/03/16 10:24, Neil Armstrong wrote:
> Under the OX810SE, this exact same interface is used as "Reference Peripheral
> Specification" Interrupt Controller, so add a new compatible string.
>
> Signed-off-by: Neil Armstrong <[email protected]>

Acked-by: Marc Zyngier <[email protected]>

M.
--
Jazz is not dead. It just smells funny...

2016-03-15 14:30:10

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 11/17] dt-bindings: Add PLX Technology OXNAS pinctrl and gpio bindings

On Thu, Mar 3, 2016 at 12:40 PM, Neil Armstrong <[email protected]> wrote:

This is a vert terse zero-line commit message. Atleast describe what you are
trying to do.

> Signed-off-by: Neil Armstrong <[email protected]>

> +optional properties:
> +- #gpio-lines: Number of gpio if absent 32.

NACK, use ngpio from the gpio.txt document like
everyone else.

> +Required properties for iomux controller:
> +- compatible: "plxtech,nas782x-pinctrl" or "plxtech,ox810se-pinctrl"
> +- plxtech,mux-mask: array of mask (periph per bank) to describe if a pin can be
> + configured in this periph mode. All the periph and bank need to be describe.

Why? Encode this into the driver and select muxmask from
the compatible string if it is a hardware limitation.

> +Each column will represent the possible peripheral of the pinctrl
> +Each line will represent a pio bank
> +
> +For example :
> +Peripheral: 2 ( A and B)
> +Bank: 2 (A, B and C)
> +=>
> +
> + /* A B */
> + 0xffffffff 0xffc00c3b /* pioA */
> + 0xffffffff 0x7fff3ccf /* pioB */
> +
> +For each peripheral/bank we will descibe in a u32 if a pin can be
> +configured in it by putting 1 to the pin bit (1 << pin)

That's just completely hopeless to understand for a DT author.
Put it into the driver.

> +Required properties for pin configuration node:
> +- plxtech,pins: 4 integers array, represents a group of pins mux and config
> + setting. The format is plxtech,pins = <PIN_BANK PIN_BANK_NUM PERIPH CONFIG>.
> + The PERIPH 0 means gpio, PERIPH 1 is periph A, PERIPH 2 is periph B...
> + PIN_BANK 0 is pioA, PIN_BANK 1 is pioB...

NACK, use the standard binding for "pins" from pinctrl-bindings.txt

Also make the driver use the existing helpers for this property.

Yours,
Linus Walleij

2016-03-15 14:56:33

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 10/17] pinctrl: Add PLX Technology OXNAS pinctrl and gpio driver

On Thu, Mar 3, 2016 at 12:40 PM, Neil Armstrong <[email protected]> wrote:

> Add pinctrl and gprio control support to PLX Technology OXNAS SoC Family

Be a bit more verbose. Is this MIPS? ARM? How many pins does it have? Etc.

> CC: Ma Haijun <[email protected]>
> CC: Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
> Signed-off-by: Neil Armstrong <[email protected]>

This driver has some way to go, but let's work on it!

> +config PINCTRL_OXNAS
> + bool
> + depends on OF
> + select PINMUX
> + select PINCONF

Why is it not using GENERIC_PINCONF?

> + select GPIOLIB
> + select OF_GPIO

I can already see that this driver should use
select GPIOLIB_IRQCHIP and the existing infrastructure
to manage chained IRQs in the gpiolib core.

The driver need to be rewritten for this: see other drivers
using GPIOLIB_IRQCHIP for examples, both GPIO and pin control
drivers use this so there are plenty of examples.

As a result the code will shrink quite a bit.

> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/io.h>
> +#include <linux/gpio.h>

You should only need to include <linux/gpio/driver.h>

> +#include <linux/pinctrl/machine.h>

Why?

> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +/* Since we request GPIOs from ourself */
> +#include <linux/pinctrl/consumer.h>

So why do you do this? Is this a copy/paste?

> +#include <linux/version.h>

This looks like something from a porting shim that brings this
same driver to a few different kernel versions. This include should
not be needed.

> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +
> +#include "core.h"
> +
> +#define MAX_NB_GPIO_PER_BANK 32
> +#define MAX_GPIO_BANKS 2
> +
> +struct oxnas_gpio_chip {
> + struct gpio_chip chip;
> + struct pinctrl_gpio_range range;
> + void __iomem *regbase; /* GPIOA/B virtual address */
> + struct irq_domain *domain; /* associated irq domain */

Should not be needed with GPIOLIB_IRQCHIP

> +#define to_oxnas_gpio_chip(c) container_of(c, struct oxnas_gpio_chip, chip)

We nowadays use gpiochip_get_data() to get the state container pointer.
Use this along with gpiochip_add_data(), or even better:
devm_gpiochip_add_data()
which will be merged for v4.6.

> +static struct oxnas_gpio_chip *gpio_chips[MAX_GPIO_BANKS];

Is that really needed? Oh well let's see as we work on this.

> +static int oxnas_dt_node_to_map(struct pinctrl_dev *pctldev,
> + struct device_node *np,
> + struct pinctrl_map **map, unsigned *num_maps)
> +{

> + /*
> + * first find the group of this node and check if we need create
> + * config maps for pins
> + */
> + grp = oxnas_pinctrl_find_group_by_name(info, np->name);
> + if (!grp) {
> + dev_err(info->dev, "unable to find group for node %s\n",
> + np->name);
> + return -EINVAL;
> + }
> +
> + map_num += grp->npins;
> + new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num,
> + GFP_KERNEL);
> + if (!new_map)
> + return -ENOMEM;
> +
> + *map = new_map;
> + *num_maps = map_num;

Ugh this looks hairy. Can you not use the utility functions from
pinctrl-utils.h with pinctrl_utils_reserve_map() etc?

> +static void oxnas_dt_free_map(struct pinctrl_dev *pctldev,
> + struct pinctrl_map *map, unsigned num_maps)
> +{
> +}

Really? You don't fool me. ;)

pinctrl_utils_dt_free_map() from pinctrl-utils.h should be your friend,
if you follow the pattern from other drivers.

> +static void __iomem *pin_to_gpioctrl(struct oxnas_pinctrl *info,
> + unsigned int bank)
> +{
> + return gpio_chips[bank]->regbase;
> +}
> +
> +static inline int pin_to_bank(unsigned pin)
> +{
> + return pin / MAX_NB_GPIO_PER_BANK;
> +}
> +
> +static unsigned pin_to_mask(unsigned int pin)
> +{
> + return 1 << pin;
> +}

Those are a bit simplistic. The last one can be replaced by the
this inline:

+ include <linux/bitops.h>

- pin_to_mask(foo);
+ BIT(foo);

> +static int gpio_irq_type(struct irq_data *d, unsigned type)
> +{
> + if ((type & IRQ_TYPE_EDGE_BOTH) == 0) {
> + pr_warn("oxnas: Unsupported type for irq %d\n",
> + gpio_to_irq(d->irq));
> + return -EINVAL;
> + }
> + /* seems no way to set trigger type without enable irq,
> + * so leave it to unmask time
> + */
> +
> + return 0;
> +}

This will make your interrupt chip accept level IRQ types
which it obviously does not support.

> +static struct irq_chip gpio_irqchip = {
> + .name = "GPIO",
> + .irq_disable = gpio_irq_mask,
> + .irq_mask = gpio_irq_mask,
> + .irq_unmask = gpio_irq_unmask,
> + .irq_set_type = gpio_irq_type,
> +};

I think you should implement .irq_ack which will ACK the
IRQ before continuing with the IRQ handler.

> +static void gpio_irq_handler(struct irq_desc *desc)
> +{
> + struct irq_chip *chip = irq_desc_get_chip(desc);
> + struct irq_data *idata = irq_desc_get_irq_data(desc);
> + struct oxnas_gpio_chip *oxnas_gpio = irq_data_get_irq_chip_data(idata);
> + void __iomem *pio = oxnas_gpio->regbase;
> + unsigned long isr;
> + int n;
> +
> + chained_irq_enter(chip, desc);
> + for (;;) {
> + isr = readl_relaxed(pio + IRQ_PENDING);
> + if (!isr)
> + break;
> +
> + /* acks pending interrupts */
> + writel_relaxed(isr, pio + IRQ_PENDING);

This should not be done here but in the .irq_ack() function that you
should implement in the irq chip.

See drivers/gpio/gpio-pl061.c for inspiration.

> + * This lock class tells lockdep that GPIO irqs are in a different
> + * category than their parents, so it won't report false recursion.
> + */
> +static struct lock_class_key gpio_lock_class;

This is also handled by GPIOLIB_IRQCHIP.

> +static int oxnas_gpio_irq_map(struct irq_domain *h, unsigned int virq,
> + irq_hw_number_t hw)
> +{
> + struct oxnas_gpio_chip *oxnas_gpio = h->host_data;
> +
> + irq_set_lockdep_class(virq, &gpio_lock_class);
> +
> + irq_set_chip_and_handler(virq, &gpio_irqchip, handle_edge_irq);

So you use handle_edge_irq() but do not implement .irq_ack(), that
looks wrong.

> +
> + irq_set_chip_data(virq, oxnas_gpio);
> +
> + return 0;
> +}

And this is also handled by GPIOLIB_IRQCHIP by the way.

> +static int oxnas_gpio_irq_domain_xlate(struct irq_domain *d,
> + struct device_node *ctrlr,
> + const u32 *intspec,
> + unsigned int intsize,
> + irq_hw_number_t *out_hwirq,
> + unsigned int *out_type)
> +{
> + struct oxnas_gpio_chip *oxnas_gpio = d->host_data;
> + int ret;
> + int pin = oxnas_gpio->chip.base + intspec[0];
> +
> + if (WARN_ON(intsize < 2))
> + return -EINVAL;
> + *out_hwirq = intspec[0];
> + *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
> +
> + ret = gpio_request(pin, ctrlr->full_name);
> + if (ret)
> + return ret;

No, the IRQchip and gpiochip should be orthogonal.
The irqchip will mark lines as used for IRQ though.
Rely on GPIOLIB_IRQCHIP.

> +
> + ret = gpio_direction_input(pin);
> + if (ret)
> + return ret;

Your irqchip code should set up the hardware for IRQ,
not the xlate function.

> +
> + return 0;
> +}
> +
> +static struct irq_domain_ops oxnas_gpio_ops = {
> + .map = oxnas_gpio_irq_map,
> + .xlate = oxnas_gpio_irq_domain_xlate,
> +};

And all this goes away with GPIOLIB_IRQCHIP.

> + names = devm_kzalloc(&pdev->dev, sizeof(char *) * chip->ngpio,
> + GFP_KERNEL);
> +
> + if (!names) {
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + for (i = 0; i < chip->ngpio; i++)
> + names[i] = kasprintf(GFP_KERNEL, "MF_%c%d", alias_idx + 'A', i);
> +
> + chip->names = (const char *const *)names;

Clever, however we are discussing adding a method for naming the lines
to the device tree bindings, please see these discussions for information.
Do not use this method plese.

There will be more review comments but I look forward to v2 as the first
step, using more library functions and cutting down the code a bit!

Yours,
Linus Walleij

2016-03-16 15:00:38

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 10/17] pinctrl: Add PLX Technology OXNAS pinctrl and gpio driver

On 03/15/2016 03:56 PM, Linus Walleij wrote:
> On Thu, Mar 3, 2016 at 12:40 PM, Neil Armstrong <[email protected]> wrote:
>
>> Add pinctrl and gprio control support to PLX Technology OXNAS SoC Family
>
> Be a bit more verbose. Is this MIPS? ARM? How many pins does it have? Etc.
>
>> CC: Ma Haijun <[email protected]>
>> CC: Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
>> Signed-off-by: Neil Armstrong <[email protected]>
>
> This driver has some way to go, but let's work on it!

Hi Linus,

Thanks a lot for the review.
This driver was initially forked from the at91 driver by Jean-Christophe PLAGNIOL-VILLARD, I just
cleaned it up for upstreaming, but yes it must be rewritten.

I think I'll reboot over a clean base, could you give a reference driver I should follow ?
It's a very simple HW, it should be easy.
>
> There will be more review comments but I look forward to v2 as the first
> step, using more library functions and cutting down the code a bit!

These first comments are precious !

Could I post a v2 separately for review ?

Thanks,
Neil

>
> Yours,
> Linus Walleij
>

2016-03-17 14:50:03

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 10/17] pinctrl: Add PLX Technology OXNAS pinctrl and gpio driver

On Wed, Mar 16, 2016 at 4:00 PM, Neil Armstrong <[email protected]> wrote:

> I think I'll reboot over a clean base, could you give a reference driver I should follow ?
> It's a very simple HW, it should be easy.

I would look at those merged lately, which has pin control
combined with GPIO and interrupts.

drivers/pinctrl/qcom is good but supports quite a lot of
subvariants so it may be hard to see what parts you really
need. But I think of those as a very nice drivers.

Yours,
Linus Walleij

2016-03-17 16:13:51

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH 04/17] clocksource: Add PLX Technology RPS Timer

Hi Neil,

On 03/03/2016 12:39 PM, Neil Armstrong wrote:
> Add clocksource and clockevent driver from dual RPS timer.

please elaborate the log: describe the timer, how it works, is there a
pointer to doc, freq, etc ...

> CC: Ma Haijun <[email protected]>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/clocksource/Kconfig | 6 +
> drivers/clocksource/Makefile | 1 +
> drivers/clocksource/timer-rps.c | 249 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 256 insertions(+)
> create mode 100644 drivers/clocksource/timer-rps.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 33db740..f79bc0f 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -276,6 +276,12 @@ config VF_PIT_TIMER
> help
> Support for Period Interrupt Timer on Freescale Vybrid Family SoCs.
>
> +config CLKSRC_RPS_TIMER
> + bool
> + select CLKSRC_MMIO
> + help
> + This enables support for the PLX Tech OXNAS RPS timers.
> +

Please check DIGICOLOR_TIMER or DW_APB_TIMER or ROCKCHIP_TIMER or
ARMADA_370_XP_TIMER or ... config section and try to stick to the same
pattern.

> config SYS_SUPPORTS_SH_CMT
> bool
>
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index dc2b899..120bc09 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -46,6 +46,7 @@ obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
> obj-$(CONFIG_MTK_TIMER) += mtk_timer.o
> obj-$(CONFIG_CLKSRC_PISTACHIO) += time-pistachio.o
> obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o
> +obj-$(CONFIG_CLKSRC_RPS_TIMER) += timer-rps.o
>
> obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
> obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
> diff --git a/drivers/clocksource/timer-rps.c b/drivers/clocksource/timer-rps.c
> new file mode 100644
> index 0000000..79621b8
> --- /dev/null
> +++ b/drivers/clocksource/timer-rps.c
> @@ -0,0 +1,249 @@
> +/*
> + * drivers/clocksource/timer-rps.c
> + *
> + * Copyright (C) 2009 Oxford Semiconductor Ltd
> + * Copyright (C) 2013 Ma Haijun <[email protected]>
> + * Copyright (C) 2016 Neil Armstrong <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/io.h>
> +#include <linux/clockchips.h>
> +#include <linux/clk.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/sched_clock.h>
> +
> +/* TIMER1 as tick
> + * TIMER2 as clocksource
> + */

Comment format:

/*
* TIMER1 used as tick
* TIMER2 used as clocksource
*/

> +enum {
> + TIMER_LOAD = 0,
> + TIMER_CURR = 4,
> + TIMER_CTRL = 8,
> + TIMER_CLRINT = 0xC,
> +
> + TIMER_BITS = 24,
> +
> + TIMER_MAX_VAL = (1 << TIMER_BITS) - 1,
> +
> + TIMER_PERIODIC = (1 << 6),
> + TIMER_ENABLE = (1 << 7),
> +
> + TIMER_DIV1 = (0 << 2),
> + TIMER_DIV16 = (1 << 2),
> + TIMER_DIV256 = (2 << 2),
> +
> + TIMER1_OFFSET = 0,
> + TIMER2_OFFSET = 0x20,
> +};

What is the point of having this unorganized enum with duplicate values
? and why TIMER_DIV1 = (0 << 2) ?

Replace by #define.

> +/* Clockevent */
> +
> +static unsigned long timer_period = HZ;
> +static unsigned timer_prescaler = 1;
> +static void __iomem *timer_base;

Encapsulate these variables into a structure and use container_of.

> +
> +static irqreturn_t rps_timer_irq(int irq, void *dev_id)
> +{
> + struct clock_event_device *evt = dev_id;
> +
> + iowrite32(0, timer_base + TIMER_CLRINT);
> +
> + evt->event_handler(evt);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void rps_timer_config(unsigned long period, unsigned periodic)
> +{
> + uint32_t cfg = 0;
> +
> + if (period)
> + cfg |= TIMER_ENABLE;
> +
> + if (periodic)
> + cfg |= TIMER_PERIODIC;
> +
> + switch (timer_prescaler) {
> + case 1:
> + cfg |= TIMER_DIV1;
> + break;
> + case 16:
> + cfg |= TIMER_DIV16;
> + break;
> + case 256:
> + cfg |= TIMER_DIV256;
> + break;
> + }

No direct value (1, 16, 256) but macros please.

> + iowrite32(period, timer_base + TIMER_LOAD);
> + iowrite32(cfg, timer_base + TIMER_CTRL);
> +}
> +
> +static int rps_timer_shutdown(struct clock_event_device *evt)
> +{
> + if (!clockevent_state_periodic(evt))
> + return 0;
> +
> + rps_timer_config(0, 0);
> +
> + return 0;
> +}
> +
> +static int rps_timer_set_periodic(struct clock_event_device *evt)
> +{
> + rps_timer_config(timer_period, 1);
> +
> + return 0;
> +}
> +
> +static int rps_timer_set_oneshot(struct clock_event_device *evt)
> +{
> + rps_timer_config(timer_period, 0);
> +
> + return 0;
> +}
> +
> +static int rps_timer_next_event(unsigned long delta,
> + struct clock_event_device *evt)
> +{
> + rps_timer_config(delta, 0);
> +
> + return 0;
> +}
> +
> +static struct clock_event_device rps_clockevent = {
> + .name = "rps",
> + .features = CLOCK_EVT_FEAT_PERIODIC |
> + CLOCK_EVT_FEAT_ONESHOT,
> + .tick_resume = rps_timer_shutdown,
> + .set_state_shutdown = rps_timer_shutdown,
> + .set_state_periodic = rps_timer_set_periodic,
> + .set_state_oneshot = rps_timer_set_oneshot,
> + .set_next_event = rps_timer_next_event,
> + .rating = 200,
> +};
> +
> +static void __init rps_clockevent_init(void __iomem *base, ulong ref_rate,
> + int irq)
> +{
> + timer_base = base;
> +
> + /* Start with prescaler 1 */
> + timer_prescaler = 1;
> + timer_period = DIV_ROUND_UP(ref_rate, HZ);
> +
> + if (timer_period > TIMER_MAX_VAL) {
> + timer_prescaler = 16;
> + timer_period = DIV_ROUND_UP(ref_rate / timer_prescaler, HZ);

Using macro and bit shifting will be nicer.

> + }
> + if (timer_period > TIMER_MAX_VAL) {
> + timer_prescaler = 256;
> + timer_period = DIV_ROUND_UP(ref_rate / timer_prescaler, HZ);
> + }
> + rps_clockevent.cpumask = cpu_possible_mask;
> + rps_clockevent.irq = irq;

So if you are using such cpumask, I suggest you have a look at the
DYNIRQ flag.

> + clockevents_config_and_register(&rps_clockevent,
> + ref_rate / timer_prescaler,
> + 1,
> + TIMER_MAX_VAL);
> +
> + pr_info("rps: Registered clock event rate %luHz prescaler %d period %lu\n",
> + ref_rate,
> + timer_prescaler,
> + timer_period);
> +}
> +
> +/* Clocksource */
> +
> +static void __iomem *timer_curr;
> +
> +static u64 notrace rps_read_sched_clock(void)
> +{
> + return ~readl_relaxed(timer_curr);
> +}
> +
> +static void __init rps_clocksource_init(void __iomem *base, ulong ref_rate)
> +{
> + int ret;
> + ulong clock_rate;
> + /* use prescale 16 */
> + clock_rate = ref_rate / 16;
> +
> + iowrite32(TIMER_MAX_VAL, base + TIMER_LOAD);
> + iowrite32(TIMER_PERIODIC | TIMER_ENABLE | TIMER_DIV16,
> + base + TIMER_CTRL);
> +
> + timer_curr = base + TIMER_CURR;
> + sched_clock_register(rps_read_sched_clock, TIMER_BITS, clock_rate);
> + ret = clocksource_mmio_init(base + TIMER_CURR, "rps_clocksource_timer",
> + clock_rate, 250, TIMER_BITS,
> + clocksource_mmio_readl_down);
> + if (ret)
> + panic("can't register clocksource\n");

No panic, just an error.

> + pr_info("rps: Registered clocksource rate %luHz\n", clock_rate);
> +}
> +
> +static struct irqaction rps_timer_irqaction = {
> + .name = "rps_timer",
> + .flags = IRQF_TIMER | IRQF_IRQPOLL,
> + .handler = rps_timer_irq,
> + .dev_id = &rps_clockevent,
> +};
> +
> +static void __init rps_timer_init(struct device_node *np)
> +{
> + struct clk *refclk;
> + unsigned long ref_rate;
> + void __iomem *base;
> + int irq, ret;
> +
> + refclk = of_clk_get(np, 0);
> +
> + if (IS_ERR(refclk) || clk_prepare_enable(refclk))
> + panic("rps_timer_init: failed to get refclk\n");

no panic.

> + ref_rate = clk_get_rate(refclk);
> +
> + base = of_iomap(np, 0);
> + if (!base)
> + panic("rps_timer_init: failed to map io\n");
> +
> + irq = irq_of_parse_and_map(np, 0);
> + if (irq < 0)
> + panic("rps_timer_init: failed to parse IRQ\n");

if (!irq)
no panic.

> +
> + /* Disable timers */
> + iowrite32(0, base + TIMER1_OFFSET + TIMER_CTRL);
> + iowrite32(0, base + TIMER2_OFFSET + TIMER_CTRL);
> + iowrite32(0, base + TIMER1_OFFSET + TIMER_LOAD);
> + iowrite32(0, base + TIMER2_OFFSET + TIMER_LOAD);
> + iowrite32(0, base + TIMER1_OFFSET + TIMER_CLRINT);
> + iowrite32(0, base + TIMER2_OFFSET + TIMER_CLRINT);
> +
> + rps_clocksource_init(base + TIMER2_OFFSET, ref_rate);
> + rps_clockevent_init(base + TIMER1_OFFSET, ref_rate, irq);
> +
> + ret = setup_irq(irq, &rps_timer_irqaction);

Replace setup_irq by request_irq.

> + if (ret)
> + panic("rps_timer_init: failed to request irq\n");
> +}
> +
> +CLOCKSOURCE_OF_DECLARE(nas782x, "plxtech,nas782x-rps-timer", rps_timer_init);


Thanks !

-- Daniel


--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog

2016-03-17 16:40:24

by Daniel Lezcano

[permalink] [raw]
Subject: Re: [PATCH v2 01/18] clocksource: sp804: Add support for non-32bit width counter

On 03/09/2016 11:24 AM, Neil Armstrong wrote:
> Some vendor variants can implement norrower counter width, add
> an optional DT property changing the clocksource width and the
> clockevent mask, but keeping 32bit as default for legacy interface.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/clocksource/timer-sp804.c | 38 +++++++++++++++++++++++++++-----------
> include/clocksource/timer-sp804.h | 11 ++++++-----
> 2 files changed, 33 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c
> index 5f45b9a..8acf524 100644
> --- a/drivers/clocksource/timer-sp804.c
> +++ b/drivers/clocksource/timer-sp804.c
> @@ -80,7 +80,8 @@ void __init sp804_timer_disable(void __iomem *base)
> void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
> const char *name,
> struct clk *clk,
> - int use_sched_clock)
> + int use_sched_clock,
> + unsigned width)
> {
> long rate;
>
> @@ -93,6 +94,9 @@ void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
> }
> }
>
> + if (!width || width > 32)
> + width = 32;
> +

check comment below in the caller function.

> rate = sp804_get_clock_rate(clk);
>
> if (rate < 0)
> @@ -106,11 +110,11 @@ void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
> base + TIMER_CTRL);
>
> clocksource_mmio_init(base + TIMER_VALUE, name,
> - rate, 200, 32, clocksource_mmio_readl_down);
> + rate, 200, width, clocksource_mmio_readl_down);
>
> if (use_sched_clock) {
> sched_clock_base = base;
> - sched_clock_register(sp804_read, 32, rate);
> + sched_clock_register(sp804_read, width, rate);
> }
> }
>
> @@ -186,7 +190,9 @@ static struct irqaction sp804_timer_irq = {
> .dev_id = &sp804_clockevent,
> };
>
> -void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name)
> +void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq,
> + struct clk *clk, const char *name,
> + unsigned width)
> {
> struct clock_event_device *evt = &sp804_clockevent;
> long rate;
> @@ -199,6 +205,9 @@ void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struc
> return;
> }
>
> + if (!width || width > 32)
> + width = 32;

Check comment below in the caller function.

> rate = sp804_get_clock_rate(clk);
> if (rate < 0)
> return;
> @@ -212,7 +221,7 @@ void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struc
> writel(0, base + TIMER_CTRL);
>
> setup_irq(irq, &sp804_timer_irq);
> - clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
> + clockevents_config_and_register(evt, rate, 0xf, GENMASK(width-1, 0));

This GENMASK is strange here. Nothing else than this ?

> }
>
> static void __init sp804_of_init(struct device_node *np)
> @@ -223,6 +232,7 @@ static void __init sp804_of_init(struct device_node *np)
> u32 irq_num = 0;
> struct clk *clk1, *clk2;
> const char *name = of_get_property(np, "compatible", NULL);
> + u32 width = 32;
>
> base = of_iomap(np, 0);
> if (WARN_ON(!base))
> @@ -254,14 +264,19 @@ static void __init sp804_of_init(struct device_node *np)
> if (irq <= 0)
> goto err;
>
> + /* Some vendor variants can have a different counter width */
> + of_property_read_u32(np, "arm,timer-width", &width);
> +

Better to do the sanity check when the value is read.

Here, you can default to 32 if the width is 0 instead of initializing to
32 then read the property and call __sp804_clockevents_init which in
turn check zero or more and set it to 32 again.

> of_property_read_u32(np, "arm,sp804-has-irq", &irq_num);
> if (irq_num == 2) {
> - __sp804_clockevents_init(base + TIMER_2_BASE, irq, clk2, name);
> - __sp804_clocksource_and_sched_clock_init(base, name, clk1, 1);
> + __sp804_clockevents_init(base + TIMER_2_BASE, irq,
> + clk2, name, width);
> + __sp804_clocksource_and_sched_clock_init(base, name,
> + clk1, 1, width);
> } else {
> - __sp804_clockevents_init(base, irq, clk1 , name);
> + __sp804_clockevents_init(base, irq, clk1, name, width);
> __sp804_clocksource_and_sched_clock_init(base + TIMER_2_BASE,
> - name, clk2, 1);
> + name, clk2, 1, width);
> }
> initialized = true;
>
> @@ -293,13 +308,14 @@ static void __init integrator_cp_of_init(struct device_node *np)
> goto err;
>
> if (!init_count)
> - __sp804_clocksource_and_sched_clock_init(base, name, clk, 0);
> + __sp804_clocksource_and_sched_clock_init(base, name,
> + clk, 0, 32);
> else {
> irq = irq_of_parse_and_map(np, 0);
> if (irq <= 0)
> goto err;
>
> - __sp804_clockevents_init(base, irq, clk, name);
> + __sp804_clockevents_init(base, irq, clk, name, 32);
> }
>
> init_count++;
> diff --git a/include/clocksource/timer-sp804.h b/include/clocksource/timer-sp804.h
> index 1f8a1ca..ad71fcb 100644
> --- a/include/clocksource/timer-sp804.h
> +++ b/include/clocksource/timer-sp804.h
> @@ -4,25 +4,26 @@
> struct clk;
>
> void __sp804_clocksource_and_sched_clock_init(void __iomem *,
> - const char *, struct clk *, int);
> + const char *, struct clk *,
> + int, unsigned);
> void __sp804_clockevents_init(void __iomem *, unsigned int,
> - struct clk *, const char *);
> + struct clk *, const char *, unsigned);
> void sp804_timer_disable(void __iomem *);
>
> static inline void sp804_clocksource_init(void __iomem *base, const char *name)
> {
> - __sp804_clocksource_and_sched_clock_init(base, name, NULL, 0);
> + __sp804_clocksource_and_sched_clock_init(base, name, NULL, 0, 32);
> }
>
> static inline void sp804_clocksource_and_sched_clock_init(void __iomem *base,
> const char *name)
> {
> - __sp804_clocksource_and_sched_clock_init(base, name, NULL, 1);
> + __sp804_clocksource_and_sched_clock_init(base, name, NULL, 1, 32);
> }
>
> static inline void sp804_clockevents_init(void __iomem *base, unsigned int irq, const char *name)
> {
> - __sp804_clockevents_init(base, irq, NULL, name);
> + __sp804_clockevents_init(base, irq, NULL, name, 32);
>
> }
> #endif
>


--
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog

2016-03-17 17:09:34

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v2 02/18] dt-bindings: timer: sp804: add timer-width property

On Wed, Mar 09, 2016 at 11:24:04AM +0100, Neil Armstrong wrote:
> Add timer-width optional property to specify a different vendor
> specific timer counter bit-width.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> Documentation/devicetree/bindings/timer/arm,sp804.txt | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/timer/arm,sp804.txt b/Documentation/devicetree/bindings/timer/arm,sp804.txt
> index 5cd8eee7..141e143 100644
> --- a/Documentation/devicetree/bindings/timer/arm,sp804.txt
> +++ b/Documentation/devicetree/bindings/timer/arm,sp804.txt
> @@ -17,6 +17,8 @@ Optional properties:
> - arm,sp804-has-irq = <#>: In the case of only 1 timer irq line connected, this
> specifies if the irq connection is for timer 1 or timer 2. A value of 1
> or 2 should be used.
> +- arm,timer-width: Should contain the width in number of bits of the counter,
> + is considered by default 32 but can be changed for vendor variants.

That would not be an SP804 nor would the vendor be ARM in that case. So
add a new compatible string for the vendor that decided to hack up ARM's
IP block.

Rob

2016-03-17 17:15:28

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v2 04/18] dt-bindings: irq: arm,versatile-fpga: add arm,rps-irq compatible string

On Wed, Mar 09, 2016 at 11:24:06AM +0100, Neil Armstrong wrote:
> Under the OX810SE, this same controller is used as "Reference Peripheral
> Specification" Interrupt Controller, so add new compatible string.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> .../devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt
> index c9cf605..2fe78d5 100644
> --- a/Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt
> +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,versatile-fpga-irq.txt
> @@ -6,7 +6,7 @@ controllers are OR:ed together and fed to the CPU tile's IRQ input. Each
> instance can handle up to 32 interrupts.
>
> Required properties:
> -- compatible: "arm,versatile-fpga-irq"
> +- compatible: "arm,versatile-fpga-irq" or "arm,rps-irq"

Use a compatible string that reflects the actual implementation not a
spec. The current string is a bad example as it already refers to
multiple implementations.

Rob

2016-03-17 17:15:57

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v2 05/18] dt-bindings: vendor-prefixes: Add PLX Technology

On Wed, Mar 09, 2016 at 11:24:07AM +0100, Neil Armstrong wrote:
> Add PLX Technology vendor prefix.
> Fixed "pixdir" alphabetizing.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.txt | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)

Acked-by: Rob Herring <[email protected]>

2016-03-17 17:16:32

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v2 06/18] dt-bindings: Add Oxford Semiconductors to vendor prefixes

On Wed, Mar 09, 2016 at 11:24:08AM +0100, Neil Armstrong wrote:
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> 1 file changed, 1 insertion(+)

Acked-by: Rob Herring <[email protected]>

2016-03-17 17:18:47

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v2 08/18] dt-bindings: Add PLX Technology Reset Controller bindings

On Wed, Mar 09, 2016 at 11:24:10AM +0100, Neil Armstrong wrote:
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> .../devicetree/bindings/reset/plxtech,reset.txt | 58 ++++++++++++++++++++++
> 1 file changed, 58 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/reset/plxtech,reset.txt

Acked-by: Rob Herring <[email protected]>

2016-03-17 17:19:54

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v2 10/18] dt-bindings: Add PLX Technology OXNAS Standard Clocks bindings

On Wed, Mar 09, 2016 at 11:24:12AM +0100, Neil Armstrong wrote:
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> .../devicetree/bindings/clock/plxtech,stdclk.txt | 35 ++++++++++++++++++++++
> 1 file changed, 35 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/plxtech,stdclk.txt

Acked-by: Rob Herring <[email protected]>

2016-03-17 17:25:59

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v2 12/18] dt-bindings: Add PLX Technology OXNAS pinctrl and gpio bindings

On Wed, Mar 09, 2016 at 11:24:14AM +0100, Neil Armstrong wrote:
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> .../devicetree/bindings/gpio/gpio_oxnas.txt | 27 ++++++
> .../bindings/pinctrl/plxtech,pinctrl.txt | 100 +++++++++++++++++++++
> 2 files changed, 127 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
> create mode 100644 Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt
>
> diff --git a/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt b/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
> new file mode 100644
> index 0000000..cbb03c4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
> @@ -0,0 +1,27 @@
> +PLX Technology OXNAS SoC GPIO Controller
> +==========================================
> +
> +Required properties:
> +- compatible: "oxsemi,ox810se-gpio".
> +- reg: Should contain GPIO controller registers location and length
> +- interrupts: Should be the port interrupt shared by all the pins.
> +- #gpio-cells: Should be two. The first cell is the pin number and
> + the second cell is used to specify optional parameters (currently
> + unused).
> +- gpio-controller: Marks the device node as a GPIO controller.
> +
> +optional properties:
> +- #gpio-lines: Number of gpio if absent 32.
> +
> +
> +Example:
> + gpio0: gpio@000000 {

Drop the leading 0s.

> + compatible = "oxsemi,ox810se-gpio";
> + reg = <0x000000 0x100000>;
> + interrupts = <21>;
> + #gpio-cells = <2>;
> + gpio-controller;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + #gpio-lines = <32>;
> + };
> diff --git a/Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt
> new file mode 100644
> index 0000000..0c5051a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/plxtech,pinctrl.txt
> @@ -0,0 +1,100 @@
> +PLX Technology OXNAS SoC Pinmux Controller
> +==========================================
> +
> +The OXNAS Pinmux Controller, enables the IC to share one PAD to several
> +functional blocks. The sharing is done by multiplexing the PAD input/output
> +signals. For each PAD there are up to 8 muxing options (called periph modes).
> +Since different modules require different PAD settings
> +(like pull up, keeper, etc) the contoller controls also the PAD settings
> +parameters.
> +
> +Please refer to pinctrl-bindings.txt in this directory for details of the
> +common pinctrl bindings used by client devices, including the meaning of the
> +phrase "pin configuration node".
> +
> +OXNAS pin configuration node is a node of a group of pins which can be
> +used for a specific device or function. This node represents both mux and config
> +of the pins in that group. The 'pins' selects the function mode(also named pin
> +mode) this pin can work on and the 'config' configures various pad settings
> +such as pull-up, multi drive, etc.
> +
> +Required properties for iomux controller:
> +- compatible: "oxsemi,ox810se-pinctrl"
> +- plxtech,mux-mask: array of mask (periph per bank) to describe if a pin can be
> + configured in this periph mode. All the periph and bank need to be describe.
> +- plxtech,sys-ctrl: a phandle to the system controller syscon node
> +
> +How to create such array:
> +
> +Each column will represent the possible peripheral of the pinctrl
> +Each line will represent a pio bank
> +
> +For example :
> +Peripheral: 2 ( A and B)
> +Bank: 2 (A, B and C)
> +=>
> +
> + /* A B */
> + 0xffffffff 0xffc00c3b /* pioA */
> + 0xffffffff 0x7fff3ccf /* pioB */
> +
> +For each peripheral/bank we will descibe in a u32 if a pin can be
> +configured in it by putting 1 to the pin bit (1 << pin)
> +
> +Required properties for pin configuration node:
> +- plxtech,pins: 4 integers array, represents a group of pins mux and config
> + setting. The format is plxtech,pins = <PIN_BANK PIN_BANK_NUM PERIPH CONFIG>.
> + The PERIPH 0 means gpio, PERIPH 1 is periph A, PERIPH 2 is periph B...
> + PIN_BANK 0 is pioA, PIN_BANK 1 is pioB...
> +
> +Bits used for CONFIG:
> + - None Yet
> +
> +Examples:
> +
> +pinctrl: pinctrl {
> + compatible = "oxsemi,ox810se-pinctrl", "simple-bus";
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges;
> +
> + /* Regmap for sys registers */
> + plxtech,sys-ctrl = <&sys>;
> +
> + /* Default, all-open mux-map */
> + plxtech,mux-mask = <
> + 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
> + 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF
> + >;
> +
> + uart0 {
> + pinctrl_uart0: uart0 {
> + plxtech,pins = <0 31 3 0
> + 0 32 3 0>;
> + };
> + pinctrl_uart0_modem: uart0_modem {
> + plxtech,pins = <0 27 3 0
> + 0 28 3 0
> + 0 29 3 0
> + 0 30 3 0
> + 0 33 3 0
> + 0 34 3 0>;
> + };
> + };
> +};
> +
> +uart0: uart@200000 {

serial@200000

With those changes:

Acked-by: Rob Herring <[email protected]>

2016-03-17 17:27:48

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v2 16/18] dt-bindings: Add OXNAS bindings

On Wed, Mar 09, 2016 at 11:24:18AM +0100, Neil Armstrong wrote:
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> Documentation/devicetree/bindings/arm/oxnas.txt | 9 +++++++++
> 1 file changed, 9 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/oxnas.txt
>
> diff --git a/Documentation/devicetree/bindings/arm/oxnas.txt b/Documentation/devicetree/bindings/arm/oxnas.txt
> new file mode 100644
> index 0000000..f6032d2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/oxnas.txt
> @@ -0,0 +1,9 @@
> +PLX Technology OXNAS SoCs Family device tree bindings
> +-------------------------------------------
> +
> +Boards with the OX810SE Soc SoC shall have the following properties:

s/Soc SoC/SoC/

> + Required root node property:
> + compatible: "oxsemi,ox810se"
> +
> +Board compatible values:
> + - "wd,mbwe" (OX810SE)

Seems kind of generic. Only one version?

> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2016-03-17 18:06:16

by Robin Murphy

[permalink] [raw]
Subject: Re: [PATCH v2 02/18] dt-bindings: timer: sp804: add timer-width property

Hi Rob,

On 17/03/16 17:09, Rob Herring wrote:
> On Wed, Mar 09, 2016 at 11:24:04AM +0100, Neil Armstrong wrote:
>> Add timer-width optional property to specify a different vendor
>> specific timer counter bit-width.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> Documentation/devicetree/bindings/timer/arm,sp804.txt | 2 ++
>> 1 file changed, 2 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/timer/arm,sp804.txt b/Documentation/devicetree/bindings/timer/arm,sp804.txt
>> index 5cd8eee7..141e143 100644
>> --- a/Documentation/devicetree/bindings/timer/arm,sp804.txt
>> +++ b/Documentation/devicetree/bindings/timer/arm,sp804.txt
>> @@ -17,6 +17,8 @@ Optional properties:
>> - arm,sp804-has-irq = <#>: In the case of only 1 timer irq line connected, this
>> specifies if the irq connection is for timer 1 or timer 2. A value of 1
>> or 2 should be used.
>> +- arm,timer-width: Should contain the width in number of bits of the counter,
>> + is considered by default 32 but can be changed for vendor variants.
>
> That would not be an SP804 nor would the vendor be ARM in that case. So
> add a new compatible string for the vendor that decided to hack up ARM's
> IP block.

By all accounts this is some ancient reference design[1] which later
evolved _into_ the SP804, so that vendor would probably still be ARM ;)

A separate compatible string would indeed make more sense, though. Both
semantically and in terms of letting the driver account for the
differences automatically.

Robin.

[1]:http://infocenter.arm.com/help/topic/com.arm.doc.ddi0170a/I350250.html

>
> Rob
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

2016-03-17 19:01:15

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v2 02/18] dt-bindings: timer: sp804: add timer-width property

On Thu, Mar 17, 2016 at 1:06 PM, Robin Murphy <[email protected]> wrote:
> Hi Rob,
>
> On 17/03/16 17:09, Rob Herring wrote:
>>
>> On Wed, Mar 09, 2016 at 11:24:04AM +0100, Neil Armstrong wrote:
>>>
>>> Add timer-width optional property to specify a different vendor
>>> specific timer counter bit-width.
>>>
>>> Signed-off-by: Neil Armstrong <[email protected]>
>>> ---
>>> Documentation/devicetree/bindings/timer/arm,sp804.txt | 2 ++
>>> 1 file changed, 2 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/timer/arm,sp804.txt
>>> b/Documentation/devicetree/bindings/timer/arm,sp804.txt
>>> index 5cd8eee7..141e143 100644
>>> --- a/Documentation/devicetree/bindings/timer/arm,sp804.txt
>>> +++ b/Documentation/devicetree/bindings/timer/arm,sp804.txt
>>> @@ -17,6 +17,8 @@ Optional properties:
>>> - arm,sp804-has-irq = <#>: In the case of only 1 timer irq line
>>> connected, this
>>> specifies if the irq connection is for timer 1 or timer 2. A
>>> value of 1
>>> or 2 should be used.
>>> +- arm,timer-width: Should contain the width in number of bits of the
>>> counter,
>>> + is considered by default 32 but can be changed for vendor
>>> variants.
>>
>>
>> That would not be an SP804 nor would the vendor be ARM in that case. So
>> add a new compatible string for the vendor that decided to hack up ARM's
>> IP block.
>
>
> By all accounts this is some ancient reference design[1] which later evolved
> _into_ the SP804, so that vendor would probably still be ARM ;)

Right.

> A separate compatible string would indeed make more sense, though. Both
> semantically and in terms of letting the driver account for the differences
> automatically.
>
> Robin.
>
> [1]:http://infocenter.arm.com/help/topic/com.arm.doc.ddi0170a/I350250.html

Humm, same as integrator timers perhaps?

Rob

>
>>
>> Rob
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> [email protected]
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
>

2016-03-17 19:21:29

by Robin Murphy

[permalink] [raw]
Subject: Re: [PATCH v2 02/18] dt-bindings: timer: sp804: add timer-width property

On 17/03/16 19:00, Rob Herring wrote:
> On Thu, Mar 17, 2016 at 1:06 PM, Robin Murphy <[email protected]> wrote:
>> Hi Rob,
>>
>> On 17/03/16 17:09, Rob Herring wrote:
>>>
>>> On Wed, Mar 09, 2016 at 11:24:04AM +0100, Neil Armstrong wrote:
>>>>
>>>> Add timer-width optional property to specify a different vendor
>>>> specific timer counter bit-width.
>>>>
>>>> Signed-off-by: Neil Armstrong <[email protected]>
>>>> ---
>>>> Documentation/devicetree/bindings/timer/arm,sp804.txt | 2 ++
>>>> 1 file changed, 2 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/timer/arm,sp804.txt
>>>> b/Documentation/devicetree/bindings/timer/arm,sp804.txt
>>>> index 5cd8eee7..141e143 100644
>>>> --- a/Documentation/devicetree/bindings/timer/arm,sp804.txt
>>>> +++ b/Documentation/devicetree/bindings/timer/arm,sp804.txt
>>>> @@ -17,6 +17,8 @@ Optional properties:
>>>> - arm,sp804-has-irq = <#>: In the case of only 1 timer irq line
>>>> connected, this
>>>> specifies if the irq connection is for timer 1 or timer 2. A
>>>> value of 1
>>>> or 2 should be used.
>>>> +- arm,timer-width: Should contain the width in number of bits of the
>>>> counter,
>>>> + is considered by default 32 but can be changed for vendor
>>>> variants.
>>>
>>>
>>> That would not be an SP804 nor would the vendor be ARM in that case. So
>>> add a new compatible string for the vendor that decided to hack up ARM's
>>> IP block.
>>
>>
>> By all accounts this is some ancient reference design[1] which later evolved
>> _into_ the SP804, so that vendor would probably still be ARM ;)
>
> Right.
>
>> A separate compatible string would indeed make more sense, though. Both
>> semantically and in terms of letting the driver account for the differences
>> automatically.
>>
>> Robin.
>>
>> [1]:http://infocenter.arm.com/help/topic/com.arm.doc.ddi0170a/I350250.html
>
> Humm, same as integrator timers perhaps?

Having had a quick look, what the Integrator/AP manual describes
certainly smells like the same basic block as the "AMBA Timer" - 16 bit
counters and the same control register layout - albeit in a mutant
triple-timer version with a bigger offset between each register set.
Integrator/CP, on the other hand, looks much more SP804-like.

Robin.

2016-03-22 09:21:33

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v2 02/18] dt-bindings: timer: sp804: add timer-width property

On 03/17/2016 08:21 PM, Robin Murphy wrote:
> On 17/03/16 19:00, Rob Herring wrote:
>> On Thu, Mar 17, 2016 at 1:06 PM, Robin Murphy <[email protected]> wrote:
>>> Hi Rob,
>>>
>>> On 17/03/16 17:09, Rob Herring wrote:
>>>> That would not be an SP804 nor would the vendor be ARM in that case. So
>>>> add a new compatible string for the vendor that decided to hack up ARM's
>>>> IP block.
>>>
>>>
>>> By all accounts this is some ancient reference design[1] which later evolved
>>> _into_ the SP804, so that vendor would probably still be ARM ;)
>>
>> Right.
>>
>>> A separate compatible string would indeed make more sense, though. Both
>>> semantically and in terms of letting the driver account for the differences
>>> automatically.
>>>
>>> Robin.
>>>
>>> [1]:http://infocenter.arm.com/help/topic/com.arm.doc.ddi0170a/I350250.html
>>
>> Humm, same as integrator timers perhaps?
>
> Having had a quick look, what the Integrator/AP manual describes certainly smells like the same basic block as the "AMBA Timer" - 16 bit counters and the same control register layout - albeit in a mutant triple-timer version with a bigger offset between each register set. Integrator/CP, on the other hand, looks much more SP804-like.
>
> Robin.
>

Hi,

I will switch to oxsemi,ox810se-rps-timer since it need a specific register width that will be handled by the driver.

Thanks,
Neil

2016-03-22 12:02:46

by Robin Murphy

[permalink] [raw]
Subject: Re: [PATCH v2 02/18] dt-bindings: timer: sp804: add timer-width property

Hi Neil,

On 22/03/16 09:21, Neil Armstrong wrote:
> On 03/17/2016 08:21 PM, Robin Murphy wrote:
>> On 17/03/16 19:00, Rob Herring wrote:
>>> On Thu, Mar 17, 2016 at 1:06 PM, Robin Murphy <[email protected]> wrote:
>>>> Hi Rob,
>>>>
>>>> On 17/03/16 17:09, Rob Herring wrote:
>>>>> That would not be an SP804 nor would the vendor be ARM in that case. So
>>>>> add a new compatible string for the vendor that decided to hack up ARM's
>>>>> IP block.
>>>>
>>>>
>>>> By all accounts this is some ancient reference design[1] which later evolved
>>>> _into_ the SP804, so that vendor would probably still be ARM ;)
>>>
>>> Right.
>>>
>>>> A separate compatible string would indeed make more sense, though. Both
>>>> semantically and in terms of letting the driver account for the differences
>>>> automatically.
>>>>
>>>> Robin.
>>>>
>>>> [1]:http://infocenter.arm.com/help/topic/com.arm.doc.ddi0170a/I350250.html
>>>
>>> Humm, same as integrator timers perhaps?
>>
>> Having had a quick look, what the Integrator/AP manual describes certainly smells like the same basic block as the "AMBA Timer" - 16 bit counters and the same control register layout - albeit in a mutant triple-timer version with a bigger offset between each register set. Integrator/CP, on the other hand, looks much more SP804-like.
>>
>> Robin.
>>
>
> Hi,
>
> I will switch to oxsemi,ox810se-rps-timer since it need a specific register width that will be handled by the driver.

By "needs a specific register width" do you mean the OxSemi
implementation will give a bus error on a 32-bit access and requires
16-bit accessors? If so, I'd expect to see patch 1 changing readl()s to
readw()s at least somewhere. Otherwise, if it's merely that the
clocksource API needs to know the upper 16 bits of a word it reads are
undefined, then since that's the standard behaviour I'd be inclined to
add it to the driver as a canonical "arm,amba-timer" implementation,
then have your implementation-specific compatible on top of that just in
case.

Robin.

>
> Thanks,
> Neil
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

2016-03-22 14:29:53

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v2 02/18] dt-bindings: timer: sp804: add timer-width property

On 03/22/2016 01:02 PM, Robin Murphy wrote:
> Hi Neil,
>>>>
>>>> Humm, same as integrator timers perhaps?
>>>
>>> Having had a quick look, what the Integrator/AP manual describes certainly smells like the same basic block as the "AMBA Timer" - 16 bit counters and the same control register layout - albeit in a mutant triple-timer version with a bigger offset between each register set. Integrator/CP, on the other hand, looks much more SP804-like.
>>>
>>> Robin.
>>>
>>
>> Hi,
>>
>> I will switch to oxsemi,ox810se-rps-timer since it need a specific register width that will be handled by the driver.
>
> By "needs a specific register width" do you mean the OxSemi implementation will give a bus error on a 32-bit access and requires 16-bit accessors? If so, I'd expect to see patch 1 changing readl()s to readw()s at least somewhere. Otherwise, if it's merely that the clocksource API needs to know the upper 16 bits of a word it reads are undefined, then since that's the standard behaviour I'd be inclined to add it to the driver as a canonical "arm,amba-timer" implementation, then have your implementation-specific compatible on top of that just in case.

No actually the bus access is 32bit but the counter is 24bit wide instead of 16bit, so the clocksource won't work and the system time will furiously drift. It's not the case of the clockevent since the delay fits in 24 bits.

It also seems is ignores the 32BIT config bit, so it seems based on the initial 16bit only reference design.

How do you think I should implement this ?

Neil

> Robin.
>
>>
>> Thanks,
>> Neil
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> [email protected]
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
>

2016-03-23 08:38:13

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v2 16/18] dt-bindings: Add OXNAS bindings

On 03/17/2016 06:27 PM, Rob Herring wrote:
> On Wed, Mar 09, 2016 at 11:24:18AM +0100, Neil Armstrong wrote:
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> Documentation/devicetree/bindings/arm/oxnas.txt | 9 +++++++++
>> 1 file changed, 9 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/arm/oxnas.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/oxnas.txt b/Documentation/devicetree/bindings/arm/oxnas.txt
>> new file mode 100644
>> index 0000000..f6032d2
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/oxnas.txt
>> @@ -0,0 +1,9 @@
>> +PLX Technology OXNAS SoCs Family device tree bindings
>> +-------------------------------------------
>> +
>> +Boards with the OX810SE Soc SoC shall have the following properties:
>
> s/Soc SoC/SoC/

OK
>
>> + Required root node property:
>> + compatible: "oxsemi,ox810se"
>> +
>> +Board compatible values:
>> + - "wd,mbwe" (OX810SE)
>
> Seems kind of generic. Only one version?

There is a My Book World Edition II, which seems to be the same (they share the same firmware) but with two SATA ports, the original kernel does not make any differences between the two models.

I'm not aware of different versions and the firmware does not contain any specific code for different board revisions.

Neil