econet: add EN7528 subtarget support

The EN7528 is a little endian dual-core MIPS 1004Kc SoC used in xPON
devices. Unlike the big endian EN751221, EN7528 uses the MIPS GIC
interrupt controller for SMP.

This adds minimal boot support for EN7528:
- New en7528 subtarget with mipsel architecture
- Kernel patches for EN7528 SoC with GIC support
- Timer driver extended to support GIC shared interrupts per CPU
- SPI driver fix for EN7528 chip select handling
- Generic device tree for initial bring-up

Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/21326
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
Ahmed Naseef 2025-12-29 13:12:20 +04:00 committed by Hauke Mehrtens
parent 862b46dd8f
commit fab098cb61
15 changed files with 1041 additions and 5 deletions

View file

@ -4,11 +4,10 @@
include $(TOPDIR)/rules.mk
ARCH:=mips
BOARD:=econet
BOARDNAME:=EcoNet EN75xx MIPS
FEATURES:=dt source-only squashfs nand usb
SUBTARGETS:=en751221
SUBTARGETS:=en751221 en7528
KERNEL_PATCHVER:=6.12

View file

@ -0,0 +1,103 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/dts-v1/;
#include <dt-bindings/interrupt-controller/mips-gic.h>
/ {
compatible = "econet,en7528";
#address-cells = <1>;
#size-cells = <1>;
hpt_clock: clock {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <200000000>; /* 200 MHz */
};
spi_clock: spi-clock {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <40000000>; /* 40 MHz */
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "mips,mips1004Kc";
reg = <0>;
};
cpu@1 {
device_type = "cpu";
compatible = "mips,mips1004Kc";
reg = <1>;
};
};
cpuintc: interrupt-controller {
compatible = "mti,cpu-interrupt-controller";
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
gic: interrupt-controller@1f8c0000 {
compatible = "mti,gic";
reg = <0x1f8c0000 0x20000>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&cpuintc>;
interrupts = <2>;
};
timer_hpt: timer@1fbf0400 {
compatible = "econet,en7528-timer";
reg = <0x1fbf0400 0x14>,
<0x1fbe0000 0x14>;
interrupt-parent = <&gic>;
interrupts = <GIC_SHARED 30 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 29 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 37 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&hpt_clock>;
};
spi_ctrl: spi@1fa10000 {
compatible = "airoha,en7581-snand";
reg = <0x1fa10000 0x140>,
<0x1fa11000 0x160>;
clocks = <&spi_clock>;
clock-names = "spi";
#address-cells = <1>;
#size-cells = <0>;
nand: nand@0 {
compatible = "spi-nand";
reg = <0>;
spi-max-frequency = <40000000>;
spi-tx-bus-width = <1>;
spi-rx-bus-width = <2>;
};
};
uart: serial@1fbf0000 {
compatible = "airoha,en7523-uart";
reg = <0x1fbf0000 0x30>;
reg-io-width = <4>;
reg-shift = <2>;
interrupt-parent = <&gic>;
interrupts = <GIC_SHARED 2 IRQ_TYPE_LEVEL_HIGH>;
clock-frequency = <7372800>;
};
};

View file

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
#include "en7528.dtsi"
/ {
model = "Generic EN7528";
compatible = "econet,en7528-generic", "econet,en7528";
memory@0 {
// We hope at least 64MB will be available on every device
device_type = "memory";
reg = <0x00000000 0x4000000>;
};
chosen {
stdout-path = "serial0:115200n8";
linux,usable-memory-range = <0x00020000 0x3fe0000>;
};
aliases {
serial0 = &uart;
};
};
&nand {
status = "okay";
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@1 {
// We don't know how big the flash is
// Put 1GB and let it truncate
label = "all_flash";
reg = <0x0 0x40000000>;
read-only;
};
partition@2 {
// We don't know how big the bootloader
// is, but when we're doing testing, lets
// make sure nobody touches anything below 4MB
label = "bootloader";
reg = <0x0 0x00400000>;
read-only;
};
partition@3 {
label = "rest_of_flash";
reg = <0x00400000 0x40000000>;
};
};
};

View file

@ -147,6 +147,7 @@ CONFIG_RESET_CONTROLLER=y
CONFIG_RFS_ACCEL=y
CONFIG_RPS=y
CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES=y
# CONFIG_SERIAL_8250_AIROHA is not set
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SGL_ALLOC=y
@ -154,8 +155,10 @@ CONFIG_SMP=y
CONFIG_SMP_UP=y
CONFIG_SOCK_RX_QUEUE_MAPPING=y
CONFIG_SOC_ECONET_EN751221=y
# CONFIG_SOC_ECONET_EN7528 is not set
CONFIG_SPI=y
CONFIG_SPI_AIROHA_EN7523=y
# CONFIG_SPI_AIROHA_SNFI is not set
CONFIG_SPI_MASTER=y
CONFIG_SPI_MEM=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y

