summaryrefslogtreecommitdiffstats
path: root/arch/mips/ralink
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/ralink')
-rw-r--r--arch/mips/ralink/Kconfig110
-rw-r--r--arch/mips/ralink/Makefile28
-rw-r--r--arch/mips/ralink/Platform33
-rw-r--r--arch/mips/ralink/bootrom.c29
-rw-r--r--arch/mips/ralink/cevt-rt3352.c153
-rw-r--r--arch/mips/ralink/clk.c86
-rw-r--r--arch/mips/ralink/common.h28
-rw-r--r--arch/mips/ralink/early_printk.c88
-rw-r--r--arch/mips/ralink/ill_acc.c91
-rw-r--r--arch/mips/ralink/irq-gic.c23
-rw-r--r--arch/mips/ralink/irq.c204
-rw-r--r--arch/mips/ralink/mt7620.c256
-rw-r--r--arch/mips/ralink/mt7621.c213
-rw-r--r--arch/mips/ralink/of.c120
-rw-r--r--arch/mips/ralink/prom.c65
-rw-r--r--arch/mips/ralink/reset.c43
-rw-r--r--arch/mips/ralink/rt288x.c110
-rw-r--r--arch/mips/ralink/rt305x.c215
-rw-r--r--arch/mips/ralink/rt3883.c110
-rw-r--r--arch/mips/ralink/timer-gic.c22
-rw-r--r--arch/mips/ralink/timer.c150
21 files changed, 2177 insertions, 0 deletions
diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig
new file mode 100644
index 0000000000..08c012a259
--- /dev/null
+++ b/arch/mips/ralink/Kconfig
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: GPL-2.0
+if RALINK
+
+config CLKEVT_RT3352
+ bool
+ depends on SOC_RT305X || SOC_MT7620
+ default y
+ select TIMER_OF
+ select CLKSRC_MMIO
+
+config RALINK_ILL_ACC
+ bool
+ depends on SOC_RT305X
+ default y
+
+config IRQ_INTC
+ bool
+ default y
+ depends on !SOC_MT7621
+
+choice
+ prompt "Ralink SoC selection"
+ default SOC_RT305X
+ help
+ Select Ralink MIPS SoC type.
+
+ config SOC_RT288X
+ bool "RT288x"
+ select MIPS_AUTO_PFN_OFFSET
+ select MIPS_L1_CACHE_SHIFT_4
+ select HAVE_PCI
+ select SOC_BUS
+
+ config SOC_RT305X
+ bool "RT305x"
+ select SOC_BUS
+
+ config SOC_RT3883
+ bool "RT3883"
+ select HAVE_PCI
+ select SOC_BUS
+
+ config SOC_MT7620
+ bool "MT7620/8"
+ select CPU_MIPSR2_IRQ_VI
+ select HAVE_PCI
+ select SOC_BUS
+
+ config SOC_MT7621
+ bool "MT7621"
+ select MIPS_CPU_SCACHE
+ select SYS_SUPPORTS_MULTITHREADING
+ select SYS_SUPPORTS_SMP
+ select SYS_SUPPORTS_MIPS_CPS
+ select SYS_SUPPORTS_HIGHMEM
+ select MIPS_GIC
+ select CLKSRC_MIPS_GIC
+ select HAVE_PCI
+ select PCI_DRIVERS_GENERIC
+ select SOC_BUS
+ select PINCTRL
+
+ help
+ The MT7621 system-on-a-chip includes an 880 MHz MIPS1004Kc
+ dual-core CPU, a 5-port 10/100/1000 switch/PHY and one RGMII.
+endchoice
+
+choice
+ prompt "Devicetree selection"
+ depends on !SOC_MT7621
+ default DTB_RT_NONE
+ help
+ Select the devicetree.
+
+ config DTB_RT_NONE
+ bool "None"
+
+ config DTB_RT2880_EVAL
+ bool "RT2880 eval kit"
+ depends on SOC_RT288X
+ select BUILTIN_DTB
+
+ config DTB_RT305X_EVAL
+ bool "RT305x eval kit"
+ depends on SOC_RT305X
+ select BUILTIN_DTB
+
+ config DTB_RT3883_EVAL
+ bool "RT3883 eval kit"
+ depends on SOC_RT3883
+ select BUILTIN_DTB
+
+ config DTB_MT7620A_EVAL
+ bool "MT7620A eval kit"
+ depends on SOC_MT7620
+ select BUILTIN_DTB
+
+ config DTB_OMEGA2P
+ bool "Onion Omega2+"
+ depends on SOC_MT7620
+ select BUILTIN_DTB
+
+ config DTB_VOCORE2
+ bool "VoCore2"
+ depends on SOC_MT7620
+ select BUILTIN_DTB
+
+endchoice
+
+endif
diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile
new file mode 100644
index 0000000000..26fabbdea1
--- /dev/null
+++ b/arch/mips/ralink/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Makefile for the Ralink common stuff
+#
+# Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
+# Copyright (C) 2013 John Crispin <john@phrozen.org>
+
+obj-y := prom.o of.o reset.o
+
+ifndef CONFIG_MIPS_GIC
+ obj-y += clk.o timer.o
+endif
+
+obj-$(CONFIG_CLKEVT_RT3352) += cevt-rt3352.o
+
+obj-$(CONFIG_RALINK_ILL_ACC) += ill_acc.o
+
+obj-$(CONFIG_IRQ_INTC) += irq.o
+obj-$(CONFIG_MIPS_GIC) += irq-gic.o timer-gic.o
+
+obj-$(CONFIG_SOC_RT288X) += rt288x.o
+obj-$(CONFIG_SOC_RT305X) += rt305x.o
+obj-$(CONFIG_SOC_RT3883) += rt3883.o
+obj-$(CONFIG_SOC_MT7620) += mt7620.o
+obj-$(CONFIG_SOC_MT7621) += mt7621.o
+
+obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+
+obj-$(CONFIG_DEBUG_FS) += bootrom.o
diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform
new file mode 100644
index 0000000000..02ee079148
--- /dev/null
+++ b/arch/mips/ralink/Platform
@@ -0,0 +1,33 @@
+#
+# Ralink SoC common stuff
+#
+cflags-$(CONFIG_RALINK) += -I$(srctree)/arch/mips/include/asm/mach-ralink
+
+#
+# Ralink RT288x
+#
+load-$(CONFIG_SOC_RT288X) += 0xffffffff88000000
+cflags-$(CONFIG_SOC_RT288X) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt288x
+
+#
+# Ralink RT305x
+#
+load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000
+cflags-$(CONFIG_SOC_RT305X) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt305x
+
+#
+# Ralink RT3883
+#
+load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000
+cflags-$(CONFIG_SOC_RT3883) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt3883
+
+#
+# Ralink MT7620
+#
+load-$(CONFIG_SOC_MT7620) += 0xffffffff80000000
+cflags-$(CONFIG_SOC_MT7620) += -I$(srctree)/arch/mips/include/asm/mach-ralink/mt7620
+
+# Ralink MT7621
+#
+load-$(CONFIG_SOC_MT7621) += 0xffffffff80001000
+cflags-$(CONFIG_SOC_MT7621) += -I$(srctree)/arch/mips/include/asm/mach-ralink/mt7621
diff --git a/arch/mips/ralink/bootrom.c b/arch/mips/ralink/bootrom.c
new file mode 100644
index 0000000000..8c8cc0a81e
--- /dev/null
+++ b/arch/mips/ralink/bootrom.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#define BOOTROM_OFFSET 0x10118000
+#define BOOTROM_SIZE 0x8000
+
+static void __iomem *membase = (void __iomem *) KSEG1ADDR(BOOTROM_OFFSET);
+
+static int bootrom_show(struct seq_file *s, void *unused)
+{
+ seq_write(s, membase, BOOTROM_SIZE);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(bootrom);
+
+static int __init bootrom_setup(void)
+{
+ debugfs_create_file("bootrom", 0444, NULL, NULL, &bootrom_fops);
+ return 0;
+}
+
+postcore_initcall(bootrom_setup);
diff --git a/arch/mips/ralink/cevt-rt3352.c b/arch/mips/ralink/cevt-rt3352.c
new file mode 100644
index 0000000000..269d4877d1
--- /dev/null
+++ b/arch/mips/ralink/cevt-rt3352.c
@@ -0,0 +1,153 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2013 by John Crispin <john@phrozen.org>
+ */
+
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
+#include <linux/reset.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#define SYSTICK_FREQ (50 * 1000)
+
+#define SYSTICK_CONFIG 0x00
+#define SYSTICK_COMPARE 0x04
+#define SYSTICK_COUNT 0x08
+
+/* route systick irq to mips irq 7 instead of the r4k-timer */
+#define CFG_EXT_STK_EN 0x2
+/* enable the counter */
+#define CFG_CNT_EN 0x1
+
+struct systick_device {
+ void __iomem *membase;
+ struct clock_event_device dev;
+ int irq_requested;
+ int freq_scale;
+};
+
+static int systick_set_oneshot(struct clock_event_device *evt);
+static int systick_shutdown(struct clock_event_device *evt);
+
+static int systick_next_event(unsigned long delta,
+ struct clock_event_device *evt)
+{
+ struct systick_device *sdev;
+ u32 count;
+
+ sdev = container_of(evt, struct systick_device, dev);
+ count = ioread32(sdev->membase + SYSTICK_COUNT);
+ count = (count + delta) % SYSTICK_FREQ;
+ iowrite32(count, sdev->membase + SYSTICK_COMPARE);
+
+ return 0;
+}
+
+static void systick_event_handler(struct clock_event_device *dev)
+{
+ /* noting to do here */
+}
+
+static irqreturn_t systick_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *dev = (struct clock_event_device *) dev_id;
+
+ dev->event_handler(dev);
+
+ return IRQ_HANDLED;
+}
+
+static struct systick_device systick = {
+ .dev = {
+ /*
+ * cevt-r4k uses 300, make sure systick
+ * gets used if available
+ */
+ .rating = 310,
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .set_next_event = systick_next_event,
+ .set_state_shutdown = systick_shutdown,
+ .set_state_oneshot = systick_set_oneshot,
+ .event_handler = systick_event_handler,
+ },
+};
+
+static int systick_shutdown(struct clock_event_device *evt)
+{
+ struct systick_device *sdev;
+
+ sdev = container_of(evt, struct systick_device, dev);
+
+ if (sdev->irq_requested)
+ free_irq(systick.dev.irq, &systick.dev);
+ sdev->irq_requested = 0;
+ iowrite32(0, systick.membase + SYSTICK_CONFIG);
+
+ return 0;
+}
+
+static int systick_set_oneshot(struct clock_event_device *evt)
+{
+ const char *name = systick.dev.name;
+ struct systick_device *sdev;
+ int irq = systick.dev.irq;
+
+ sdev = container_of(evt, struct systick_device, dev);
+
+ if (!sdev->irq_requested) {
+ if (request_irq(irq, systick_interrupt,
+ IRQF_PERCPU | IRQF_TIMER, name, &systick.dev))
+ pr_err("Failed to request irq %d (%s)\n", irq, name);
+ }
+ sdev->irq_requested = 1;
+ iowrite32(CFG_EXT_STK_EN | CFG_CNT_EN,
+ systick.membase + SYSTICK_CONFIG);
+
+ return 0;
+}
+
+static int __init ralink_systick_init(struct device_node *np)
+{
+ int ret;
+
+ systick.membase = of_iomap(np, 0);
+ if (!systick.membase)
+ return -ENXIO;
+
+ systick.dev.name = np->name;
+ clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60);
+ systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev);
+ systick.dev.max_delta_ticks = 0x7fff;
+ systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev);
+ systick.dev.min_delta_ticks = 0x3;
+ systick.dev.irq = irq_of_parse_and_map(np, 0);
+ if (!systick.dev.irq) {
+ pr_err("%pOFn: request_irq failed", np);
+ return -EINVAL;
+ }
+
+ ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
+ SYSTICK_FREQ, 301, 16,
+ clocksource_mmio_readl_up);
+ if (ret)
+ return ret;
+
+ clockevents_register_device(&systick.dev);
+
+ pr_info("%pOFn: running - mult: %d, shift: %d\n",
+ np, systick.dev.mult, systick.dev.shift);
+
+ return 0;
+}
+
+TIMER_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);
diff --git a/arch/mips/ralink/clk.c b/arch/mips/ralink/clk.c
new file mode 100644
index 0000000000..9db73fcac5
--- /dev/null
+++ b/arch/mips/ralink/clk.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/clkdev.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include <asm/time.h>
+
+#include "common.h"
+
+static const char *clk_cpu(int *idx)
+{
+ switch (ralink_soc) {
+ case RT2880_SOC:
+ *idx = 0;
+ return "ralink,rt2880-sysc";
+ case RT3883_SOC:
+ *idx = 0;
+ return "ralink,rt3883-sysc";
+ case RT305X_SOC_RT3050:
+ *idx = 0;
+ return "ralink,rt3050-sysc";
+ case RT305X_SOC_RT3052:
+ *idx = 0;
+ return "ralink,rt3052-sysc";
+ case RT305X_SOC_RT3350:
+ *idx = 1;
+ return "ralink,rt3350-sysc";
+ case RT305X_SOC_RT3352:
+ *idx = 1;
+ return "ralink,rt3352-sysc";
+ case RT305X_SOC_RT5350:
+ *idx = 1;
+ return "ralink,rt5350-sysc";
+ case MT762X_SOC_MT7620A:
+ *idx = 2;
+ return "ralink,mt7620-sysc";
+ case MT762X_SOC_MT7620N:
+ *idx = 2;
+ return "ralink,mt7620-sysc";
+ case MT762X_SOC_MT7628AN:
+ *idx = 1;
+ return "ralink,mt7628-sysc";
+ case MT762X_SOC_MT7688:
+ *idx = 1;
+ return "ralink,mt7688-sysc";
+ default:
+ *idx = -1;
+ return "invalid";
+ }
+}
+
+void __init plat_time_init(void)
+{
+ struct of_phandle_args clkspec;
+ const char *compatible;
+ struct clk *clk;
+ int cpu_clk_idx;
+
+ ralink_of_remap();
+
+ compatible = clk_cpu(&cpu_clk_idx);
+ if (cpu_clk_idx == -1)
+ panic("unable to get CPU clock index");
+
+ of_clk_init(NULL);
+ clkspec.np = of_find_compatible_node(NULL, NULL, compatible);
+ clkspec.args_count = 1;
+ clkspec.args[0] = cpu_clk_idx;
+ clk = of_clk_get_from_provider(&clkspec);
+ if (IS_ERR(clk))
+ panic("unable to get CPU clock, err=%ld", PTR_ERR(clk));
+ pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000);
+ mips_hpt_frequency = clk_get_rate(clk) / 2;
+ clk_put(clk);
+ timer_probe();
+}
diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h
new file mode 100644
index 0000000000..893981a0ca
--- /dev/null
+++ b/arch/mips/ralink/common.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ *
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#ifndef _RALINK_COMMON_H__
+#define _RALINK_COMMON_H__
+
+#define RAMIPS_SYS_TYPE_LEN 32
+
+struct ralink_soc_info {
+ unsigned char sys_type[RAMIPS_SYS_TYPE_LEN];
+ unsigned char *compatible;
+
+ unsigned long mem_base;
+ unsigned long mem_size;
+ unsigned long mem_size_min;
+ unsigned long mem_size_max;
+ void (*mem_detect)(void);
+};
+extern struct ralink_soc_info soc_info;
+
+extern void ralink_of_remap(void);
+
+extern void __init prom_soc_init(struct ralink_soc_info *soc_info);
+
+#endif /* _RALINK_COMMON_H__ */
diff --git a/arch/mips/ralink/early_printk.c b/arch/mips/ralink/early_printk.c
new file mode 100644
index 0000000000..eb4fac25ea
--- /dev/null
+++ b/arch/mips/ralink/early_printk.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ */
+
+#include <linux/io.h>
+#include <linux/serial_reg.h>
+
+#include <asm/addrspace.h>
+#include <asm/setup.h>
+
+#ifdef CONFIG_SOC_RT288X
+#define EARLY_UART_BASE 0x300c00
+#define CHIPID_BASE 0x300004
+#elif defined(CONFIG_SOC_MT7621)
+#define EARLY_UART_BASE 0x1E000c00
+#define CHIPID_BASE 0x1E000004
+#else
+#define EARLY_UART_BASE 0x10000c00
+#define CHIPID_BASE 0x10000004
+#endif
+
+#define MT7628_CHIP_NAME1 0x20203832
+
+#define UART_REG_TX 0x04
+#define UART_REG_LCR 0x0c
+#define UART_REG_LSR 0x14
+#define UART_REG_LSR_RT2880 0x1c
+
+static __iomem void *uart_membase = (__iomem void *) KSEG1ADDR(EARLY_UART_BASE);
+static __iomem void *chipid_membase = (__iomem void *) KSEG1ADDR(CHIPID_BASE);
+static int init_complete;
+
+static inline void uart_w32(u32 val, unsigned reg)
+{
+ __raw_writel(val, uart_membase + reg);
+}
+
+static inline u32 uart_r32(unsigned reg)
+{
+ return __raw_readl(uart_membase + reg);
+}
+
+static inline int soc_is_mt7628(void)
+{
+ return IS_ENABLED(CONFIG_SOC_MT7620) &&
+ (__raw_readl(chipid_membase) == MT7628_CHIP_NAME1);
+}
+
+static void find_uart_base(void)
+{
+ int i;
+
+ if (!soc_is_mt7628())
+ return;
+
+ for (i = 0; i < 3; i++) {
+ u32 reg = uart_r32(UART_REG_LCR + (0x100 * i));
+
+ if (!reg)
+ continue;
+
+ uart_membase = (__iomem void *) KSEG1ADDR(EARLY_UART_BASE +
+ (0x100 * i));
+ break;
+ }
+}
+
+void prom_putchar(char ch)
+{
+ if (!init_complete) {
+ find_uart_base();
+ init_complete = 1;
+ }
+
+ if (IS_ENABLED(CONFIG_SOC_MT7621) || soc_is_mt7628()) {
+ uart_w32((unsigned char)ch, UART_TX);
+ while ((uart_r32(UART_REG_LSR) & UART_LSR_THRE) == 0)
+ ;
+ } else {
+ while ((uart_r32(UART_REG_LSR_RT2880) & UART_LSR_THRE) == 0)
+ ;
+ uart_w32((unsigned char)ch, UART_REG_TX);
+ while ((uart_r32(UART_REG_LSR_RT2880) & UART_LSR_THRE) == 0)
+ ;
+ }
+}
diff --git a/arch/mips/ralink/ill_acc.c b/arch/mips/ralink/ill_acc.c
new file mode 100644
index 0000000000..25341b2319
--- /dev/null
+++ b/arch/mips/ralink/ill_acc.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#define REG_ILL_ACC_ADDR 0x10
+#define REG_ILL_ACC_TYPE 0x14
+
+#define ILL_INT_STATUS BIT(31)
+#define ILL_ACC_WRITE BIT(30)
+#define ILL_ACC_LEN_M 0xff
+#define ILL_ACC_OFF_M 0xf
+#define ILL_ACC_OFF_S 16
+#define ILL_ACC_ID_M 0x7
+#define ILL_ACC_ID_S 8
+
+#define DRV_NAME "ill_acc"
+
+static const char * const ill_acc_ids[] = {
+ "cpu", "dma", "ppe", "pdma rx", "pdma tx", "pci/e", "wmac", "usb",
+};
+
+static irqreturn_t ill_acc_irq_handler(int irq, void *_priv)
+{
+ struct device *dev = (struct device *) _priv;
+ u32 addr = rt_memc_r32(REG_ILL_ACC_ADDR);
+ u32 type = rt_memc_r32(REG_ILL_ACC_TYPE);
+
+ dev_err(dev, "illegal %s access from %s - addr:0x%08x offset:%d len:%d\n",
+ (type & ILL_ACC_WRITE) ? ("write") : ("read"),
+ ill_acc_ids[(type >> ILL_ACC_ID_S) & ILL_ACC_ID_M],
+ addr, (type >> ILL_ACC_OFF_S) & ILL_ACC_OFF_M,
+ type & ILL_ACC_LEN_M);
+
+ rt_memc_w32(ILL_INT_STATUS, REG_ILL_ACC_TYPE);
+
+ return IRQ_HANDLED;
+}
+
+static int __init ill_acc_of_setup(void)
+{
+ struct platform_device *pdev;
+ struct device_node *np;
+ int irq;
+
+ /* somehow this driver breaks on RT5350 */
+ if (of_machine_is_compatible("ralink,rt5350-soc"))
+ return -EINVAL;
+
+ np = of_find_compatible_node(NULL, NULL, "ralink,rt3050-memc");
+ if (!np)
+ return -EINVAL;
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev) {
+ pr_err("%pOFn: failed to lookup pdev\n", np);
+ of_node_put(np);
+ return -EINVAL;
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ of_node_put(np);
+ if (!irq) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ put_device(&pdev->dev);
+ return -EINVAL;
+ }
+
+ if (request_irq(irq, ill_acc_irq_handler, 0, "ill_acc", &pdev->dev)) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ put_device(&pdev->dev);
+ return -EINVAL;
+ }
+
+ rt_memc_w32(ILL_INT_STATUS, REG_ILL_ACC_TYPE);
+
+ dev_info(&pdev->dev, "irq registered\n");
+
+ return 0;
+}
+
+arch_initcall(ill_acc_of_setup);
diff --git a/arch/mips/ralink/irq-gic.c b/arch/mips/ralink/irq-gic.c
new file mode 100644
index 0000000000..3bab51a5fb
--- /dev/null
+++ b/arch/mips/ralink/irq-gic.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2015 Nikolay Martynov <mar.kolya@gmail.com>
+ * Copyright (C) 2015 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/init.h>
+
+#include <linux/of.h>
+#include <linux/irqchip.h>
+#include <asm/mips-cps.h>
+
+int get_c0_perfcount_int(void)
+{
+ return gic_get_c0_perfcount_int();
+}
+EXPORT_SYMBOL_GPL(get_c0_perfcount_int);
+
+void __init arch_init_irq(void)
+{
+ irqchip_init();
+}
diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c
new file mode 100644
index 0000000000..46aef0a1b2
--- /dev/null
+++ b/arch/mips/ralink/irq.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+
+#include "common.h"
+
+#define INTC_INT_GLOBAL BIT(31)
+
+#define RALINK_CPU_IRQ_INTC (MIPS_CPU_IRQ_BASE + 2)
+#define RALINK_CPU_IRQ_PCI (MIPS_CPU_IRQ_BASE + 4)
+#define RALINK_CPU_IRQ_FE (MIPS_CPU_IRQ_BASE + 5)
+#define RALINK_CPU_IRQ_WIFI (MIPS_CPU_IRQ_BASE + 6)
+#define RALINK_CPU_IRQ_COUNTER (MIPS_CPU_IRQ_BASE + 7)
+
+/* we have a cascade of 8 irqs */
+#define RALINK_INTC_IRQ_BASE 8
+
+/* we have 32 SoC irqs */
+#define RALINK_INTC_IRQ_COUNT 32
+
+#define RALINK_INTC_IRQ_PERFC (RALINK_INTC_IRQ_BASE + 9)
+
+enum rt_intc_regs_enum {
+ INTC_REG_STATUS0 = 0,
+ INTC_REG_STATUS1,
+ INTC_REG_TYPE,
+ INTC_REG_RAW_STATUS,
+ INTC_REG_ENABLE,
+ INTC_REG_DISABLE,
+};
+
+static u32 rt_intc_regs[] = {
+ [INTC_REG_STATUS0] = 0x00,
+ [INTC_REG_STATUS1] = 0x04,
+ [INTC_REG_TYPE] = 0x20,
+ [INTC_REG_RAW_STATUS] = 0x30,
+ [INTC_REG_ENABLE] = 0x34,
+ [INTC_REG_DISABLE] = 0x38,
+};
+
+static void __iomem *rt_intc_membase;
+
+static int rt_perfcount_irq;
+
+static inline void rt_intc_w32(u32 val, unsigned reg)
+{
+ __raw_writel(val, rt_intc_membase + rt_intc_regs[reg]);
+}
+
+static inline u32 rt_intc_r32(unsigned reg)
+{
+ return __raw_readl(rt_intc_membase + rt_intc_regs[reg]);
+}
+
+static void ralink_intc_irq_unmask(struct irq_data *d)
+{
+ rt_intc_w32(BIT(d->hwirq), INTC_REG_ENABLE);
+}
+
+static void ralink_intc_irq_mask(struct irq_data *d)
+{
+ rt_intc_w32(BIT(d->hwirq), INTC_REG_DISABLE);
+}
+
+static struct irq_chip ralink_intc_irq_chip = {
+ .name = "INTC",
+ .irq_unmask = ralink_intc_irq_unmask,
+ .irq_mask = ralink_intc_irq_mask,
+ .irq_mask_ack = ralink_intc_irq_mask,
+};
+
+int get_c0_perfcount_int(void)
+{
+ return rt_perfcount_irq;
+}
+EXPORT_SYMBOL_GPL(get_c0_perfcount_int);
+
+unsigned int get_c0_compare_int(void)
+{
+ return CP0_LEGACY_COMPARE_IRQ;
+}
+
+static void ralink_intc_irq_handler(struct irq_desc *desc)
+{
+ u32 pending = rt_intc_r32(INTC_REG_STATUS0);
+
+ if (pending) {
+ struct irq_domain *domain = irq_desc_get_handler_data(desc);
+ generic_handle_domain_irq(domain, __ffs(pending));
+ } else {
+ spurious_interrupt();
+ }
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ unsigned long pending;
+
+ pending = read_c0_status() & read_c0_cause() & ST0_IM;
+
+ if (pending & STATUSF_IP7)
+ do_IRQ(RALINK_CPU_IRQ_COUNTER);
+
+ else if (pending & STATUSF_IP5)
+ do_IRQ(RALINK_CPU_IRQ_FE);
+
+ else if (pending & STATUSF_IP6)
+ do_IRQ(RALINK_CPU_IRQ_WIFI);
+
+ else if (pending & STATUSF_IP4)
+ do_IRQ(RALINK_CPU_IRQ_PCI);
+
+ else if (pending & STATUSF_IP2)
+ do_IRQ(RALINK_CPU_IRQ_INTC);
+
+ else
+ spurious_interrupt();
+}
+
+static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(irq, &ralink_intc_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops irq_domain_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ .map = intc_map,
+};
+
+static int __init intc_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct resource res;
+ struct irq_domain *domain;
+ int irq;
+
+ if (!of_property_read_u32_array(node, "ralink,intc-registers",
+ rt_intc_regs, 6))
+ pr_info("intc: using register map from devicetree\n");
+
+ irq = irq_of_parse_and_map(node, 0);
+ if (!irq)
+ panic("Failed to get INTC IRQ");
+
+ if (of_address_to_resource(node, 0, &res))
+ panic("Failed to get intc memory range");
+
+ if (!request_mem_region(res.start, resource_size(&res),
+ res.name))
+ pr_err("Failed to request intc memory");
+
+ rt_intc_membase = ioremap(res.start,
+ resource_size(&res));
+ if (!rt_intc_membase)
+ panic("Failed to remap intc memory");
+
+ /* disable all interrupts */
+ rt_intc_w32(~0, INTC_REG_DISABLE);
+
+ /* route all INTC interrupts to MIPS HW0 interrupt */
+ rt_intc_w32(0, INTC_REG_TYPE);
+
+ domain = irq_domain_add_legacy(node, RALINK_INTC_IRQ_COUNT,
+ RALINK_INTC_IRQ_BASE, 0, &irq_domain_ops, NULL);
+ if (!domain)
+ panic("Failed to add irqdomain");
+
+ rt_intc_w32(INTC_INT_GLOBAL, INTC_REG_ENABLE);
+
+ irq_set_chained_handler_and_data(irq, ralink_intc_irq_handler, domain);
+
+ /* tell the kernel which irq is used for performance monitoring */
+ rt_perfcount_irq = irq_create_mapping(domain, 9);
+
+ return 0;
+}
+
+static struct of_device_id __initdata of_irq_ids[] = {
+ { .compatible = "mti,cpu-interrupt-controller", .data = mips_cpu_irq_of_init },
+ { .compatible = "ralink,rt2880-intc", .data = intc_of_init },
+ {},
+};
+
+void __init arch_init_irq(void)
+{
+ of_irq_init(of_irq_ids);
+}
+
diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c
new file mode 100644
index 0000000000..672249a13a
--- /dev/null
+++ b/arch/mips/ralink/mt7620.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Parts of this file are based on Ralink's 2.6.21 BSP
+ *
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+#include <asm/mipsregs.h>
+#include <asm/mach-ralink/ralink_regs.h>
+#include <asm/mach-ralink/mt7620.h>
+
+#include "common.h"
+
+/* analog */
+#define PMU0_CFG 0x88
+#define PMU_SW_SET BIT(28)
+#define A_DCDC_EN BIT(24)
+#define A_SSC_PERI BIT(19)
+#define A_SSC_GEN BIT(18)
+#define A_SSC_M 0x3
+#define A_SSC_S 16
+#define A_DLY_M 0x7
+#define A_DLY_S 8
+#define A_VTUNE_M 0xff
+
+/* digital */
+#define PMU1_CFG 0x8C
+#define DIG_SW_SEL BIT(25)
+
+/* EFUSE bits */
+#define EFUSE_MT7688 0x100000
+
+/* DRAM type bit */
+#define DRAM_TYPE_MT7628_MASK 0x1
+
+/* does the board have sdram or ddram */
+static int dram_type;
+
+static struct ralink_soc_info *soc_info_ptr;
+
+static __init void
+mt7620_dram_init(struct ralink_soc_info *soc_info)
+{
+ switch (dram_type) {
+ case SYSCFG0_DRAM_TYPE_SDRAM:
+ pr_info("Board has SDRAM\n");
+ soc_info->mem_size_min = MT7620_SDRAM_SIZE_MIN;
+ soc_info->mem_size_max = MT7620_SDRAM_SIZE_MAX;
+ break;
+
+ case SYSCFG0_DRAM_TYPE_DDR1:
+ pr_info("Board has DDR1\n");
+ soc_info->mem_size_min = MT7620_DDR1_SIZE_MIN;
+ soc_info->mem_size_max = MT7620_DDR1_SIZE_MAX;
+ break;
+
+ case SYSCFG0_DRAM_TYPE_DDR2:
+ pr_info("Board has DDR2\n");
+ soc_info->mem_size_min = MT7620_DDR2_SIZE_MIN;
+ soc_info->mem_size_max = MT7620_DDR2_SIZE_MAX;
+ break;
+ default:
+ BUG();
+ }
+}
+
+static __init void
+mt7628_dram_init(struct ralink_soc_info *soc_info)
+{
+ switch (dram_type) {
+ case SYSCFG0_DRAM_TYPE_DDR1_MT7628:
+ pr_info("Board has DDR1\n");
+ soc_info->mem_size_min = MT7620_DDR1_SIZE_MIN;
+ soc_info->mem_size_max = MT7620_DDR1_SIZE_MAX;
+ break;
+
+ case SYSCFG0_DRAM_TYPE_DDR2_MT7628:
+ pr_info("Board has DDR2\n");
+ soc_info->mem_size_min = MT7620_DDR2_SIZE_MIN;
+ soc_info->mem_size_max = MT7620_DDR2_SIZE_MAX;
+ break;
+ default:
+ BUG();
+ }
+}
+
+static unsigned int __init mt7620_get_soc_name0(void)
+{
+ return __raw_readl(MT7620_SYSC_BASE + SYSC_REG_CHIP_NAME0);
+}
+
+static unsigned int __init mt7620_get_soc_name1(void)
+{
+ return __raw_readl(MT7620_SYSC_BASE + SYSC_REG_CHIP_NAME1);
+}
+
+static bool __init mt7620_soc_valid(void)
+{
+ if (mt7620_get_soc_name0() == MT7620_CHIP_NAME0 &&
+ mt7620_get_soc_name1() == MT7620_CHIP_NAME1)
+ return true;
+ else
+ return false;
+}
+
+static bool __init mt7628_soc_valid(void)
+{
+ if (mt7620_get_soc_name0() == MT7620_CHIP_NAME0 &&
+ mt7620_get_soc_name1() == MT7628_CHIP_NAME1)
+ return true;
+ else
+ return false;
+}
+
+static unsigned int __init mt7620_get_rev(void)
+{
+ return __raw_readl(MT7620_SYSC_BASE + SYSC_REG_CHIP_REV);
+}
+
+static unsigned int __init mt7620_get_bga(void)
+{
+ return (mt7620_get_rev() >> CHIP_REV_PKG_SHIFT) & CHIP_REV_PKG_MASK;
+}
+
+static unsigned int __init mt7620_get_efuse(void)
+{
+ return __raw_readl(MT7620_SYSC_BASE + SYSC_REG_EFUSE_CFG);
+}
+
+static unsigned int __init mt7620_get_soc_ver(void)
+{
+ return (mt7620_get_rev() >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK;
+}
+
+static unsigned int __init mt7620_get_soc_eco(void)
+{
+ return (mt7620_get_rev() & CHIP_REV_ECO_MASK);
+}
+
+static const char __init *mt7620_get_soc_name(struct ralink_soc_info *soc_info)
+{
+ if (mt7620_soc_valid()) {
+ u32 bga = mt7620_get_bga();
+
+ if (bga) {
+ ralink_soc = MT762X_SOC_MT7620A;
+ soc_info->compatible = "ralink,mt7620a-soc";
+ return "MT7620A";
+ } else {
+ ralink_soc = MT762X_SOC_MT7620N;
+ soc_info->compatible = "ralink,mt7620n-soc";
+ return "MT7620N";
+ }
+ } else if (mt7628_soc_valid()) {
+ u32 efuse = mt7620_get_efuse();
+ unsigned char *name = NULL;
+
+ if (efuse & EFUSE_MT7688) {
+ ralink_soc = MT762X_SOC_MT7688;
+ name = "MT7688";
+ } else {
+ ralink_soc = MT762X_SOC_MT7628AN;
+ name = "MT7628AN";
+ }
+ soc_info->compatible = "ralink,mt7628an-soc";
+ return name;
+ } else {
+ panic("mt762x: unknown SoC, n0:%08x n1:%08x\n",
+ mt7620_get_soc_name0(), mt7620_get_soc_name1());
+ }
+}
+
+static const char __init *mt7620_get_soc_id_name(void)
+{
+ if (ralink_soc == MT762X_SOC_MT7620A)
+ return "mt7620a";
+ else if (ralink_soc == MT762X_SOC_MT7620N)
+ return "mt7620n";
+ else if (ralink_soc == MT762X_SOC_MT7688)
+ return "mt7688";
+ else if (ralink_soc == MT762X_SOC_MT7628AN)
+ return "mt7628n";
+ else
+ return "invalid";
+}
+
+static int __init mt7620_soc_dev_init(void)
+{
+ struct soc_device *soc_dev;
+ struct soc_device_attribute *soc_dev_attr;
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENOMEM;
+
+ soc_dev_attr->family = "Ralink";
+ soc_dev_attr->soc_id = mt7620_get_soc_id_name();
+
+ soc_dev_attr->data = soc_info_ptr;
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ kfree(soc_dev_attr);
+ return PTR_ERR(soc_dev);
+ }
+
+ return 0;
+}
+device_initcall(mt7620_soc_dev_init);
+
+void __init prom_soc_init(struct ralink_soc_info *soc_info)
+{
+ const char *name = mt7620_get_soc_name(soc_info);
+ u32 cfg0;
+ u32 pmu0;
+ u32 pmu1;
+
+ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN,
+ "MediaTek %s ver:%u eco:%u",
+ name, mt7620_get_soc_ver(), mt7620_get_soc_eco());
+
+ cfg0 = __raw_readl(MT7620_SYSC_BASE + SYSC_REG_SYSTEM_CONFIG0);
+ if (is_mt76x8()) {
+ dram_type = cfg0 & DRAM_TYPE_MT7628_MASK;
+ } else {
+ dram_type = (cfg0 >> SYSCFG0_DRAM_TYPE_SHIFT) &
+ SYSCFG0_DRAM_TYPE_MASK;
+ if (dram_type == SYSCFG0_DRAM_TYPE_UNKNOWN)
+ dram_type = SYSCFG0_DRAM_TYPE_SDRAM;
+ }
+
+ soc_info->mem_base = MT7620_DRAM_BASE;
+ if (is_mt76x8())
+ mt7628_dram_init(soc_info);
+ else
+ mt7620_dram_init(soc_info);
+
+ pmu0 = __raw_readl(MT7620_SYSC_BASE + PMU0_CFG);
+ pmu1 = __raw_readl(MT7620_SYSC_BASE + PMU1_CFG);
+
+ pr_info("Analog PMU set to %s control\n",
+ (pmu0 & PMU_SW_SET) ? ("sw") : ("hw"));
+ pr_info("Digital PMU set to %s control\n",
+ (pmu1 & DIG_SW_SEL) ? ("sw") : ("hw"));
+
+ soc_info_ptr = soc_info;
+}
diff --git a/arch/mips/ralink/mt7621.c b/arch/mips/ralink/mt7621.c
new file mode 100644
index 0000000000..137781d0bd
--- /dev/null
+++ b/arch/mips/ralink/mt7621.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2015 Nikolay Martynov <mar.kolya@gmail.com>
+ * Copyright (C) 2015 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/memblock.h>
+#include <linux/pci.h>
+#include <linux/bug.h>
+
+#include <asm/bootinfo.h>
+#include <asm/mipsregs.h>
+#include <asm/smp-ops.h>
+#include <asm/mips-cps.h>
+#include <asm/mach-ralink/ralink_regs.h>
+#include <asm/mach-ralink/mt7621.h>
+
+#include "common.h"
+
+#define MT7621_MEM_TEST_PATTERN 0xaa5555aa
+
+static u32 detect_magic __initdata;
+static struct ralink_soc_info *soc_info_ptr;
+
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ struct resource_entry *entry;
+ resource_size_t mask;
+
+ entry = resource_list_first_type(&bridge->windows, IORESOURCE_MEM);
+ if (!entry) {
+ pr_err("Cannot get memory resource\n");
+ return -EINVAL;
+ }
+
+ if (mips_cps_numiocu(0)) {
+ /*
+ * Hardware doesn't accept mask values with 1s after
+ * 0s (e.g. 0xffef), so warn if that's happen
+ */
+ mask = ~(entry->res->end - entry->res->start) & CM_GCR_REGn_MASK_ADDRMASK;
+ WARN_ON(mask && BIT(ffz(~mask)) - 1 != ~mask);
+
+ write_gcr_reg1_base(entry->res->start);
+ write_gcr_reg1_mask(mask | CM_GCR_REGn_MASK_CMTGT_IOCU0);
+ pr_info("PCI coherence region base: 0x%08llx, mask/settings: 0x%08llx\n",
+ (unsigned long long)read_gcr_reg1_base(),
+ (unsigned long long)read_gcr_reg1_mask());
+ }
+
+ return 0;
+}
+
+phys_addr_t mips_cpc_default_phys_base(void)
+{
+ panic("Cannot detect cpc address");
+}
+
+static bool __init mt7621_addr_wraparound_test(phys_addr_t size)
+{
+ void *dm = (void *)KSEG1ADDR(&detect_magic);
+
+ if (CPHYSADDR(dm + size) >= MT7621_LOWMEM_MAX_SIZE)
+ return true;
+ __raw_writel(MT7621_MEM_TEST_PATTERN, dm);
+ if (__raw_readl(dm) != __raw_readl(dm + size))
+ return false;
+ __raw_writel(~MT7621_MEM_TEST_PATTERN, dm);
+ return __raw_readl(dm) == __raw_readl(dm + size);
+}
+
+static void __init mt7621_memory_detect(void)
+{
+ phys_addr_t size;
+
+ for (size = 32 * SZ_1M; size <= 256 * SZ_1M; size <<= 1) {
+ if (mt7621_addr_wraparound_test(size)) {
+ memblock_add(MT7621_LOWMEM_BASE, size);
+ return;
+ }
+ }
+
+ memblock_add(MT7621_LOWMEM_BASE, MT7621_LOWMEM_MAX_SIZE);
+ memblock_add(MT7621_HIGHMEM_BASE, MT7621_HIGHMEM_SIZE);
+}
+
+static unsigned int __init mt7621_get_soc_name0(void)
+{
+ return __raw_readl(MT7621_SYSC_BASE + SYSC_REG_CHIP_NAME0);
+}
+
+static unsigned int __init mt7621_get_soc_name1(void)
+{
+ return __raw_readl(MT7621_SYSC_BASE + SYSC_REG_CHIP_NAME1);
+}
+
+static bool __init mt7621_soc_valid(void)
+{
+ if (mt7621_get_soc_name0() == MT7621_CHIP_NAME0 &&
+ mt7621_get_soc_name1() == MT7621_CHIP_NAME1)
+ return true;
+ else
+ return false;
+}
+
+static const char __init *mt7621_get_soc_id(void)
+{
+ if (mt7621_soc_valid())
+ return "MT7621";
+ else
+ return "invalid";
+}
+
+static unsigned int __init mt7621_get_soc_rev(void)
+{
+ return __raw_readl(MT7621_SYSC_BASE + SYSC_REG_CHIP_REV);
+}
+
+static unsigned int __init mt7621_get_soc_ver(void)
+{
+ return (mt7621_get_soc_rev() >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK;
+}
+
+static unsigned int __init mt7621_get_soc_eco(void)
+{
+ return (mt7621_get_soc_rev() & CHIP_REV_ECO_MASK);
+}
+
+static const char __init *mt7621_get_soc_revision(void)
+{
+ if (mt7621_get_soc_rev() == 1 && mt7621_get_soc_eco() == 1)
+ return "E2";
+ else
+ return "E1";
+}
+
+static int __init mt7621_soc_dev_init(void)
+{
+ struct soc_device *soc_dev;
+ struct soc_device_attribute *soc_dev_attr;
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENOMEM;
+
+ soc_dev_attr->soc_id = "mt7621";
+ soc_dev_attr->family = "Ralink";
+ soc_dev_attr->revision = mt7621_get_soc_revision();
+
+ soc_dev_attr->data = soc_info_ptr;
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ kfree(soc_dev_attr);
+ return PTR_ERR(soc_dev);
+ }
+
+ return 0;
+}
+device_initcall(mt7621_soc_dev_init);
+
+void __init prom_soc_init(struct ralink_soc_info *soc_info)
+{
+ /* Early detection of CMP support */
+ mips_cm_probe();
+ mips_cpc_probe();
+
+ if (mips_cps_numiocu(0)) {
+ /*
+ * mips_cm_probe() wipes out bootloader
+ * config for CM regions and we have to configure them
+ * again. This SoC cannot talk to pamlbus devices
+ * witout proper iocu region set up.
+ *
+ * FIXME: it would be better to do this with values
+ * from DT, but we need this very early because
+ * without this we cannot talk to pretty much anything
+ * including serial.
+ */
+ write_gcr_reg0_base(MT7621_PALMBUS_BASE);
+ write_gcr_reg0_mask(~MT7621_PALMBUS_SIZE |
+ CM_GCR_REGn_MASK_CMTGT_IOCU0);
+ __sync();
+ }
+
+ if (mt7621_soc_valid())
+ soc_info->compatible = "mediatek,mt7621-soc";
+ else
+ panic("mt7621: unknown SoC, n0:%08x n1:%08x\n",
+ mt7621_get_soc_name0(),
+ mt7621_get_soc_name1());
+ ralink_soc = MT762X_SOC_MT7621AT;
+
+ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN,
+ "MediaTek %s ver:%u eco:%u",
+ mt7621_get_soc_id(),
+ mt7621_get_soc_ver(),
+ mt7621_get_soc_eco());
+
+ soc_info->mem_detect = mt7621_memory_detect;
+
+ soc_info_ptr = soc_info;
+
+ if (!register_cps_smp_ops())
+ return;
+ if (!register_vsmp_smp_ops())
+ return;
+}
diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c
new file mode 100644
index 0000000000..7f90068c68
--- /dev/null
+++ b/arch/mips/ralink/of.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/sizes.h>
+#include <linux/of_fdt.h>
+#include <linux/kernel.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <asm/reboot.h>
+#include <asm/bootinfo.h>
+#include <asm/addrspace.h>
+#include <asm/prom.h>
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include "common.h"
+
+__iomem void *rt_sysc_membase;
+__iomem void *rt_memc_membase;
+EXPORT_SYMBOL_GPL(rt_sysc_membase);
+
+static const struct of_device_id mtmips_memc_match[] = {
+ { .compatible = "mediatek,mt7621-memc" },
+ { .compatible = "ralink,mt7620a-memc" },
+ { .compatible = "ralink,rt2880-memc" },
+ { .compatible = "ralink,rt3050-memc" },
+ { .compatible = "ralink,rt3883-memc" },
+ {}
+};
+
+static const struct of_device_id mtmips_sysc_match[] = {
+ { .compatible = "mediatek,mt7621-sysc" },
+ { .compatible = "ralink,mt7620-sysc" },
+ { .compatible = "ralink,mt7628-sysc" },
+ { .compatible = "ralink,mt7688-sysc" },
+ { .compatible = "ralink,rt2880-sysc" },
+ { .compatible = "ralink,rt3050-sysc" },
+ { .compatible = "ralink,rt3052-sysc" },
+ { .compatible = "ralink,rt3352-sysc" },
+ { .compatible = "ralink,rt3883-sysc" },
+ { .compatible = "ralink,rt5350-sysc" },
+ {}
+};
+
+static __iomem void *
+mtmips_of_remap_node(const struct of_device_id *match, const char *type)
+{
+ struct resource res;
+ struct device_node *np;
+
+ np = of_find_matching_node(NULL, match);
+ if (!np)
+ panic("Failed to find %s controller node", type);
+
+ if (of_address_to_resource(np, 0, &res))
+ panic("Failed to get resource for %s node", np->name);
+
+ if (!request_mem_region(res.start,
+ resource_size(&res),
+ res.name))
+ panic("Failed to request resources for %s node", np->name);
+
+ of_node_put(np);
+
+ return ioremap(res.start, resource_size(&res));
+}
+
+void __init ralink_of_remap(void)
+{
+ rt_sysc_membase = mtmips_of_remap_node(mtmips_sysc_match, "system");
+ rt_memc_membase = mtmips_of_remap_node(mtmips_memc_match, "memory");
+
+ if (!rt_sysc_membase || !rt_memc_membase)
+ panic("Failed to remap core resources");
+}
+
+void __init plat_mem_setup(void)
+{
+ void *dtb;
+
+ set_io_port_base(KSEG1);
+
+ /*
+ * Load the builtin devicetree. This causes the chosen node to be
+ * parsed resulting in our memory appearing.
+ */
+ dtb = get_fdt();
+ __dt_setup_arch(dtb);
+
+ if (early_init_dt_scan_memory())
+ return;
+
+ if (soc_info.mem_detect)
+ soc_info.mem_detect();
+ else if (soc_info.mem_size)
+ memblock_add(soc_info.mem_base, soc_info.mem_size * SZ_1M);
+ else
+ detect_memory_region(soc_info.mem_base,
+ soc_info.mem_size_min * SZ_1M,
+ soc_info.mem_size_max * SZ_1M);
+}
+
+static int __init plat_of_setup(void)
+{
+ __dt_register_buses(soc_info.compatible, "palmbus");
+
+ return 0;
+}
+
+arch_initcall(plat_of_setup);
diff --git a/arch/mips/ralink/prom.c b/arch/mips/ralink/prom.c
new file mode 100644
index 0000000000..c3b9686184
--- /dev/null
+++ b/arch/mips/ralink/prom.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Joonas Lahtinen <joonas.lahtinen@gmail.com>
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/string.h>
+
+#include <asm/bootinfo.h>
+#include <asm/addrspace.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include "common.h"
+
+struct ralink_soc_info soc_info;
+
+enum ralink_soc_type ralink_soc;
+EXPORT_SYMBOL_GPL(ralink_soc);
+
+const char *get_system_type(void)
+{
+ return soc_info.sys_type;
+}
+
+static __init void prom_init_cmdline(void)
+{
+ int argc;
+ char **argv;
+ int i;
+
+ pr_debug("prom: fw_arg0=%08x fw_arg1=%08x fw_arg2=%08x fw_arg3=%08x\n",
+ (unsigned int)fw_arg0, (unsigned int)fw_arg1,
+ (unsigned int)fw_arg2, (unsigned int)fw_arg3);
+
+ argc = fw_arg0;
+ argv = (char **) KSEG1ADDR(fw_arg1);
+
+ if (!argv) {
+ pr_debug("argv=%p is invalid, skipping\n",
+ argv);
+ return;
+ }
+
+ for (i = 0; i < argc; i++) {
+ char *p = (char *) KSEG1ADDR(argv[i]);
+
+ if (CPHYSADDR(p) && *p) {
+ pr_debug("argv[%d]: %s\n", i, p);
+ strlcat(arcs_cmdline, " ", sizeof(arcs_cmdline));
+ strlcat(arcs_cmdline, p, sizeof(arcs_cmdline));
+ }
+ }
+}
+
+void __init prom_init(void)
+{
+ prom_soc_init(&soc_info);
+
+ pr_info("SoC Type: %s\n", get_system_type());
+
+ prom_init_cmdline();
+}
diff --git a/arch/mips/ralink/reset.c b/arch/mips/ralink/reset.c
new file mode 100644
index 0000000000..4875637ef4
--- /dev/null
+++ b/arch/mips/ralink/reset.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+
+#include <asm/reboot.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+/* Reset Control */
+#define SYSC_REG_RESET_CTRL 0x034
+
+#define RSTCTL_RESET_PCI BIT(26)
+#define RSTCTL_RESET_SYSTEM BIT(0)
+
+static void ralink_restart(char *command)
+{
+ if (IS_ENABLED(CONFIG_PCI)) {
+ rt_sysc_m32(0, RSTCTL_RESET_PCI, SYSC_REG_RESET_CTRL);
+ mdelay(50);
+ }
+
+ local_irq_disable();
+ rt_sysc_w32(RSTCTL_RESET_SYSTEM, SYSC_REG_RESET_CTRL);
+ unreachable();
+}
+
+static int __init mips_reboot_setup(void)
+{
+ _machine_restart = ralink_restart;
+
+ return 0;
+}
+
+arch_initcall(mips_reboot_setup);
diff --git a/arch/mips/ralink/rt288x.c b/arch/mips/ralink/rt288x.c
new file mode 100644
index 0000000000..ce8b5b6025
--- /dev/null
+++ b/arch/mips/ralink/rt288x.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Parts of this file are based on Ralink's 2.6.21 BSP
+ *
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+#include <asm/mipsregs.h>
+#include <asm/mach-ralink/ralink_regs.h>
+#include <asm/mach-ralink/rt288x.h>
+
+#include "common.h"
+
+static struct ralink_soc_info *soc_info_ptr;
+
+static unsigned int __init rt2880_get_soc_name0(void)
+{
+ return __raw_readl(RT2880_SYSC_BASE + SYSC_REG_CHIP_NAME0);
+}
+
+static unsigned int __init rt2880_get_soc_name1(void)
+{
+ return __raw_readl(RT2880_SYSC_BASE + SYSC_REG_CHIP_NAME1);
+}
+
+static bool __init rt2880_soc_valid(void)
+{
+ if (rt2880_get_soc_name0() == RT2880_CHIP_NAME0 &&
+ rt2880_get_soc_name1() == RT2880_CHIP_NAME1)
+ return true;
+ else
+ return false;
+}
+
+static const char __init *rt2880_get_soc_name(void)
+{
+ if (rt2880_soc_valid())
+ return "RT2880";
+ else
+ return "invalid";
+}
+
+static unsigned int __init rt2880_get_soc_id(void)
+{
+ return __raw_readl(RT2880_SYSC_BASE + SYSC_REG_CHIP_ID);
+}
+
+static unsigned int __init rt2880_get_soc_ver(void)
+{
+ return (rt2880_get_soc_id() >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK;
+}
+
+static unsigned int __init rt2880_get_soc_rev(void)
+{
+ return (rt2880_get_soc_id() & CHIP_ID_REV_MASK);
+}
+
+static int __init rt2880_soc_dev_init(void)
+{
+ struct soc_device *soc_dev;
+ struct soc_device_attribute *soc_dev_attr;
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENOMEM;
+
+ soc_dev_attr->family = "Ralink";
+ soc_dev_attr->soc_id = rt2880_get_soc_name();
+
+ soc_dev_attr->data = soc_info_ptr;
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ kfree(soc_dev_attr);
+ return PTR_ERR(soc_dev);
+ }
+
+ return 0;
+}
+device_initcall(rt2880_soc_dev_init);
+
+void __init prom_soc_init(struct ralink_soc_info *soc_info)
+{
+ if (rt2880_soc_valid())
+ soc_info->compatible = "ralink,r2880-soc";
+ else
+ panic("rt288x: unknown SoC, n0:%08x n1:%08x",
+ rt2880_get_soc_name0(), rt2880_get_soc_name1());
+
+ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN,
+ "Ralink %s id:%u rev:%u",
+ rt2880_get_soc_name(),
+ rt2880_get_soc_ver(),
+ rt2880_get_soc_rev());
+
+ soc_info->mem_base = RT2880_SDRAM_BASE;
+ soc_info->mem_size_min = RT2880_MEM_SIZE_MIN;
+ soc_info->mem_size_max = RT2880_MEM_SIZE_MAX;
+
+ ralink_soc = RT2880_SOC;
+ soc_info_ptr = soc_info;
+}
diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c
new file mode 100644
index 0000000000..1f422470b0
--- /dev/null
+++ b/arch/mips/ralink/rt305x.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Parts of this file are based on Ralink's 2.6.21 BSP
+ *
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+#include <asm/io.h>
+#include <asm/mipsregs.h>
+#include <asm/mach-ralink/ralink_regs.h>
+#include <asm/mach-ralink/rt305x.h>
+
+#include "common.h"
+
+static struct ralink_soc_info *soc_info_ptr;
+
+static unsigned long rt5350_get_mem_size(void)
+{
+ unsigned long ret;
+ u32 t;
+
+ t = __raw_readl(RT305X_SYSC_BASE + SYSC_REG_SYSTEM_CONFIG);
+ t = (t >> RT5350_SYSCFG0_DRAM_SIZE_SHIFT) &
+ RT5350_SYSCFG0_DRAM_SIZE_MASK;
+
+ switch (t) {
+ case RT5350_SYSCFG0_DRAM_SIZE_2M:
+ ret = 2;
+ break;
+ case RT5350_SYSCFG0_DRAM_SIZE_8M:
+ ret = 8;
+ break;
+ case RT5350_SYSCFG0_DRAM_SIZE_16M:
+ ret = 16;
+ break;
+ case RT5350_SYSCFG0_DRAM_SIZE_32M:
+ ret = 32;
+ break;
+ case RT5350_SYSCFG0_DRAM_SIZE_64M:
+ ret = 64;
+ break;
+ default:
+ panic("rt5350: invalid DRAM size: %u", t);
+ break;
+ }
+
+ return ret;
+}
+
+static unsigned int __init rt305x_get_soc_name0(void)
+{
+ return __raw_readl(RT305X_SYSC_BASE + SYSC_REG_CHIP_NAME0);
+}
+
+static unsigned int __init rt305x_get_soc_name1(void)
+{
+ return __raw_readl(RT305X_SYSC_BASE + SYSC_REG_CHIP_NAME1);
+}
+
+static bool __init rt3052_soc_valid(void)
+{
+ if (rt305x_get_soc_name0() == RT3052_CHIP_NAME0 &&
+ rt305x_get_soc_name1() == RT3052_CHIP_NAME1)
+ return true;
+ else
+ return false;
+}
+
+static bool __init rt3350_soc_valid(void)
+{
+ if (rt305x_get_soc_name0() == RT3350_CHIP_NAME0 &&
+ rt305x_get_soc_name1() == RT3350_CHIP_NAME1)
+ return true;
+ else
+ return false;
+}
+
+static bool __init rt3352_soc_valid(void)
+{
+ if (rt305x_get_soc_name0() == RT3352_CHIP_NAME0 &&
+ rt305x_get_soc_name1() == RT3352_CHIP_NAME1)
+ return true;
+ else
+ return false;
+}
+
+static bool __init rt5350_soc_valid(void)
+{
+ if (rt305x_get_soc_name0() == RT5350_CHIP_NAME0 &&
+ rt305x_get_soc_name1() == RT5350_CHIP_NAME1)
+ return true;
+ else
+ return false;
+}
+
+static const char __init *rt305x_get_soc_name(struct ralink_soc_info *soc_info)
+{
+ if (rt3052_soc_valid()) {
+ unsigned long icache_sets;
+
+ icache_sets = (read_c0_config1() >> 22) & 7;
+ if (icache_sets == 1) {
+ ralink_soc = RT305X_SOC_RT3050;
+ soc_info->compatible = "ralink,rt3050-soc";
+ return "RT3050";
+ } else {
+ ralink_soc = RT305X_SOC_RT3052;
+ soc_info->compatible = "ralink,rt3052-soc";
+ return "RT3052";
+ }
+ } else if (rt3350_soc_valid()) {
+ ralink_soc = RT305X_SOC_RT3350;
+ soc_info->compatible = "ralink,rt3350-soc";
+ return "RT3350";
+ } else if (rt3352_soc_valid()) {
+ ralink_soc = RT305X_SOC_RT3352;
+ soc_info->compatible = "ralink,rt3352-soc";
+ return "RT3352";
+ } else if (rt5350_soc_valid()) {
+ ralink_soc = RT305X_SOC_RT5350;
+ soc_info->compatible = "ralink,rt5350-soc";
+ return "RT5350";
+ } else {
+ panic("rt305x: unknown SoC, n0:%08x n1:%08x",
+ rt305x_get_soc_name0(), rt305x_get_soc_name1());
+ }
+}
+
+static unsigned int __init rt305x_get_soc_id(void)
+{
+ return __raw_readl(RT305X_SYSC_BASE + SYSC_REG_CHIP_ID);
+}
+
+static unsigned int __init rt305x_get_soc_ver(void)
+{
+ return (rt305x_get_soc_id() >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK;
+}
+
+static unsigned int __init rt305x_get_soc_rev(void)
+{
+ return (rt305x_get_soc_id() & CHIP_ID_REV_MASK);
+}
+
+static const char __init *rt305x_get_soc_id_name(void)
+{
+ if (soc_is_rt3050())
+ return "rt3050";
+ else if (soc_is_rt3052())
+ return "rt3052";
+ else if (soc_is_rt3350())
+ return "rt3350";
+ else if (soc_is_rt3352())
+ return "rt3352";
+ else if (soc_is_rt5350())
+ return "rt5350";
+ else
+ return "invalid";
+}
+
+static int __init rt305x_soc_dev_init(void)
+{
+ struct soc_device *soc_dev;
+ struct soc_device_attribute *soc_dev_attr;
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENOMEM;
+
+ soc_dev_attr->family = "Ralink";
+ soc_dev_attr->soc_id = rt305x_get_soc_id_name();
+
+ soc_dev_attr->data = soc_info_ptr;
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ kfree(soc_dev_attr);
+ return PTR_ERR(soc_dev);
+ }
+
+ return 0;
+}
+device_initcall(rt305x_soc_dev_init);
+
+void __init prom_soc_init(struct ralink_soc_info *soc_info)
+{
+ const char *name = rt305x_get_soc_name(soc_info);
+
+ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN,
+ "Ralink %s id:%u rev:%u",
+ name,
+ rt305x_get_soc_ver(),
+ rt305x_get_soc_rev());
+
+ soc_info->mem_base = RT305X_SDRAM_BASE;
+ if (soc_is_rt5350()) {
+ soc_info->mem_size = rt5350_get_mem_size();
+ } else if (soc_is_rt305x() || soc_is_rt3350()) {
+ soc_info->mem_size_min = RT305X_MEM_SIZE_MIN;
+ soc_info->mem_size_max = RT305X_MEM_SIZE_MAX;
+ } else if (soc_is_rt3352()) {
+ soc_info->mem_size_min = RT3352_MEM_SIZE_MIN;
+ soc_info->mem_size_max = RT3352_MEM_SIZE_MAX;
+ }
+
+ soc_info_ptr = soc_info;
+}
diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c
new file mode 100644
index 0000000000..21ce00da57
--- /dev/null
+++ b/arch/mips/ralink/rt3883.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Parts of this file are based on Ralink's 2.6.21 BSP
+ *
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+#include <asm/mipsregs.h>
+#include <asm/mach-ralink/ralink_regs.h>
+#include <asm/mach-ralink/rt3883.h>
+
+#include "common.h"
+
+static struct ralink_soc_info *soc_info_ptr;
+
+static unsigned int __init rt3883_get_soc_name0(void)
+{
+ return __raw_readl(RT3883_SYSC_BASE + RT3883_SYSC_REG_CHIPID0_3);
+}
+
+static unsigned int __init rt3883_get_soc_name1(void)
+{
+ return __raw_readl(RT3883_SYSC_BASE + RT3883_SYSC_REG_CHIPID4_7);
+}
+
+static bool __init rt3883_soc_valid(void)
+{
+ if (rt3883_get_soc_name0() == RT3883_CHIP_NAME0 &&
+ rt3883_get_soc_name1() == RT3883_CHIP_NAME1)
+ return true;
+ else
+ return false;
+}
+
+static const char __init *rt3883_get_soc_name(void)
+{
+ if (rt3883_soc_valid())
+ return "RT3883";
+ else
+ return "invalid";
+}
+
+static unsigned int __init rt3883_get_soc_id(void)
+{
+ return __raw_readl(RT3883_SYSC_BASE + RT3883_SYSC_REG_REVID);
+}
+
+static unsigned int __init rt3883_get_soc_ver(void)
+{
+ return (rt3883_get_soc_id() >> RT3883_REVID_VER_ID_SHIFT) & RT3883_REVID_VER_ID_MASK;
+}
+
+static unsigned int __init rt3883_get_soc_rev(void)
+{
+ return (rt3883_get_soc_id() & RT3883_REVID_ECO_ID_MASK);
+}
+
+static int __init rt3883_soc_dev_init(void)
+{
+ struct soc_device *soc_dev;
+ struct soc_device_attribute *soc_dev_attr;
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENOMEM;
+
+ soc_dev_attr->family = "Ralink";
+ soc_dev_attr->soc_id = rt3883_get_soc_name();
+
+ soc_dev_attr->data = soc_info_ptr;
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ kfree(soc_dev_attr);
+ return PTR_ERR(soc_dev);
+ }
+
+ return 0;
+}
+device_initcall(rt3883_soc_dev_init);
+
+void __init prom_soc_init(struct ralink_soc_info *soc_info)
+{
+ if (rt3883_soc_valid())
+ soc_info->compatible = "ralink,rt3883-soc";
+ else
+ panic("rt3883: unknown SoC, n0:%08x n1:%08x",
+ rt3883_get_soc_name0(), rt3883_get_soc_name1());
+
+ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN,
+ "Ralink %s ver:%u eco:%u",
+ rt3883_get_soc_name(),
+ rt3883_get_soc_ver(),
+ rt3883_get_soc_rev());
+
+ soc_info->mem_base = RT3883_SDRAM_BASE;
+ soc_info->mem_size_min = RT3883_MEM_SIZE_MIN;
+ soc_info->mem_size_max = RT3883_MEM_SIZE_MAX;
+
+ ralink_soc = RT3883_SOC;
+ soc_info_ptr = soc_info;
+}
diff --git a/arch/mips/ralink/timer-gic.c b/arch/mips/ralink/timer-gic.c
new file mode 100644
index 0000000000..dcf2a44ac5
--- /dev/null
+++ b/arch/mips/ralink/timer-gic.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2015 Nikolay Martynov <mar.kolya@gmail.com>
+ * Copyright (C) 2015 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/init.h>
+
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/clocksource.h>
+
+#include "common.h"
+
+void __init plat_time_init(void)
+{
+ ralink_of_remap();
+
+ of_clk_init(NULL);
+ timer_probe();
+}
diff --git a/arch/mips/ralink/timer.c b/arch/mips/ralink/timer.c
new file mode 100644
index 0000000000..fc503679a9
--- /dev/null
+++ b/arch/mips/ralink/timer.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Ralink RT2880 timer
+ * Author: John Crispin
+ *
+ * Copyright (C) 2013 John Crispin <john@phrozen.org>
+*/
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#define TIMER_REG_TMRSTAT 0x00
+#define TIMER_REG_TMR0LOAD 0x10
+#define TIMER_REG_TMR0CTL 0x18
+
+#define TMRSTAT_TMR0INT BIT(0)
+
+#define TMR0CTL_ENABLE BIT(7)
+#define TMR0CTL_MODE_PERIODIC BIT(4)
+#define TMR0CTL_PRESCALER 1
+#define TMR0CTL_PRESCALE_VAL (0xf - TMR0CTL_PRESCALER)
+#define TMR0CTL_PRESCALE_DIV (65536 / BIT(TMR0CTL_PRESCALER))
+
+struct rt_timer {
+ struct device *dev;
+ void __iomem *membase;
+ int irq;
+ unsigned long timer_freq;
+ unsigned long timer_div;
+};
+
+static inline void rt_timer_w32(struct rt_timer *rt, u8 reg, u32 val)
+{
+ __raw_writel(val, rt->membase + reg);
+}
+
+static inline u32 rt_timer_r32(struct rt_timer *rt, u8 reg)
+{
+ return __raw_readl(rt->membase + reg);
+}
+
+static irqreturn_t rt_timer_irq(int irq, void *_rt)
+{
+ struct rt_timer *rt = (struct rt_timer *) _rt;
+
+ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div);
+ rt_timer_w32(rt, TIMER_REG_TMRSTAT, TMRSTAT_TMR0INT);
+
+ return IRQ_HANDLED;
+}
+
+
+static int rt_timer_request(struct rt_timer *rt)
+{
+ int err = request_irq(rt->irq, rt_timer_irq, 0,
+ dev_name(rt->dev), rt);
+ if (err) {
+ dev_err(rt->dev, "failed to request irq\n");
+ } else {
+ u32 t = TMR0CTL_MODE_PERIODIC | TMR0CTL_PRESCALE_VAL;
+ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t);
+ }
+ return err;
+}
+
+static int rt_timer_config(struct rt_timer *rt, unsigned long divisor)
+{
+ if (rt->timer_freq < divisor)
+ rt->timer_div = rt->timer_freq;
+ else
+ rt->timer_div = divisor;
+
+ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div);
+
+ return 0;
+}
+
+static int rt_timer_enable(struct rt_timer *rt)
+{
+ u32 t;
+
+ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div);
+
+ t = rt_timer_r32(rt, TIMER_REG_TMR0CTL);
+ t |= TMR0CTL_ENABLE;
+ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t);
+
+ return 0;
+}
+
+static int rt_timer_probe(struct platform_device *pdev)
+{
+ struct rt_timer *rt;
+ struct clk *clk;
+
+ rt = devm_kzalloc(&pdev->dev, sizeof(*rt), GFP_KERNEL);
+ if (!rt) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ rt->irq = platform_get_irq(pdev, 0);
+ if (rt->irq < 0)
+ return rt->irq;
+
+ rt->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(rt->membase))
+ return PTR_ERR(rt->membase);
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed get clock rate\n");
+ return PTR_ERR(clk);
+ }
+
+ rt->timer_freq = clk_get_rate(clk) / TMR0CTL_PRESCALE_DIV;
+ if (!rt->timer_freq)
+ return -EINVAL;
+
+ rt->dev = &pdev->dev;
+ platform_set_drvdata(pdev, rt);
+
+ rt_timer_request(rt);
+ rt_timer_config(rt, 2);
+ rt_timer_enable(rt);
+
+ dev_info(&pdev->dev, "maximum frequency is %luHz\n", rt->timer_freq);
+
+ return 0;
+}
+
+static const struct of_device_id rt_timer_match[] = {
+ { .compatible = "ralink,rt2880-timer" },
+ {},
+};
+
+static struct platform_driver rt_timer_driver = {
+ .probe = rt_timer_probe,
+ .driver = {
+ .name = "rt-timer",
+ .of_match_table = rt_timer_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(rt_timer_driver);