View file

@ -1,3 +1,4 @@
ARCH:=mips
BOARDNAME:=en751221
CPU_TYPE:=24kc
KERNELNAME:=vmlinuz.bin

View file

@ -0,0 +1,216 @@
CONFIG_ARCH_32BIT_OFF_T=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_KEEP_MEMBLOCK=y
CONFIG_ARCH_MMAP_RND_BITS_MAX=15
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_BOARD_SCACHE=y
CONFIG_CLKSRC_MMIO=y
CONFIG_CLONE_BACKWARDS=y
CONFIG_COMMON_CLK=y
# CONFIG_COMMON_CLK_EN7523 is not set
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
CONFIG_COMPAT_32BIT_TIME=y
CONFIG_CONTEXT_TRACKING=y
CONFIG_CONTEXT_TRACKING_IDLE=y
CONFIG_CPU_GENERIC_DUMP_TLB=y
CONFIG_CPU_HAS_DIEI=y
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_RIXI=y
CONFIG_CPU_HAS_SYNC=y
CONFIG_CPU_LITTLE_ENDIAN=y
CONFIG_CPU_MIPS32=y
# CONFIG_CPU_MIPS32_R1 is not set
CONFIG_CPU_MIPS32_R2=y
CONFIG_CPU_MIPSR2=y
CONFIG_CPU_MIPSR2_IRQ_EI=y
CONFIG_CPU_MIPSR2_IRQ_VI=y
CONFIG_CPU_MITIGATIONS=y
CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y
CONFIG_CPU_R4K_CACHE_TLB=y
CONFIG_CPU_RMAP=y
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
CONFIG_CPU_SUPPORTS_MSA=y
CONFIG_CRC16=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_HASH_INFO=y
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
CONFIG_CRYPTO_LIB_GF128MUL=y
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2
CONFIG_CRYPTO_LIB_SHA1=y
CONFIG_CRYPTO_LIB_UTILS=y
CONFIG_CRYPTO_LZO=y
CONFIG_CRYPTO_ZSTD=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_ZBOOT=y
CONFIG_DMA_NEED_SYNC=y
CONFIG_DMA_NONCOHERENT=y
CONFIG_DTB_ECONET_NONE=y
CONFIG_DTC=y
CONFIG_EARLY_PRINTK=y
CONFIG_EARLY_PRINTK_8250=y
CONFIG_ECONET=y
CONFIG_ECONET_EN751221_TIMER=y
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
CONFIG_FS_IOMAP=y
CONFIG_FUNCTION_ALIGNMENT=0
CONFIG_FW_LOADER_PAGED_BUF=y
CONFIG_FW_LOADER_SYSFS=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_GENERIC_GETTIMEOFDAY=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IOMAP=y
CONFIG_GENERIC_IRQ_CHIP=y
CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_LIB_ASHLDI3=y
CONFIG_GENERIC_LIB_ASHRDI3=y
CONFIG_GENERIC_LIB_CMPDI2=y
CONFIG_GENERIC_LIB_LSHRDI3=y
CONFIG_GENERIC_LIB_UCMPDI2=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_SCHED_CLOCK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GPIO_CDEV=y
CONFIG_HARDWARE_WATCHPOINTS=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_IOPORT_MAP=y
CONFIG_HZ_PERIODIC=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_MIPS_CPU=y
CONFIG_IRQ_WORK=y
# CONFIG_JFFS2_FS is not set
CONFIG_LIBFDT=y
CONFIG_LOCK_DEBUGGING_SUPPORT=y
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_MIGRATION=y
CONFIG_MIPS=y
CONFIG_MIPS_ASID_BITS=8
CONFIG_MIPS_ASID_SHIFT=0
CONFIG_MIPS_CM=y
# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set
CONFIG_MIPS_CMDLINE_FROM_DTB=y
CONFIG_MIPS_CPC=y
CONFIG_MIPS_CPS=y
# CONFIG_MIPS_CPS_NS16550_BOOL is not set
CONFIG_MIPS_CPU_SCACHE=y
CONFIG_MIPS_GIC=y
CONFIG_MIPS_L1_CACHE_SHIFT=5
CONFIG_MIPS_MT=y
CONFIG_MIPS_MT_FPAFF=y
CONFIG_MIPS_MT_SMP=y
# CONFIG_MIPS_NO_APPENDED_DTB is not set
CONFIG_MIPS_NR_CPU_NR_MAP=4
CONFIG_MIPS_PERF_SHARED_TC_COUNTERS=y
CONFIG_MIPS_RAW_APPENDED_DTB=y
CONFIG_MIPS_SPRAM=y
CONFIG_MMU_LAZY_TLB_REFCOUNT=y
CONFIG_MODULES_USE_ELF_REL=y
CONFIG_MTD_NAND_CORE=y
CONFIG_MTD_NAND_ECC=y
CONFIG_MTD_NAND_MTK_BMT=y
CONFIG_MTD_SPI_NAND=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_BEB_LIMIT=13
CONFIG_MTD_UBI_BLOCK=y
CONFIG_MTD_UBI_WL_THRESHOLD=4096
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_SRCU_NMI_SAFE=y
CONFIG_NET_EGRESS=y
CONFIG_NET_FLOW_LIMIT=y
CONFIG_NET_INGRESS=y
CONFIG_NET_XGRESS=y
CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y
CONFIG_NR_CPUS=4
CONFIG_NVMEM=y
CONFIG_NVMEM_LAYOUTS=y
CONFIG_NVMEM_SYSFS=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_PADATA=y
CONFIG_PAGE_POOL=y
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
CONFIG_PCI_DRIVERS_LEGACY=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PGTABLE_LEVELS=2
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RATIONAL=y
CONFIG_REGMAP=y
CONFIG_REGMAP_MMIO=y
CONFIG_RFS_ACCEL=y
CONFIG_RPS=y
# CONFIG_SCHED_CORE is not set
CONFIG_SCHED_SMT=y
CONFIG_SERIAL_8250_AIROHA=y
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SGL_ALLOC=y
CONFIG_SMP=y
CONFIG_SMP_UP=y
CONFIG_SOCK_RX_QUEUE_MAPPING=y
# CONFIG_SOC_ECONET_EN751221 is not set
CONFIG_SOC_ECONET_EN7528=y
CONFIG_SPI=y
# CONFIG_SPI_AIROHA_EN7523 is not set
CONFIG_SPI_AIROHA_SNFI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_MEM=y
CONFIG_SPLIT_PTE_PTLOCKS=y
CONFIG_SYNC_R4K=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
CONFIG_SYS_HAS_CPU_MIPS32_R2=y
CONFIG_SYS_HAS_EARLY_PRINTK=y
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
CONFIG_SYS_SUPPORTS_HIGHMEM=y
CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y
CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
CONFIG_SYS_SUPPORTS_MIPS16=y
CONFIG_SYS_SUPPORTS_MIPS_CPS=y
CONFIG_SYS_SUPPORTS_MULTITHREADING=y
CONFIG_SYS_SUPPORTS_SCHED_SMT=y
CONFIG_SYS_SUPPORTS_SMP=y
CONFIG_SYS_SUPPORTS_ZBOOT=y
CONFIG_SYS_SUPPORTS_ZBOOT_UART16550=y
CONFIG_TARGET_ISA_REV=2
CONFIG_TICK_CPU_ACCOUNTING=y
CONFIG_TIMER_OF=y
CONFIG_TIMER_PROBE=y
CONFIG_TREE_RCU=y
CONFIG_TREE_SRCU=y
CONFIG_UBIFS_FS=y
CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
CONFIG_USE_OF=y
CONFIG_WEAK_ORDERING=y
CONFIG_XPS=y
CONFIG_XXHASH=y
CONFIG_ZBOOT_LOAD_ADDRESS=0x80020000
CONFIG_ZLIB_DEFLATE=y
CONFIG_ZLIB_INFLATE=y
CONFIG_ZSTD_COMMON=y
CONFIG_ZSTD_COMPRESS=y
CONFIG_ZSTD_DECOMPRESS=y

View file

@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright (C) 2025 OpenWrt.org
define Profile/Default
NAME:=Default Profile
endef
define Profile/Default/Description
Default package set compatible with most EN7528 boards.
endef
$(eval $(call Profile,Default))

View file

@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
ARCH:=mipsel
SUBTARGET:=en7528
BOARDNAME:=EN7528 based boards
CPU_TYPE:=24kc
KERNELNAME:=vmlinuz.bin
define Target/Description
Build firmware images for EcoNet EN7528 based boards.
endef

View file

@ -0,0 +1,6 @@
define Device/en7528_generic
DEVICE_VENDOR := EN7528
DEVICE_MODEL := Generic
DEVICE_DTS := en7528_generic
endef
TARGET_DEVICES += en7528_generic

View file

@ -0,0 +1,119 @@
From: Ahmed Naseef <naseefkm@gmail.com>
Subject: mips: econet: add EN7528 SoC support
The EN7528 is a little endian dual-core MIPS 1004Kc SoC used in xPON
devices. Unlike the big endian EN751221, EN7528 uses the MIPS GIC
interrupt controller for SMP.
This adds boot support for the EN7528 SoC family:
- New SOC_ECONET_EN7528 Kconfig option
- Little endian support for ECONET platform
- UART base address adjustment for endianness
- CPS SMP ops registration for multi-core support
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -391,13 +391,13 @@ config MACH_DECSTATION
config ECONET
bool "EcoNet MIPS family"
select BOOT_RAW
- select CPU_BIG_ENDIAN
select DEBUG_ZBOOT if DEBUG_KERNEL
select EARLY_PRINTK_8250
select ECONET_EN751221_TIMER
select SERIAL_8250
select SERIAL_OF_PLATFORM
select SYS_SUPPORTS_BIG_ENDIAN
+ select SYS_SUPPORTS_LITTLE_ENDIAN
select SYS_HAS_CPU_MIPS32_R1
select SYS_HAS_CPU_MIPS32_R2
select SYS_HAS_EARLY_PRINTK
--- a/arch/mips/econet/Kconfig
+++ b/arch/mips/econet/Kconfig
@@ -12,6 +12,7 @@ choice
config SOC_ECONET_EN751221
bool "EN751221 family"
select COMMON_CLK
+ select CPU_BIG_ENDIAN
select ECONET_EN751221_INTC
select IRQ_MIPS_CPU
select SMP
@@ -22,6 +23,23 @@ choice
They are based on single core MIPS 34Kc processors. To boot
this kernel, you will need a device tree such as
MIPS_RAW_APPENDED_DTB=y, and a root filesystem.
+
+ config SOC_ECONET_EN7528
+ bool "EN7528 family"
+ select COMMON_CLK
+ select CPU_LITTLE_ENDIAN
+ select IRQ_MIPS_CPU
+ select MIPS_CPU_SCACHE
+ select MIPS_GIC
+ select SMP
+ select SMP_UP
+ select SYS_SUPPORTS_HIGHMEM
+ select SYS_SUPPORTS_MIPS_CPS
+ select SYS_SUPPORTS_MULTITHREADING
+ select SYS_SUPPORTS_SMP
+ help
+ The EN7528 family with dual-core MIPS 1004Kc.
+ Requires MIPS_RAW_APPENDED_DTB=y for boot.
endchoice
choice
--- a/arch/mips/econet/init.c
+++ b/arch/mips/econet/init.c
@@ -16,11 +16,16 @@
#include <asm/prom.h>
#include <asm/smp-ops.h>
#include <asm/reboot.h>
+#include <asm/mips-cps.h>
#define CR_AHB_RSTCR ((void __iomem *)CKSEG1ADDR(0x1fb00040))
#define RESET BIT(31)
-#define UART_BASE CKSEG1ADDR(0x1fbf0003)
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+#define UART_BASE CKSEG1ADDR(0x1fbf0000) /* LE: byte at offset 0 */
+#else
+#define UART_BASE CKSEG1ADDR(0x1fbf0003) /* BE: byte at offset 3 */
+#endif
#define UART_REG_SHIFT 2
static void hw_reset(char *command)
@@ -51,11 +56,18 @@ void __init plat_mem_setup(void)
early_init_dt_scan_memory();
}
-/* 3. Overload __weak device_tree_init(), add SMP_UP ops */
+/* 3. Overload __weak device_tree_init(), register SMP ops */
void __init device_tree_init(void)
{
unflatten_and_copy_device_tree();
+ /* EN7528 dual-core: probe CM/CPC and register CPS SMP ops */
+ mips_cm_probe();
+ mips_cpc_probe();
+
+ if (!register_cps_smp_ops())
+ return;
+
register_up_smp_ops();
}
--- a/arch/mips/boot/compressed/uart-16550.c
+++ b/arch/mips/boot/compressed/uart-16550.c
@@ -21,7 +21,11 @@
#endif
#ifdef CONFIG_ECONET
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+#define EN75_UART_BASE 0x1fbf0000
+#else
#define EN75_UART_BASE 0x1fbf0003
+#endif
#define PORT(offset) (CKSEG1ADDR(EN75_UART_BASE) + (4 * (offset)))
#endif

View file

@ -0,0 +1,264 @@
From: Ahmed Naseef <naseefkm@gmail.com>
Subject: mips: econet: timer: add EN7528 support to EN751221 timer driver
Extend the existing EN751221 timer driver to support EN7528/EN751627 SoCs.
The driver now auto-detects the IRQ mode based on device tree:
- EN751221: Single percpu IRQ (legacy interrupt controller)
- EN7528/EN751627: Separate IRQ per CPU (GIC shared interrupts)
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -79,7 +79,10 @@ config ECONET_EN751221_TIMER
select CLKSRC_MMIO
select TIMER_OF
help
- Support for CPU timer found on EcoNet MIPS based SoCs.
+ Support for CPU timer found on EcoNet EN75xx MIPS based SoCs
+ (EN751221, EN751627, EN7528). The driver supports both GIC-based
+ (separate IRQ per CPU) and legacy interrupt controller (percpu IRQ)
+ modes.
config FTTMR010_TIMER
bool "Faraday Technology timer driver" if COMPILE_TEST
--- a/drivers/clocksource/timer-econet-en751221.c
+++ b/drivers/clocksource/timer-econet-en751221.c
@@ -2,12 +2,20 @@
/*
* Timer present on EcoNet EN75xx MIPS based SoCs.
*
+ * This driver supports both:
+ * - EN751221: Single percpu IRQ mode (legacy interrupt controller)
+ * - EN7528/EN751627: Separate IRQ per CPU mode (GIC shared interrupts)
+ *
+ * The mode is auto-detected based on IRQ count in device tree.
+ *
* Copyright (C) 2025 by Caleb James DeLisle <cjd@cjdns.fr>
+ * Copyright (C) 2025 by Ahmed Naseef <naseefkm@gmail.com>
*/
#include <linux/io.h>
#include <linux/cpumask.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/sched_clock.h>
#include <linux/of.h>
@@ -21,10 +29,14 @@
#define ECONET_MAX_DELTA GENMASK(ECONET_BITS - 2, 0)
/* 34Kc hardware has 1 block and 1004Kc has 2. */
#define ECONET_NUM_BLOCKS DIV_ROUND_UP(NR_CPUS, 2)
+#define ECONET_MAX_IRQS 4
static struct {
void __iomem *membase[ECONET_NUM_BLOCKS];
u32 freq_hz;
+ int irqs[ECONET_MAX_IRQS];
+ int num_irqs;
+ bool use_percpu_irq;
} econet_timer __ro_after_init;
static DEFINE_PER_CPU(struct clock_event_device, econet_timer_pcpu);
@@ -98,12 +110,21 @@ static int cevt_init_cpu(uint cpu)
struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, cpu);
u32 reg;
+ if (!econet_timer.use_percpu_irq && cpu >= econet_timer.num_irqs)
+ return -EINVAL;
+
pr_debug("%s: Setting up clockevent for CPU %d\n", cd->name, cpu);
reg = ioread32(reg_ctl(cpu)) | ctl_bit_enabled(cpu);
iowrite32(reg, reg_ctl(cpu));
- enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
+ if (econet_timer.use_percpu_irq) {
+ enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
+ } else {
+ if (irq_force_affinity(econet_timer.irqs[cpu], cpumask_of(cpu)))
+ pr_warn("%s: failed to set IRQ %d affinity to CPU %d\n",
+ cd->name, econet_timer.irqs[cpu], cpu);
+ }
/* Do this last because it synchronously configures the timer */
clockevents_config_and_register(cd, econet_timer.freq_hz,
@@ -126,7 +147,21 @@ static void __init cevt_dev_init(uint cp
iowrite32(U32_MAX, reg_compare(cpu));
}
-static int __init cevt_init(struct device_node *np)
+static void __init cevt_setup_clockevent(struct clock_event_device *cd,
+ struct device_node *np,
+ int irq, int cpu)
+{
+ cd->rating = 310;
+ cd->features = CLOCK_EVT_FEAT_ONESHOT |
+ CLOCK_EVT_FEAT_C3STOP |
+ CLOCK_EVT_FEAT_PERCPU;
+ cd->set_next_event = cevt_set_next_event;
+ cd->irq = irq;
+ cd->cpumask = cpumask_of(cpu);
+ cd->name = np->name;
+}
+
+static int __init cevt_init_percpu(struct device_node *np)
{
int i, irq, ret;
@@ -137,42 +172,85 @@ static int __init cevt_init(struct devic
}
ret = request_percpu_irq(irq, cevt_interrupt, np->name, &econet_timer_pcpu);
-
if (ret < 0) {
pr_err("%pOFn: IRQ %d setup failed (%d)\n", np, irq, ret);
- goto err_unmap_irq;
+ irq_dispose_mapping(irq);
+ return ret;
}
for_each_possible_cpu(i) {
struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, i);
- cd->rating = 310,
- cd->features = CLOCK_EVT_FEAT_ONESHOT |
- CLOCK_EVT_FEAT_C3STOP |
- CLOCK_EVT_FEAT_PERCPU;
- cd->set_next_event = cevt_set_next_event;
- cd->irq = irq;
- cd->cpumask = cpumask_of(i);
- cd->name = np->name;
+ cevt_setup_clockevent(cd, np, irq, i);
+ cevt_dev_init(i);
+ }
+
+ return 0;
+}
+
+static int __init cevt_init_separate(struct device_node *np)
+{
+ int i, ret;
+
+ for (i = 0; i < econet_timer.num_irqs; i++) {
+ struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, i);
+
+ econet_timer.irqs[i] = irq_of_parse_and_map(np, i);
+ if (econet_timer.irqs[i] <= 0) {
+ pr_err("%pOFn: irq_of_parse_and_map failed", np);
+ ret = -EINVAL;
+ goto err_free_irqs;
+ }
+
+ ret = request_irq(econet_timer.irqs[i], cevt_interrupt,
+ IRQF_TIMER | IRQF_NOBALANCING,
+ np->name, NULL);
+ if (ret < 0) {
+ pr_err("%pOFn: IRQ %d setup failed (%d)\n", np,
+ econet_timer.irqs[i], ret);
+ irq_dispose_mapping(econet_timer.irqs[i]);
+ goto err_free_irqs;
+ }
+ cevt_setup_clockevent(cd, np, econet_timer.irqs[i], i);
cevt_dev_init(i);
}
- cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
- "clockevents/econet/timer:starting",
- cevt_init_cpu, NULL);
return 0;
-err_unmap_irq:
- irq_dispose_mapping(irq);
+err_free_irqs:
+ while (--i >= 0) {
+ free_irq(econet_timer.irqs[i], NULL);
+ irq_dispose_mapping(econet_timer.irqs[i]);
+ }
return ret;
}
+static int __init cevt_init(struct device_node *np)
+{
+ econet_timer.num_irqs = of_irq_count(np);
+ if (econet_timer.num_irqs <= 0 || econet_timer.num_irqs > ECONET_MAX_IRQS) {
+ pr_err("%pOFn: invalid IRQ count %d\n", np, econet_timer.num_irqs);
+ return -EINVAL;
+ }
+
+ /* Auto-detect mode based on IRQ count:
+ * 1 IRQ = percpu mode (EN751221)
+ * N IRQs = separate IRQ per CPU (EN7528/EN751627)
+ */
+ econet_timer.use_percpu_irq = (econet_timer.num_irqs == 1);
+
+ if (econet_timer.use_percpu_irq)
+ return cevt_init_percpu(np);
+ else
+ return cevt_init_separate(np);
+}
+
static int __init timer_init(struct device_node *np)
{
int num_blocks = DIV_ROUND_UP(num_possible_cpus(), 2);
struct clk *clk;
- int ret;
+ int ret, i;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
@@ -182,11 +260,12 @@ static int __init timer_init(struct devi
econet_timer.freq_hz = clk_get_rate(clk);
- for (int i = 0; i < num_blocks; i++) {
+ for (i = 0; i < num_blocks; i++) {
econet_timer.membase[i] = of_iomap(np, i);
if (!econet_timer.membase[i]) {
pr_err("%pOFn: failed to map register [%d]\n", np, i);
- return -ENXIO;
+ ret = -ENXIO;
+ goto err_unmap;
}
}
@@ -196,21 +275,34 @@ static int __init timer_init(struct devi
clocksource_mmio_readl_up);
if (ret) {
pr_err("%pOFn: clocksource_mmio_init failed: %d", np, ret);
- return ret;
+ goto err_unmap;
}
ret = cevt_init(np);
if (ret < 0)
- return ret;
+ goto err_unmap;
+
+ cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "clockevents/econet/timer:starting",
+ cevt_init_cpu, NULL);
sched_clock_register(sched_clock_read, ECONET_BITS,
econet_timer.freq_hz);
- pr_info("%pOFn: using %u.%03u MHz high precision timer\n", np,
+ pr_info("%pOFn: using %u.%03u MHz high precision timer (%s mode)\n", np,
econet_timer.freq_hz / 1000000,
- (econet_timer.freq_hz / 1000) % 1000);
+ (econet_timer.freq_hz / 1000) % 1000,
+ econet_timer.use_percpu_irq ? "percpu" : "separate IRQ");
return 0;
+
+err_unmap:
+ for (i = 0; i < num_blocks; i++) {
+ if (econet_timer.membase[i])
+ iounmap(econet_timer.membase[i]);
+ }
+ return ret;
}
-TIMER_OF_DECLARE(econet_timer_hpt, "econet,en751221-timer", timer_init);
+TIMER_OF_DECLARE(econet_en751221_timer, "econet,en751221-timer", timer_init);
+TIMER_OF_DECLARE(econet_en7528_timer, "econet,en7528-timer", timer_init);

View file

@ -0,0 +1,21 @@
spi: airoha-snfi: enable for EcoNet EN7528
Enable the Airoha SNFI (SPI NAND Flash Interface) driver for EcoNet
EN7528 SoC. The EN7528 shares the same SPI controller and NFI DMA
engine as the Airoha EN7523/EN7581, with identical register layouts.
Using the DMA-capable SNFI driver provides significantly better
performance compared to the manual mode spi-en7523 driver.
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -59,7 +59,7 @@ comment "SPI Master Controller Drivers"
config SPI_AIROHA_SNFI
tristate "Airoha SPI NAND Flash Interface"
- depends on ARCH_AIROHA || COMPILE_TEST
+ depends on ARCH_AIROHA || ECONET || COMPILE_TEST
depends on SPI_MASTER
select REGMAP_MMIO
help

View file

@ -1,13 +1,13 @@
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -392,6 +392,7 @@ config ECONET
@@ -391,6 +391,7 @@ config MACH_DECSTATION
config ECONET
bool "EcoNet MIPS family"
select BOOT_RAW
select CPU_BIG_ENDIAN
+ select DMA_NONCOHERENT
select DEBUG_ZBOOT if DEBUG_KERNEL
select EARLY_PRINTK_8250
select ECONET_EN751221_TIMER
select SERIAL_8250
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -71,7 +71,7 @@ config USB_XHCI_HISTB

View file

@ -0,0 +1,206 @@
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_en7523.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Airoha EN7523 driver.
+ *
+ * Copyright (c) 2022 Genexis Sweden AB
+ * Author: Benjamin Larsson <benjamin.larsson@genexis.eu>
+ */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
+#include <linux/console.h>
+#include <linux/dma-mapping.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include "8250.h"
+
+
+/* The Airoha UART is 16550-compatible except for the baud rate calculation.
+ *
+ * crystal_clock = 20 MHz
+ * xindiv_clock = crystal_clock / clock_div
+ * (x/y) = XYD, 32 bit register with 16 bits of x and and then 16 bits of y
+ * clock_div = XINCLK_DIVCNT (default set to 10 (0x4)),
+ * - 3 bit register [ 1, 2, 4, 8, 10, 12, 16, 20 ]
+ *
+ * baud_rate = ((xindiv_clock) * (x/y)) / ([BRDH,BRDL] * 16)
+ *
+ * XYD_y seems to need to be larger then XYD_x for things to work.
+ * Setting [BRDH,BRDL] to [0,1] and XYD_y to 65000 give even values
+ * for usual baud rates.
+ *
+ * Selecting divider needs to fulfill
+ * 1.8432 MHz <= xindiv_clk <= APB clock / 2
+ * The clocks are unknown but a divider of value 1 did not work.
+ *
+ * Optimally the XYD, BRD and XINCLK_DIVCNT registers could be searched to
+ * find values that gives the least error for every baud rate. But searching
+ * the space takes time and in practise only a few rates are of interest.
+ * With some value combinations not working a tested subset is used giving
+ * a usable range from 110 to 460800 baud.
+ */
+
+#define CLOCK_DIV_TAB_ELEMS 3
+#define XYD_Y 65000
+#define XINDIV_CLOCK 20000000
+#define UART_BRDL_20M 0x01
+#define UART_BRDH_20M 0x00
+
+static int clock_div_tab[] = { 10, 4, 2};
+static int clock_div_reg[] = { 4, 2, 1};
+
+
+int en7523_set_uart_baud_rate (struct uart_port *port, unsigned int baud)
+{
+ struct uart_8250_port *up = up_to_u8250p(port);
+ unsigned int xyd_x, nom, denom;
+ int i;
+
+ /* set DLAB to access the baud rate divider registers (BRDH, BRDL) */
+ serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
+
+ /* set baud rate calculation defaults */
+
+ /* set BRDIV ([BRDH,BRDL]) to 1 */
+ serial_port_out(port, UART_BRDL, UART_BRDL_20M);
+ serial_port_out(port, UART_BRDH, UART_BRDH_20M);
+
+ /* calculate XYD_x and XINCLKDR register */
+
+ for (i = 0 ; i < CLOCK_DIV_TAB_ELEMS ; i++) {
+ denom = (XINDIV_CLOCK/40) / clock_div_tab[i];
+ nom = (baud * (XYD_Y/40));
+ xyd_x = ((nom/denom) << 4);
+ if (xyd_x < XYD_Y) break;
+ }
+
+ serial_port_out(port, UART_XINCLKDR, clock_div_reg[i]);
+ serial_port_out(port, UART_XYD, (xyd_x<<16) | XYD_Y);
+
+ /* unset DLAB */
+ serial_port_out(port, UART_LCR, up->lcr);
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(en7523_set_uart_baud_rate);
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -341,6 +341,7 @@ static const struct of_device_id of_plat
{ .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, },
{ .compatible = "nuvoton,wpcm450-uart", .data = (void *)PORT_NPCM, },
{ .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, },
+ { .compatible = "airoha,en7523-uart", .data = (void *)PORT_AIROHA, },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, of_platform_serial_table);
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -319,6 +319,14 @@ static const struct serial8250_config ua
.rxtrig_bytes = {1, 8, 16, 30},
.flags = UART_CAP_FIFO | UART_CAP_AFE,
},
+ [PORT_AIROHA] = {
+ .name = "Airoha 16550",
+ .fifo_size = 8,
+ .tx_loadsz = 1,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01,
+ .rxtrig_bytes = {1, 4},
+ .flags = UART_CAP_FIFO,
+ },
};
/* Uart divisor latch read */
@@ -2835,6 +2843,12 @@ serial8250_do_set_termios(struct uart_po
serial8250_set_divisor(port, baud, quot, frac);
+#ifdef CONFIG_SERIAL_8250_AIROHA
+ /* Airoha SoCs have custom registers for baud rate settings */
+ if (port->type == PORT_AIROHA)
+ en7523_set_uart_baud_rate(port, baud);
+#endif
+
/*
* LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
* is written without DLAB set, this mode will be disabled.
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -355,6 +355,16 @@ config SERIAL_8250_ACORN
system, say Y to this option. The driver can handle 1, 2, or 3 port
cards. If unsure, say N.
+config SERIAL_8250_AIROHA
+ tristate "Airoha UART support"
+ depends on (ARCH_AIROHA || COMPILE_TEST) && OF && SERIAL_8250
+ help
+ Selecting this option enables an Airoha SoC specific baud rate
+ calculation routine on an otherwise 16550 compatible UART hardware.
+
+ If you have an Airoha based board and want to use the serial port,
+ say Y to this option. If unsure, say N.
+
config SERIAL_8250_BCM2835AUX
tristate "BCM2835 auxiliar mini UART support"
depends on ARCH_BCM2835 || COMPILE_TEST
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SERIAL_8250_CONSOLE) += 825
obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o
obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o
+obj-$(CONFIG_SERIAL_8250_AIROHA) += 8250_en7523.o
obj-$(CONFIG_SERIAL_8250_ASPEED_VUART) += 8250_aspeed_vuart.o
obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o
obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o
--- a/include/uapi/linux/serial_reg.h
+++ b/include/uapi/linux/serial_reg.h
@@ -383,5 +383,17 @@
#define UART_ALTR_EN_TXFIFO_LW 0x01 /* Enable the TX FIFO Low Watermark */
#define UART_ALTR_TX_LOW 0x41 /* Tx FIFO Low Watermark */
+/*
+ * These are definitions for the Airoha EN75XX uart registers
+ * Normalized because of 32 bits registers.
+ */
+#define UART_BRDL 0
+#define UART_BRDH 1
+#define UART_XINCLKDR 10
+#define UART_XYD 11
+#define UART_TXLVLCNT 12
+#define UART_RXLVLCNT 13
+#define UART_FINTLVL 14
+
#endif /* _LINUX_SERIAL_REG_H */
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -31,6 +31,7 @@
#define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */
#define PORT_RT2880 29 /* Ralink RT2880 internal UART */
#define PORT_16550A_FSL64 30 /* Freescale 16550 UART with 64 FIFOs */
+#define PORT_AIROHA 31 /* Airoha 16550 UART */
/*
* ARM specific type numbers. These are not currently guaranteed
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -195,6 +195,7 @@ void serial8250_do_set_mctrl(struct uart
void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
unsigned int quot);
int fsl8250_handle_irq(struct uart_port *port);
+int en7523_set_uart_baud_rate(struct uart_port *port, unsigned int baud);
int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
u16 serial8250_rx_chars(struct uart_8250_port *up, u16 lsr);
void serial8250_read_char(struct uart_8250_port *up, u16 lsr);

View file

@ -0,0 +1,18 @@
serial: 8250: airoha: add EcoNet platform support
The EcoNet EN75xx SoCs use the same UART IP core as the Airoha
AN7523 SoCs. Add ECONET to the Kconfig dependency to enable
the Airoha UART driver for EcoNet platforms.
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -357,7 +357,7 @@ config SERIAL_8250_ACORN
config SERIAL_8250_AIROHA
tristate "Airoha UART support"
- depends on (ARCH_AIROHA || COMPILE_TEST) && OF && SERIAL_8250
+ depends on (ARCH_AIROHA || ECONET || COMPILE_TEST) && OF && SERIAL_8250
help
Selecting this option enables an Airoha SoC specific baud rate
calculation routine on an otherwise 16550 compatible UART hardware.