mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2026-01-11 14:22:11 -05:00
kernel: Add new platform EcoNet MIPS
EcoNet EN75xx is a big endian MIPS platform used in XPON (fiber), DSL, and SIM (3g/4g) applications. Complete GPL vender SDKs exist for this platform, but are based on Linux 2.6. The bulk of this submission has already been accepted upstream: https://patchwork.kernel.org/project/linux-mips/list/?series=960479&state=* This platform uses a bootloader that is derived from old TrendChip code. This bootloader implements a frustratingly complex Bad Block Table which is implemented here in en75_bmt.c This BMT is not upstreamed because it depends on mtk_bmt framework which likewise is not upstreamed. This BMT system rewrites block indexes in flash and if the bootloader considers it to be corrupted, it will attempt to automatically rebuild on boot. So without implementing the algorithm, you can't safely use the disk at all. Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr> Link: https://github.com/openwrt/openwrt/pull/19021 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
parent
0575c3a181
commit
73d0f92460
22
target/linux/econet/Makefile
Normal file
22
target/linux/econet/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Copyright (C) 2009-2025 OpenWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
ARCH:=mips
|
||||
BOARD:=econet
|
||||
BOARDNAME:=EcoNet EN75xx MIPS
|
||||
FEATURES:=dt source-only squashfs nand
|
||||
SUBTARGETS:=en751221
|
||||
|
||||
KERNEL_PATCHVER:=6.12
|
||||
|
||||
define Target/Description
|
||||
Build firmware image for EcoNet EN75xx MIPS based boards.
|
||||
endef
|
||||
|
||||
# include the profiles
|
||||
include $(INCLUDE_DIR)/target.mk
|
||||
|
||||
$(eval $(call BuildTarget))
|
||||
82
target/linux/econet/dts/en751221.dtsi
Normal file
82
target/linux/econet/dts/en751221.dtsi
Normal file
@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
compatible = "econet,en751221";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
hpt_clock: clock {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <200000000>; /* 200 MHz */
|
||||
};
|
||||
|
||||
cpus: cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "mips,mips34Kc";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
cpuintc: interrupt-controller {
|
||||
compatible = "mti,cpu-interrupt-controller";
|
||||
interrupt-controller;
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
|
||||
spi_ctrl: spi_controller@1fa10000 {
|
||||
compatible = "airoha,en7523-spi";
|
||||
reg = <0x1fa10000 0x140>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-rx-bus-width = <2>;
|
||||
spi-tx-bus-width = <2>;
|
||||
|
||||
nand: nand@0 {
|
||||
compatible = "spi-nand";
|
||||
reg = <0>;
|
||||
nand-ecc-engine = <&nand>;
|
||||
};
|
||||
};
|
||||
|
||||
intc: interrupt-controller@1fb40000 {
|
||||
compatible = "econet,en751221-intc";
|
||||
reg = <0x1fb40000 0x100>;
|
||||
interrupt-parent = <&cpuintc>;
|
||||
interrupts = <2>;
|
||||
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
econet,shadow-interrupts = <7 2>, <8 3>, <13 12>, <30 29>;
|
||||
};
|
||||
|
||||
uart: serial@1fbf0000 {
|
||||
compatible = "ns16550";
|
||||
reg = <0x1fbf0000 0x30>;
|
||||
reg-io-width = <4>;
|
||||
reg-shift = <2>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <0>;
|
||||
/*
|
||||
* Conversion of baud rate to clock frequency requires a
|
||||
* computation that is not in the ns16550 driver, so this
|
||||
* uart is fixed at 115200 baud.
|
||||
*/
|
||||
clock-frequency = <1843200>;
|
||||
};
|
||||
|
||||
timer_hpt: timer@1fbf0400 {
|
||||
compatible = "econet,en751221-timer";
|
||||
reg = <0x1fbf0400 0x100>;
|
||||
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <30>;
|
||||
clocks = <&hpt_clock>;
|
||||
};
|
||||
};
|
||||
205
target/linux/econet/en751221/config-6.12
Normal file
205
target/linux/econet/en751221/config-6.12
Normal file
@ -0,0 +1,205 @@
|
||||
CONFIG_ARCH_32BIT_OFF_T=y
|
||||
CONFIG_ARCH_KEEP_MEMBLOCK=y
|
||||
CONFIG_ARCH_MMAP_RND_BITS_MAX=15
|
||||
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15
|
||||
CONFIG_ASN1=y
|
||||
CONFIG_ASSOCIATIVE_ARRAY=y
|
||||
CONFIG_ASYMMETRIC_KEY_TYPE=y
|
||||
CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
|
||||
CONFIG_CLKSRC_MMIO=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
CONFIG_CLZ_TAB=y
|
||||
CONFIG_COMMON_CLK=y
|
||||
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
|
||||
CONFIG_COMPAT_32BIT_TIME=y
|
||||
CONFIG_CONTEXT_TRACKING=y
|
||||
CONFIG_CONTEXT_TRACKING_IDLE=y
|
||||
CONFIG_CPU_BIG_ENDIAN=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_MIPS32=y
|
||||
# CONFIG_CPU_MIPS32_R1 is not set
|
||||
CONFIG_CPU_MIPS32_R2=y
|
||||
CONFIG_CPU_MIPSR2=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_CRC32C=m
|
||||
CONFIG_CRYPTO_DEFLATE=y
|
||||
CONFIG_CRYPTO_ECB=y
|
||||
CONFIG_CRYPTO_HASH_INFO=y
|
||||
CONFIG_CRYPTO_HMAC=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_RSA=y
|
||||
CONFIG_CRYPTO_SIG=y
|
||||
CONFIG_CRYPTO_SIG2=y
|
||||
CONFIG_CRYPTO_ZSTD=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DEBUG_ZBOOT=y
|
||||
CONFIG_DTB_ECONET_NONE=y
|
||||
# CONFIG_DTB_ECONET_SMARTFIBER_XP8421_B is not set
|
||||
CONFIG_DTC=y
|
||||
CONFIG_EARLY_PRINTK=y
|
||||
CONFIG_EARLY_PRINTK_8250=y
|
||||
CONFIG_ECONET=y
|
||||
CONFIG_ECONET_EN751221_INTC=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_FORCED_THREADING=y
|
||||
CONFIG_IRQ_MIPS_CPU=y
|
||||
CONFIG_IRQ_WORK=y
|
||||
# CONFIG_JFFS2_FS is not set
|
||||
CONFIG_KEYS=y
|
||||
CONFIG_LIBCRC32C=m
|
||||
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_CMDLINE_FROM_BOOTLOADER is not set
|
||||
CONFIG_MIPS_CMDLINE_FROM_DTB=y
|
||||
CONFIG_MIPS_L1_CACHE_SHIFT=5
|
||||
# CONFIG_MIPS_NO_APPENDED_DTB is not set
|
||||
CONFIG_MIPS_NR_CPU_NR_MAP=2
|
||||
CONFIG_MIPS_RAW_APPENDED_DTB=y
|
||||
CONFIG_MIPS_SPRAM=y
|
||||
CONFIG_MMU_LAZY_TLB_REFCOUNT=y
|
||||
CONFIG_MODULES_USE_ELF_REL=y
|
||||
CONFIG_MPILIB=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_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=2
|
||||
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_OID_REGISTRY=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_PKCS7_MESSAGE_PARSER=y
|
||||
# CONFIG_PKCS7_TEST_KEY is not set
|
||||
# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set
|
||||
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
|
||||
CONFIG_QUEUED_RWLOCKS=y
|
||||
CONFIG_QUEUED_SPINLOCKS=y
|
||||
CONFIG_RANDSTRUCT_NONE=y
|
||||
CONFIG_RATIONAL=y
|
||||
CONFIG_RFS_ACCEL=y
|
||||
CONFIG_RPS=y
|
||||
# CONFIG_SECONDARY_TRUSTED_KEYRING is not set
|
||||
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=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_AIROHA_EN7523=y
|
||||
CONFIG_SPI_MASTER=y
|
||||
CONFIG_SPI_MEM=y
|
||||
CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
|
||||
CONFIG_SYSCTL_EXCEPTION_TRACE=y
|
||||
CONFIG_SYSTEM_DATA_VERIFICATION=y
|
||||
# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set
|
||||
CONFIG_SYSTEM_TRUSTED_KEYRING=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_MIPS16=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_ATIME_SUPPORT=y
|
||||
CONFIG_UBIFS_FS=y
|
||||
CONFIG_UBIFS_FS_AUTHENTICATION=y
|
||||
CONFIG_UBIFS_FS_SECURITY=y
|
||||
CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
|
||||
CONFIG_USE_OF=y
|
||||
CONFIG_X509_CERTIFICATE_PARSER=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
|
||||
14
target/linux/econet/en751221/profiles/00-default.mk
Normal file
14
target/linux/econet/en751221/profiles/00-default.mk
Normal file
@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Copyright (C) 2016 OpenWrt.org
|
||||
|
||||
# PACKAGES:= kmod-usb2 kmod-ath9k-htc wpad-basic-mbedtls
|
||||
|
||||
define Profile/Default
|
||||
NAME:=Default Profile (all drivers)
|
||||
endef
|
||||
|
||||
define Profile/Default/Description
|
||||
Default package set compatible with most boards.
|
||||
endef
|
||||
$(eval $(call Profile,Default))
|
||||
8
target/linux/econet/en751221/target.mk
Normal file
8
target/linux/econet/en751221/target.mk
Normal file
@ -0,0 +1,8 @@
|
||||
BOARDNAME:=en751221
|
||||
CPU_TYPE:=24kc
|
||||
KERNELNAME:=vmlinuz.bin
|
||||
|
||||
define Target/Description
|
||||
Build firmware images for EcoNet EN751221 family SoC, including
|
||||
EN7512, EN7513, EN7521, EN7526 and EN7586.
|
||||
endef
|
||||
1439
target/linux/econet/files/drivers/mtd/nand/en75_bmt.c
Normal file
1439
target/linux/econet/files/drivers/mtd/nand/en75_bmt.c
Normal file
File diff suppressed because it is too large
Load Diff
10
target/linux/econet/image/Makefile
Normal file
10
target/linux/econet/image/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/image.mk
|
||||
|
||||
define Target/Description
|
||||
Build firmware images for EcoNet MIPS based boards.
|
||||
endef
|
||||
|
||||
# Devices will come in a later commit.
|
||||
|
||||
$(eval $(call BuildImage))
|
||||
@ -0,0 +1,98 @@
|
||||
From 9773c540441c6ae15aefb49e67142e94369dbbc0 Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Sun, 30 Mar 2025 17:02:58 +0000
|
||||
Subject: [PATCH] dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC
|
||||
|
||||
Document the device tree binding for the interrupt controller in the
|
||||
EcoNet EN751221 MIPS SoC.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
Link: https://lore.kernel.org/all/20250330170306.2584136-3-cjd@cjdns.fr
|
||||
---
|
||||
.../econet,en751221-intc.yaml | 78 +++++++++++++++++++
|
||||
1 file changed, 78 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
|
||||
@@ -0,0 +1,78 @@
|
||||
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/interrupt-controller/econet,en751221-intc.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: EcoNet EN751221 Interrupt Controller
|
||||
+
|
||||
+maintainers:
|
||||
+ - Caleb James DeLisle <cjd@cjdns.fr>
|
||||
+
|
||||
+description:
|
||||
+ The EcoNet EN751221 Interrupt Controller is a simple interrupt controller
|
||||
+ designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can
|
||||
+ be routed to either VPE but not both, so to support per-CPU interrupts, a
|
||||
+ secondary IRQ number is allocated to control masking/unmasking on VPE#1. For
|
||||
+ lack of a better term we call these "shadow interrupts". The assignment of
|
||||
+ shadow interrupts is defined by the SoC integrator when wiring the interrupt
|
||||
+ lines, so they are configurable in the device tree.
|
||||
+
|
||||
+allOf:
|
||||
+ - $ref: /schemas/interrupt-controller.yaml#
|
||||
+
|
||||
+properties:
|
||||
+ compatible:
|
||||
+ const: econet,en751221-intc
|
||||
+
|
||||
+ reg:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+ "#interrupt-cells":
|
||||
+ const: 1
|
||||
+
|
||||
+ interrupt-controller: true
|
||||
+
|
||||
+ interrupts:
|
||||
+ maxItems: 1
|
||||
+ description: Interrupt line connecting this controller to its parent.
|
||||
+
|
||||
+ econet,shadow-interrupts:
|
||||
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
+ description:
|
||||
+ An array of interrupt number pairs where each pair represents a shadow
|
||||
+ interrupt relationship. The first number in each pair is the primary IRQ,
|
||||
+ and the second is its shadow IRQ used for VPE#1 control. For example,
|
||||
+ <8 3> means IRQ 8 is shadowed by IRQ 3, so IRQ 3 cannot be mapped, but
|
||||
+ when VPE#1 requests IRQ 8, it will manipulate the IRQ 3 mask bit.
|
||||
+ minItems: 1
|
||||
+ maxItems: 20
|
||||
+ items:
|
||||
+ items:
|
||||
+ - description: primary per-CPU IRQ
|
||||
+ - description: shadow IRQ number
|
||||
+
|
||||
+required:
|
||||
+ - compatible
|
||||
+ - reg
|
||||
+ - interrupt-controller
|
||||
+ - "#interrupt-cells"
|
||||
+ - interrupts
|
||||
+
|
||||
+additionalProperties: false
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ interrupt-controller@1fb40000 {
|
||||
+ compatible = "econet,en751221-intc";
|
||||
+ reg = <0x1fb40000 0x100>;
|
||||
+
|
||||
+ interrupt-controller;
|
||||
+ #interrupt-cells = <1>;
|
||||
+
|
||||
+ interrupt-parent = <&cpuintc>;
|
||||
+ interrupts = <2>;
|
||||
+
|
||||
+ econet,shadow-interrupts = <7 2>, <8 3>, <13 12>, <30 29>;
|
||||
+ };
|
||||
+...
|
||||
@ -0,0 +1,353 @@
|
||||
From 1902a59cf5f9d8b99ecf0cb8f122cb00ef7a3f13 Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Sun, 30 Mar 2025 17:02:59 +0000
|
||||
Subject: [PATCH] irqchip: Add EcoNet EN751221 INTC
|
||||
|
||||
Add a driver for the interrupt controller in the EcoNet EN751221 MIPS SoC.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://lore.kernel.org/all/20250330170306.2584136-4-cjd@cjdns.fr
|
||||
---
|
||||
drivers/irqchip/Kconfig | 5 +
|
||||
drivers/irqchip/Makefile | 1 +
|
||||
drivers/irqchip/irq-econet-en751221.c | 309 ++++++++++++++++++++++++++
|
||||
3 files changed, 315 insertions(+)
|
||||
create mode 100644 drivers/irqchip/irq-econet-en751221.c
|
||||
|
||||
--- a/drivers/irqchip/Kconfig
|
||||
+++ b/drivers/irqchip/Kconfig
|
||||
@@ -147,6 +147,11 @@ config DW_APB_ICTL
|
||||
select GENERIC_IRQ_CHIP
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
|
||||
+config ECONET_EN751221_INTC
|
||||
+ bool
|
||||
+ select GENERIC_IRQ_CHIP
|
||||
+ select IRQ_DOMAIN
|
||||
+
|
||||
config FARADAY_FTINTC010
|
||||
bool
|
||||
select IRQ_DOMAIN
|
||||
--- a/drivers/irqchip/Makefile
|
||||
+++ b/drivers/irqchip/Makefile
|
||||
@@ -10,6 +10,7 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm28
|
||||
obj-$(CONFIG_ARCH_ACTIONS) += irq-owl-sirq.o
|
||||
obj-$(CONFIG_DAVINCI_CP_INTC) += irq-davinci-cp-intc.o
|
||||
obj-$(CONFIG_EXYNOS_IRQ_COMBINER) += exynos-combiner.o
|
||||
+obj-$(CONFIG_ECONET_EN751221_INTC) += irq-econet-en751221.o
|
||||
obj-$(CONFIG_FARADAY_FTINTC010) += irq-ftintc010.o
|
||||
obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o
|
||||
obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/irqchip/irq-econet-en751221.c
|
||||
@@ -0,0 +1,309 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+/*
|
||||
+ * EN751221 Interrupt Controller Driver.
|
||||
+ *
|
||||
+ * The EcoNet EN751221 Interrupt Controller is a simple interrupt controller
|
||||
+ * designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can
|
||||
+ * be routed to either VPE but not both, so to support per-CPU interrupts, a
|
||||
+ * secondary IRQ number is allocated to control masking/unmasking on VPE#1. In
|
||||
+ * this driver, these are called "shadow interrupts". The assignment of shadow
|
||||
+ * interrupts is defined by the SoC integrator when wiring the interrupt lines,
|
||||
+ * so they are configurable in the device tree.
|
||||
+ *
|
||||
+ * If an interrupt (say 30) needs per-CPU capability, the SoC integrator
|
||||
+ * allocates another IRQ number (say 29) to be its shadow. The device tree
|
||||
+ * reflects this by adding the pair <30 29> to the "econet,shadow-interrupts"
|
||||
+ * property.
|
||||
+ *
|
||||
+ * When VPE#1 requests IRQ 30, the driver manipulates the mask bit for IRQ 29,
|
||||
+ * telling the hardware to mask VPE#1's view of IRQ 30.
|
||||
+ *
|
||||
+ * Copyright (C) 2025 Caleb James DeLisle <cjd@cjdns.fr>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/cleanup.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/irqdomain.h>
|
||||
+#include <linux/irqchip.h>
|
||||
+#include <linux/irqchip/chained_irq.h>
|
||||
+
|
||||
+#define IRQ_COUNT 40
|
||||
+
|
||||
+#define NOT_PERCPU 0xff
|
||||
+#define IS_SHADOW 0xfe
|
||||
+
|
||||
+#define REG_MASK0 0x04
|
||||
+#define REG_MASK1 0x50
|
||||
+#define REG_PENDING0 0x08
|
||||
+#define REG_PENDING1 0x54
|
||||
+
|
||||
+/**
|
||||
+ * @membase: Base address of the interrupt controller registers
|
||||
+ * @interrupt_shadows: Array of all interrupts, for each value,
|
||||
+ * - NOT_PERCPU: This interrupt is not per-cpu, so it has no shadow
|
||||
+ * - IS_SHADOW: This interrupt is a shadow of another per-cpu interrupt
|
||||
+ * - else: This is a per-cpu interrupt whose shadow is the value
|
||||
+ */
|
||||
+static struct {
|
||||
+ void __iomem *membase;
|
||||
+ u8 interrupt_shadows[IRQ_COUNT];
|
||||
+} econet_intc __ro_after_init;
|
||||
+
|
||||
+static DEFINE_RAW_SPINLOCK(irq_lock);
|
||||
+
|
||||
+/* IRQs must be disabled */
|
||||
+static void econet_wreg(u32 reg, u32 val, u32 mask)
|
||||
+{
|
||||
+ u32 v;
|
||||
+
|
||||
+ guard(raw_spinlock)(&irq_lock);
|
||||
+
|
||||
+ v = ioread32(econet_intc.membase + reg);
|
||||
+ v &= ~mask;
|
||||
+ v |= val & mask;
|
||||
+ iowrite32(v, econet_intc.membase + reg);
|
||||
+}
|
||||
+
|
||||
+/* IRQs must be disabled */
|
||||
+static void econet_chmask(u32 hwirq, bool unmask)
|
||||
+{
|
||||
+ u32 reg, mask;
|
||||
+ u8 shadow;
|
||||
+
|
||||
+ /*
|
||||
+ * If the IRQ is a shadow, it should never be manipulated directly.
|
||||
+ * It should only be masked/unmasked as a result of the "real" per-cpu
|
||||
+ * irq being manipulated by a thread running on VPE#1.
|
||||
+ * If it is per-cpu (has a shadow), and we're on VPE#1, the shadow is what we mask.
|
||||
+ * This is single processor only, so smp_processor_id() never exceeds 1.
|
||||
+ */
|
||||
+ shadow = econet_intc.interrupt_shadows[hwirq];
|
||||
+ if (WARN_ON_ONCE(shadow == IS_SHADOW))
|
||||
+ return;
|
||||
+ else if (shadow != NOT_PERCPU && smp_processor_id() == 1)
|
||||
+ hwirq = shadow;
|
||||
+
|
||||
+ if (hwirq >= 32) {
|
||||
+ reg = REG_MASK1;
|
||||
+ mask = BIT(hwirq - 32);
|
||||
+ } else {
|
||||
+ reg = REG_MASK0;
|
||||
+ mask = BIT(hwirq);
|
||||
+ }
|
||||
+
|
||||
+ econet_wreg(reg, unmask ? mask : 0, mask);
|
||||
+}
|
||||
+
|
||||
+/* IRQs must be disabled */
|
||||
+static void econet_intc_mask(struct irq_data *d)
|
||||
+{
|
||||
+ econet_chmask(d->hwirq, false);
|
||||
+}
|
||||
+
|
||||
+/* IRQs must be disabled */
|
||||
+static void econet_intc_unmask(struct irq_data *d)
|
||||
+{
|
||||
+ econet_chmask(d->hwirq, true);
|
||||
+}
|
||||
+
|
||||
+static void econet_mask_all(void)
|
||||
+{
|
||||
+ /* IRQs are generally disabled during init, but guarding here makes it non-obligatory. */
|
||||
+ guard(irqsave)();
|
||||
+ econet_wreg(REG_MASK0, 0, ~0);
|
||||
+ econet_wreg(REG_MASK1, 0, ~0);
|
||||
+}
|
||||
+
|
||||
+static void econet_intc_handle_pending(struct irq_domain *d, u32 pending, u32 offset)
|
||||
+{
|
||||
+ int hwirq;
|
||||
+
|
||||
+ while (pending) {
|
||||
+ hwirq = fls(pending) - 1;
|
||||
+ generic_handle_domain_irq(d, hwirq + offset);
|
||||
+ pending &= ~BIT(hwirq);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void econet_intc_from_parent(struct irq_desc *desc)
|
||||
+{
|
||||
+ struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
+ struct irq_domain *domain;
|
||||
+ u32 pending0, pending1;
|
||||
+
|
||||
+ chained_irq_enter(chip, desc);
|
||||
+
|
||||
+ pending0 = ioread32(econet_intc.membase + REG_PENDING0);
|
||||
+ pending1 = ioread32(econet_intc.membase + REG_PENDING1);
|
||||
+
|
||||
+ if (unlikely(!(pending0 | pending1))) {
|
||||
+ spurious_interrupt();
|
||||
+ } else {
|
||||
+ domain = irq_desc_get_handler_data(desc);
|
||||
+ econet_intc_handle_pending(domain, pending0, 0);
|
||||
+ econet_intc_handle_pending(domain, pending1, 32);
|
||||
+ }
|
||||
+
|
||||
+ chained_irq_exit(chip, desc);
|
||||
+}
|
||||
+
|
||||
+static const struct irq_chip econet_irq_chip;
|
||||
+
|
||||
+static int econet_intc_map(struct irq_domain *d, u32 irq, irq_hw_number_t hwirq)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ if (hwirq >= IRQ_COUNT) {
|
||||
+ pr_err("%s: hwirq %lu out of range\n", __func__, hwirq);
|
||||
+ return -EINVAL;
|
||||
+ } else if (econet_intc.interrupt_shadows[hwirq] == IS_SHADOW) {
|
||||
+ pr_err("%s: can't map hwirq %lu, it is a shadow interrupt\n", __func__, hwirq);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (econet_intc.interrupt_shadows[hwirq] == NOT_PERCPU) {
|
||||
+ irq_set_chip_and_handler(irq, &econet_irq_chip, handle_level_irq);
|
||||
+ } else {
|
||||
+ irq_set_chip_and_handler(irq, &econet_irq_chip, handle_percpu_devid_irq);
|
||||
+ ret = irq_set_percpu_devid(irq);
|
||||
+ if (ret)
|
||||
+ pr_warn("%s: Failed irq_set_percpu_devid for %u: %d\n", d->name, irq, ret);
|
||||
+ }
|
||||
+
|
||||
+ irq_set_chip_data(irq, NULL);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct irq_chip econet_irq_chip = {
|
||||
+ .name = "en751221-intc",
|
||||
+ .irq_unmask = econet_intc_unmask,
|
||||
+ .irq_mask = econet_intc_mask,
|
||||
+ .irq_mask_ack = econet_intc_mask,
|
||||
+};
|
||||
+
|
||||
+static const struct irq_domain_ops econet_domain_ops = {
|
||||
+ .xlate = irq_domain_xlate_onecell,
|
||||
+ .map = econet_intc_map
|
||||
+};
|
||||
+
|
||||
+static int __init get_shadow_interrupts(struct device_node *node)
|
||||
+{
|
||||
+ const char *field = "econet,shadow-interrupts";
|
||||
+ int num_shadows;
|
||||
+
|
||||
+ num_shadows = of_property_count_u32_elems(node, field);
|
||||
+
|
||||
+ memset(econet_intc.interrupt_shadows, NOT_PERCPU,
|
||||
+ sizeof(econet_intc.interrupt_shadows));
|
||||
+
|
||||
+ if (num_shadows <= 0) {
|
||||
+ return 0;
|
||||
+ } else if (num_shadows % 2) {
|
||||
+ pr_err("%pOF: %s count is odd, ignoring\n", node, field);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ u32 *shadows __free(kfree) = kmalloc_array(num_shadows, sizeof(u32), GFP_KERNEL);
|
||||
+ if (!shadows)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ if (of_property_read_u32_array(node, field, shadows, num_shadows)) {
|
||||
+ pr_err("%pOF: Failed to read %s\n", node, field);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ for (int i = 0; i < num_shadows; i += 2) {
|
||||
+ u32 shadow = shadows[i + 1];
|
||||
+ u32 target = shadows[i];
|
||||
+
|
||||
+ if (shadow > IRQ_COUNT) {
|
||||
+ pr_err("%pOF: %s[%d] shadow(%d) out of range\n",
|
||||
+ node, field, i + 1, shadow);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (target >= IRQ_COUNT) {
|
||||
+ pr_err("%pOF: %s[%d] target(%d) out of range\n", node, field, i, target);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (econet_intc.interrupt_shadows[target] != NOT_PERCPU) {
|
||||
+ pr_err("%pOF: %s[%d] target(%d) already has a shadow\n",
|
||||
+ node, field, i, target);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (econet_intc.interrupt_shadows[shadow] != NOT_PERCPU) {
|
||||
+ pr_err("%pOF: %s[%d] shadow(%d) already has a target\n",
|
||||
+ node, field, i + 1, shadow);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ econet_intc.interrupt_shadows[target] = shadow;
|
||||
+ econet_intc.interrupt_shadows[shadow] = IS_SHADOW;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int __init econet_intc_of_init(struct device_node *node, struct device_node *parent)
|
||||
+{
|
||||
+ struct irq_domain *domain;
|
||||
+ struct resource res;
|
||||
+ int ret, irq;
|
||||
+
|
||||
+ ret = get_shadow_interrupts(node);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ irq = irq_of_parse_and_map(node, 0);
|
||||
+ if (!irq) {
|
||||
+ pr_err("%pOF: DT: Failed to get IRQ from 'interrupts'\n", node);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (of_address_to_resource(node, 0, &res)) {
|
||||
+ pr_err("%pOF: DT: Failed to get 'reg'\n", node);
|
||||
+ ret = -EINVAL;
|
||||
+ goto err_dispose_mapping;
|
||||
+ }
|
||||
+
|
||||
+ if (!request_mem_region(res.start, resource_size(&res), res.name)) {
|
||||
+ pr_err("%pOF: Failed to request memory\n", node);
|
||||
+ ret = -EBUSY;
|
||||
+ goto err_dispose_mapping;
|
||||
+ }
|
||||
+
|
||||
+ econet_intc.membase = ioremap(res.start, resource_size(&res));
|
||||
+ if (!econet_intc.membase) {
|
||||
+ pr_err("%pOF: Failed to remap membase\n", node);
|
||||
+ ret = -ENOMEM;
|
||||
+ goto err_release;
|
||||
+ }
|
||||
+
|
||||
+ econet_mask_all();
|
||||
+
|
||||
+ domain = irq_domain_add_linear(node, IRQ_COUNT, &econet_domain_ops, NULL);
|
||||
+ if (!domain) {
|
||||
+ pr_err("%pOF: Failed to add irqdomain\n", node);
|
||||
+ ret = -ENOMEM;
|
||||
+ goto err_unmap;
|
||||
+ }
|
||||
+
|
||||
+ irq_set_chained_handler_and_data(irq, econet_intc_from_parent, domain);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_unmap:
|
||||
+ iounmap(econet_intc.membase);
|
||||
+err_release:
|
||||
+ release_mem_region(res.start, resource_size(&res));
|
||||
+err_dispose_mapping:
|
||||
+ irq_dispose_mapping(irq);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+IRQCHIP_DECLARE(econet_en751221_intc, "econet,en751221-intc", econet_intc_of_init);
|
||||
@ -0,0 +1,26 @@
|
||||
From 9e0dd98654a528735d2b363d0dc73f7904108652 Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Sun, 30 Mar 2025 17:02:57 +0000
|
||||
Subject: [PATCH] dt-bindings: vendor-prefixes: Add EcoNet
|
||||
|
||||
Add the "econet" vendor prefix for SoC maker
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
Link: https://lore.kernel.org/r/20250330170306.2584136-2-cjd@cjdns.fr
|
||||
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
|
||||
---
|
||||
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
|
||||
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
|
||||
@@ -420,6 +420,8 @@ patternProperties:
|
||||
description: EBV Elektronik
|
||||
"^eckelmann,.*":
|
||||
description: Eckelmann AG
|
||||
+ "^econet,.*":
|
||||
+ description: EcoNet (HK) Limited
|
||||
"^edgeble,.*":
|
||||
description: Edgeble AI Technologies Pvt. Ltd.
|
||||
"^edimax,.*":
|
||||
@ -0,0 +1,100 @@
|
||||
From 30fddbd5325459102e448c9a26a1bc15ef563381 Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Wed, 7 May 2025 13:44:54 +0000
|
||||
Subject: [PATCH] dt-bindings: timer: Add EcoNet EN751221 "HPT" CPU Timer
|
||||
|
||||
Add device tree bindings for the so-called high-precision timer (HPT)
|
||||
in the EcoNet EN751221 SoC.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
Link: https://lore.kernel.org/r/20250507134500.390547-2-cjd@cjdns.fr
|
||||
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
---
|
||||
.../bindings/timer/econet,en751221-timer.yaml | 80 +++++++++++++++++++
|
||||
1 file changed, 80 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
|
||||
@@ -0,0 +1,80 @@
|
||||
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/timer/econet,en751221-timer.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: EcoNet EN751221 High Precision Timer (HPT)
|
||||
+
|
||||
+maintainers:
|
||||
+ - Caleb James DeLisle <cjd@cjdns.fr>
|
||||
+
|
||||
+description:
|
||||
+ The EcoNet High Precision Timer (HPT) is a timer peripheral found in various
|
||||
+ EcoNet SoCs, including the EN751221 and EN751627 families. It provides per-VPE
|
||||
+ count/compare registers and a per-CPU control register, with a single interrupt
|
||||
+ line using a percpu-devid interrupt mechanism.
|
||||
+
|
||||
+properties:
|
||||
+ compatible:
|
||||
+ oneOf:
|
||||
+ - const: econet,en751221-timer
|
||||
+ - items:
|
||||
+ - const: econet,en751627-timer
|
||||
+ - const: econet,en751221-timer
|
||||
+
|
||||
+ reg:
|
||||
+ minItems: 1
|
||||
+ maxItems: 2
|
||||
+
|
||||
+ interrupts:
|
||||
+ maxItems: 1
|
||||
+ description: A percpu-devid timer interrupt shared across CPUs.
|
||||
+
|
||||
+ clocks:
|
||||
+ maxItems: 1
|
||||
+
|
||||
+required:
|
||||
+ - compatible
|
||||
+ - reg
|
||||
+ - interrupts
|
||||
+ - clocks
|
||||
+
|
||||
+allOf:
|
||||
+ - if:
|
||||
+ properties:
|
||||
+ compatible:
|
||||
+ contains:
|
||||
+ const: econet,en751627-timer
|
||||
+ then:
|
||||
+ properties:
|
||||
+ reg:
|
||||
+ items:
|
||||
+ - description: VPE timers 0 and 1
|
||||
+ - description: VPE timers 2 and 3
|
||||
+ else:
|
||||
+ properties:
|
||||
+ reg:
|
||||
+ items:
|
||||
+ - description: VPE timers 0 and 1
|
||||
+
|
||||
+additionalProperties: false
|
||||
+
|
||||
+examples:
|
||||
+ - |
|
||||
+ timer@1fbf0400 {
|
||||
+ compatible = "econet,en751627-timer", "econet,en751221-timer";
|
||||
+ reg = <0x1fbf0400 0x100>, <0x1fbe0000 0x100>;
|
||||
+ interrupt-parent = <&intc>;
|
||||
+ interrupts = <30>;
|
||||
+ clocks = <&hpt_clock>;
|
||||
+ };
|
||||
+ - |
|
||||
+ timer@1fbf0400 {
|
||||
+ compatible = "econet,en751221-timer";
|
||||
+ reg = <0x1fbe0400 0x100>;
|
||||
+ interrupt-parent = <&intc>;
|
||||
+ interrupts = <30>;
|
||||
+ clocks = <&hpt_clock>;
|
||||
+ };
|
||||
+...
|
||||
@ -0,0 +1,276 @@
|
||||
From 3b4c33ac87d0d11308f4445ecec2a124e2e77724 Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Wed, 7 May 2025 13:44:55 +0000
|
||||
Subject: [PATCH] clocksource/drivers: Add EcoNet Timer HPT driver
|
||||
|
||||
Introduce a clocksource driver for the so-called high-precision timer (HPT)
|
||||
in the EcoNet EN751221 and EN751627 MIPS SoCs.
|
||||
|
||||
It's a 32 bit upward-counting one-shot timer which relies on the crystal so it
|
||||
is unaffected by CPU power mode. On MIPS 34K devices (single core) there is
|
||||
one timer, and on 1004K devices (dual core) there are two.
|
||||
|
||||
Each timer has two sets of count/compare registers so that there is one for
|
||||
each of the VPEs on the core. Because each core has 2 VPEs, register selection
|
||||
takes the CPU number / 2 for the timer corrisponding to the core, then CPU
|
||||
number % 2 for the register corrisponding to the VPE.
|
||||
|
||||
These timers use a percpu-devid IRQ to route interrupts to the VPE which set
|
||||
the event.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Link: https://lore.kernel.org/r/20250507134500.390547-3-cjd@cjdns.fr
|
||||
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
---
|
||||
drivers/clocksource/Kconfig | 8 +
|
||||
drivers/clocksource/Makefile | 1 +
|
||||
drivers/clocksource/timer-econet-en751221.c | 216 ++++++++++++++++++++
|
||||
3 files changed, 225 insertions(+)
|
||||
create mode 100644 drivers/clocksource/timer-econet-en751221.c
|
||||
|
||||
--- a/drivers/clocksource/Kconfig
|
||||
+++ b/drivers/clocksource/Kconfig
|
||||
@@ -73,6 +73,14 @@ config DW_APB_TIMER_OF
|
||||
select DW_APB_TIMER
|
||||
select TIMER_OF
|
||||
|
||||
+config ECONET_EN751221_TIMER
|
||||
+ bool "EcoNet EN751221 High Precision Timer" if COMPILE_TEST
|
||||
+ depends on HAS_IOMEM
|
||||
+ select CLKSRC_MMIO
|
||||
+ select TIMER_OF
|
||||
+ help
|
||||
+ Support for CPU timer found on EcoNet MIPS based SoCs.
|
||||
+
|
||||
config FTTMR010_TIMER
|
||||
bool "Faraday Technology timer driver" if COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
--- a/drivers/clocksource/Makefile
|
||||
+++ b/drivers/clocksource/Makefile
|
||||
@@ -17,6 +17,7 @@ obj-$(CONFIG_CLKBLD_I8253) += i8253.o
|
||||
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
|
||||
obj-$(CONFIG_DAVINCI_TIMER) += timer-davinci.o
|
||||
obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o
|
||||
+obj-$(CONFIG_ECONET_EN751221_TIMER) += timer-econet-en751221.o
|
||||
obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm.o
|
||||
obj-$(CONFIG_OMAP_DM_SYSTIMER) += timer-ti-dm-systimer.o
|
||||
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/clocksource/timer-econet-en751221.c
|
||||
@@ -0,0 +1,216 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+/*
|
||||
+ * Timer present on EcoNet EN75xx MIPS based SoCs.
|
||||
+ *
|
||||
+ * Copyright (C) 2025 by Caleb James DeLisle <cjd@cjdns.fr>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/cpumask.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/clockchips.h>
|
||||
+#include <linux/sched_clock.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/cpuhotplug.h>
|
||||
+#include <linux/clk.h>
|
||||
+
|
||||
+#define ECONET_BITS 32
|
||||
+#define ECONET_MIN_DELTA 0x00001000
|
||||
+#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)
|
||||
+
|
||||
+static struct {
|
||||
+ void __iomem *membase[ECONET_NUM_BLOCKS];
|
||||
+ u32 freq_hz;
|
||||
+} econet_timer __ro_after_init;
|
||||
+
|
||||
+static DEFINE_PER_CPU(struct clock_event_device, econet_timer_pcpu);
|
||||
+
|
||||
+/* Each memory block has 2 timers, the order of registers is:
|
||||
+ * CTL, CMR0, CNT0, CMR1, CNT1
|
||||
+ */
|
||||
+static inline void __iomem *reg_ctl(u32 timer_n)
|
||||
+{
|
||||
+ return econet_timer.membase[timer_n >> 1];
|
||||
+}
|
||||
+
|
||||
+static inline void __iomem *reg_compare(u32 timer_n)
|
||||
+{
|
||||
+ return econet_timer.membase[timer_n >> 1] + (timer_n & 1) * 0x08 + 0x04;
|
||||
+}
|
||||
+
|
||||
+static inline void __iomem *reg_count(u32 timer_n)
|
||||
+{
|
||||
+ return econet_timer.membase[timer_n >> 1] + (timer_n & 1) * 0x08 + 0x08;
|
||||
+}
|
||||
+
|
||||
+static inline u32 ctl_bit_enabled(u32 timer_n)
|
||||
+{
|
||||
+ return 1U << (timer_n & 1);
|
||||
+}
|
||||
+
|
||||
+static inline u32 ctl_bit_pending(u32 timer_n)
|
||||
+{
|
||||
+ return 1U << ((timer_n & 1) + 16);
|
||||
+}
|
||||
+
|
||||
+static bool cevt_is_pending(int cpu_id)
|
||||
+{
|
||||
+ return ioread32(reg_ctl(cpu_id)) & ctl_bit_pending(cpu_id);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t cevt_interrupt(int irq, void *dev_id)
|
||||
+{
|
||||
+ struct clock_event_device *dev = this_cpu_ptr(&econet_timer_pcpu);
|
||||
+ int cpu = cpumask_first(dev->cpumask);
|
||||
+
|
||||
+ /* Each VPE has its own events,
|
||||
+ * so this will only happen on spurious interrupt.
|
||||
+ */
|
||||
+ if (!cevt_is_pending(cpu))
|
||||
+ return IRQ_NONE;
|
||||
+
|
||||
+ iowrite32(ioread32(reg_count(cpu)), reg_compare(cpu));
|
||||
+ dev->event_handler(dev);
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static int cevt_set_next_event(ulong delta, struct clock_event_device *dev)
|
||||
+{
|
||||
+ u32 next;
|
||||
+ int cpu;
|
||||
+
|
||||
+ cpu = cpumask_first(dev->cpumask);
|
||||
+ next = ioread32(reg_count(cpu)) + delta;
|
||||
+ iowrite32(next, reg_compare(cpu));
|
||||
+
|
||||
+ if ((s32)(next - ioread32(reg_count(cpu))) < ECONET_MIN_DELTA / 2)
|
||||
+ return -ETIME;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int cevt_init_cpu(uint cpu)
|
||||
+{
|
||||
+ struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, cpu);
|
||||
+ u32 reg;
|
||||
+
|
||||
+ 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);
|
||||
+
|
||||
+ /* Do this last because it synchronously configures the timer */
|
||||
+ clockevents_config_and_register(cd, econet_timer.freq_hz,
|
||||
+ ECONET_MIN_DELTA, ECONET_MAX_DELTA);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static u64 notrace sched_clock_read(void)
|
||||
+{
|
||||
+ /* Always read from clock zero no matter the CPU */
|
||||
+ return (u64)ioread32(reg_count(0));
|
||||
+}
|
||||
+
|
||||
+/* Init */
|
||||
+
|
||||
+static void __init cevt_dev_init(uint cpu)
|
||||
+{
|
||||
+ iowrite32(0, reg_count(cpu));
|
||||
+ iowrite32(U32_MAX, reg_compare(cpu));
|
||||
+}
|
||||
+
|
||||
+static int __init cevt_init(struct device_node *np)
|
||||
+{
|
||||
+ int i, irq, ret;
|
||||
+
|
||||
+ irq = irq_of_parse_and_map(np, 0);
|
||||
+ if (irq <= 0) {
|
||||
+ pr_err("%pOFn: irq_of_parse_and_map failed", np);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ 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;
|
||||
+ }
|
||||
+
|
||||
+ 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_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);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int __init timer_init(struct device_node *np)
|
||||
+{
|
||||
+ int num_blocks = DIV_ROUND_UP(num_possible_cpus(), 2);
|
||||
+ struct clk *clk;
|
||||
+ int ret;
|
||||
+
|
||||
+ clk = of_clk_get(np, 0);
|
||||
+ if (IS_ERR(clk)) {
|
||||
+ pr_err("%pOFn: Failed to get CPU clock from DT %ld\n", np, PTR_ERR(clk));
|
||||
+ return PTR_ERR(clk);
|
||||
+ }
|
||||
+
|
||||
+ econet_timer.freq_hz = clk_get_rate(clk);
|
||||
+
|
||||
+ for (int 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;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* For clocksource purposes always read clock zero, whatever the CPU */
|
||||
+ ret = clocksource_mmio_init(reg_count(0), np->name,
|
||||
+ econet_timer.freq_hz, 301, ECONET_BITS,
|
||||
+ clocksource_mmio_readl_up);
|
||||
+ if (ret) {
|
||||
+ pr_err("%pOFn: clocksource_mmio_init failed: %d", np, ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = cevt_init(np);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ sched_clock_register(sched_clock_read, ECONET_BITS,
|
||||
+ econet_timer.freq_hz);
|
||||
+
|
||||
+ pr_info("%pOFn: using %u.%03u MHz high precision timer\n", np,
|
||||
+ econet_timer.freq_hz / 1000000,
|
||||
+ (econet_timer.freq_hz / 1000) % 1000);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+TIMER_OF_DECLARE(econet_timer_hpt, "econet,en751221-timer", timer_init);
|
||||
@ -0,0 +1,44 @@
|
||||
From be8b4173719a61fdd8379e86895d855775cf5f91 Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Wed, 7 May 2025 13:44:56 +0000
|
||||
Subject: [PATCH] dt-bindings: mips: Add EcoNet platform binding
|
||||
|
||||
Document the top-level device tree binding for EcoNet MIPS-based SoCs.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
|
||||
---
|
||||
.../devicetree/bindings/mips/econet.yaml | 26 +++++++++++++++++++
|
||||
1 file changed, 26 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/mips/econet.yaml
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/mips/econet.yaml
|
||||
@@ -0,0 +1,26 @@
|
||||
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
+%YAML 1.2
|
||||
+---
|
||||
+$id: http://devicetree.org/schemas/mips/econet.yaml#
|
||||
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
+
|
||||
+title: EcoNet MIPS SoCs
|
||||
+
|
||||
+maintainers:
|
||||
+ - Caleb James DeLisle <cjd@cjdns.fr>
|
||||
+
|
||||
+properties:
|
||||
+ $nodename:
|
||||
+ const: '/'
|
||||
+
|
||||
+ compatible:
|
||||
+ oneOf:
|
||||
+ - description: Boards with EcoNet EN751221 family SoC
|
||||
+ items:
|
||||
+ - enum:
|
||||
+ - smartfiber,xp8421-b
|
||||
+ - const: econet,en751221
|
||||
+
|
||||
+additionalProperties: true
|
||||
+
|
||||
+...
|
||||
@ -0,0 +1,222 @@
|
||||
From 35fb26f94dfa1b291086b84b2421f957214824d1 Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Wed, 7 May 2025 13:44:57 +0000
|
||||
Subject: [PATCH] mips: Add EcoNet MIPS platform support
|
||||
|
||||
Add platform support for EcoNet MIPS SoCs.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
|
||||
---
|
||||
arch/mips/Kbuild.platforms | 1 +
|
||||
arch/mips/Kconfig | 25 +++++++++
|
||||
arch/mips/boot/compressed/uart-16550.c | 5 ++
|
||||
arch/mips/econet/Kconfig | 37 ++++++++++++
|
||||
arch/mips/econet/Makefile | 2 +
|
||||
arch/mips/econet/Platform | 5 ++
|
||||
arch/mips/econet/init.c | 78 ++++++++++++++++++++++++++
|
||||
7 files changed, 153 insertions(+)
|
||||
create mode 100644 arch/mips/econet/Kconfig
|
||||
create mode 100644 arch/mips/econet/Makefile
|
||||
create mode 100644 arch/mips/econet/Platform
|
||||
create mode 100644 arch/mips/econet/init.c
|
||||
|
||||
--- a/arch/mips/Kbuild.platforms
|
||||
+++ b/arch/mips/Kbuild.platforms
|
||||
@@ -11,6 +11,7 @@ platform-$(CONFIG_CAVIUM_OCTEON_SOC) +=
|
||||
platform-$(CONFIG_EYEQ) += mobileye/
|
||||
platform-$(CONFIG_MIPS_COBALT) += cobalt/
|
||||
platform-$(CONFIG_MACH_DECSTATION) += dec/
|
||||
+platform-$(CONFIG_ECONET) += econet/
|
||||
platform-$(CONFIG_MIPS_GENERIC) += generic/
|
||||
platform-$(CONFIG_MACH_JAZZ) += jazz/
|
||||
platform-$(CONFIG_LANTIQ) += lantiq/
|
||||
--- a/arch/mips/Kconfig
|
||||
+++ b/arch/mips/Kconfig
|
||||
@@ -388,6 +388,30 @@ config MACH_DECSTATION
|
||||
|
||||
otherwise choose R3000.
|
||||
|
||||
+config ECONET
|
||||
+ bool "EcoNet MIPS family"
|
||||
+ select BOOT_RAW
|
||||
+ select CPU_BIG_ENDIAN
|
||||
+ select DEBUG_ZBOOT
|
||||
+ select EARLY_PRINTK_8250
|
||||
+ select ECONET_EN751221_TIMER
|
||||
+ select SERIAL_OF_PLATFORM
|
||||
+ select SYS_SUPPORTS_BIG_ENDIAN
|
||||
+ select SYS_HAS_CPU_MIPS32_R1
|
||||
+ select SYS_HAS_CPU_MIPS32_R2
|
||||
+ select SYS_HAS_EARLY_PRINTK
|
||||
+ select SYS_SUPPORTS_32BIT_KERNEL
|
||||
+ select SYS_SUPPORTS_MIPS16
|
||||
+ select SYS_SUPPORTS_ZBOOT_UART16550
|
||||
+ select USE_GENERIC_EARLY_PRINTK_8250
|
||||
+ select USE_OF
|
||||
+ help
|
||||
+ EcoNet EN75xx MIPS devices are big endian MIPS machines used
|
||||
+ in XPON (fiber) and DSL applications. They have SPI, PCI, USB,
|
||||
+ GPIO, and Ethernet, with optional XPON, DSL, and VoIP DSP cores.
|
||||
+ Don't confuse these with the Airoha ARM devices sometimes referred
|
||||
+ to as "EcoNet", this family is for MIPS based devices only.
|
||||
+
|
||||
config MACH_JAZZ
|
||||
bool "Jazz family of machines"
|
||||
select ARC_MEMORY
|
||||
@@ -1017,6 +1041,7 @@ source "arch/mips/ath79/Kconfig"
|
||||
source "arch/mips/bcm47xx/Kconfig"
|
||||
source "arch/mips/bcm63xx/Kconfig"
|
||||
source "arch/mips/bmips/Kconfig"
|
||||
+source "arch/mips/econet/Kconfig"
|
||||
source "arch/mips/generic/Kconfig"
|
||||
source "arch/mips/ingenic/Kconfig"
|
||||
source "arch/mips/jazz/Kconfig"
|
||||
--- a/arch/mips/boot/compressed/uart-16550.c
|
||||
+++ b/arch/mips/boot/compressed/uart-16550.c
|
||||
@@ -20,6 +20,11 @@
|
||||
#define PORT(offset) (CKSEG1ADDR(INGENIC_UART_BASE_ADDR) + (4 * offset))
|
||||
#endif
|
||||
|
||||
+#ifdef CONFIG_ECONET
|
||||
+#define EN75_UART_BASE 0x1fbf0003
|
||||
+#define PORT(offset) (CKSEG1ADDR(EN75_UART_BASE) + (4 * (offset)))
|
||||
+#endif
|
||||
+
|
||||
#ifndef IOTYPE
|
||||
#define IOTYPE char
|
||||
#endif
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/econet/Kconfig
|
||||
@@ -0,0 +1,37 @@
|
||||
+# SPDX-License-Identifier: GPL-2.0
|
||||
+if ECONET
|
||||
+
|
||||
+choice
|
||||
+ prompt "EcoNet SoC selection"
|
||||
+ default SOC_ECONET_EN751221
|
||||
+ help
|
||||
+ Select EcoNet MIPS SoC type. Individual SoCs within a family are
|
||||
+ very similar, so is it enough to select the right family, and
|
||||
+ then customize to the specific SoC using the device tree only.
|
||||
+
|
||||
+ config SOC_ECONET_EN751221
|
||||
+ bool "EN751221 family"
|
||||
+ select COMMON_CLK
|
||||
+ select ECONET_EN751221_INTC
|
||||
+ select IRQ_MIPS_CPU
|
||||
+ select SMP
|
||||
+ select SMP_UP
|
||||
+ select SYS_SUPPORTS_SMP
|
||||
+ help
|
||||
+ The EN751221 family includes EN7512, RN7513, EN7521, EN7526.
|
||||
+ 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.
|
||||
+endchoice
|
||||
+
|
||||
+choice
|
||||
+ prompt "Devicetree selection"
|
||||
+ default DTB_ECONET_NONE
|
||||
+ help
|
||||
+ Select the devicetree.
|
||||
+
|
||||
+ config DTB_ECONET_NONE
|
||||
+ bool "None"
|
||||
+endchoice
|
||||
+
|
||||
+endif
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/econet/Makefile
|
||||
@@ -0,0 +1,2 @@
|
||||
+
|
||||
+obj-y := init.o
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/econet/Platform
|
||||
@@ -0,0 +1,5 @@
|
||||
+# To address a 7.2MB kernel size limit in the EcoNet SDK bootloader,
|
||||
+# we put the load address well above where the bootloader loads and then use
|
||||
+# zboot. So please set CONFIG_ZBOOT_LOAD_ADDRESS to the address where your
|
||||
+# bootloader actually places the kernel.
|
||||
+load-$(CONFIG_ECONET) += 0xffffffff81000000
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/econet/init.c
|
||||
@@ -0,0 +1,78 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+/*
|
||||
+ * EcoNet setup code
|
||||
+ *
|
||||
+ * Copyright (C) 2025 Caleb James DeLisle <cjd@cjdns.fr>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/of_clk.h>
|
||||
+#include <linux/irqchip.h>
|
||||
+
|
||||
+#include <asm/addrspace.h>
|
||||
+#include <asm/io.h>
|
||||
+#include <asm/bootinfo.h>
|
||||
+#include <asm/time.h>
|
||||
+#include <asm/prom.h>
|
||||
+#include <asm/smp-ops.h>
|
||||
+#include <asm/reboot.h>
|
||||
+
|
||||
+#define CR_AHB_RSTCR ((void __iomem *)CKSEG1ADDR(0x1fb00040))
|
||||
+#define RESET BIT(31)
|
||||
+
|
||||
+#define UART_BASE CKSEG1ADDR(0x1fbf0003)
|
||||
+#define UART_REG_SHIFT 2
|
||||
+
|
||||
+static void hw_reset(char *command)
|
||||
+{
|
||||
+ iowrite32(RESET, CR_AHB_RSTCR);
|
||||
+}
|
||||
+
|
||||
+/* 1. Bring up early printk. */
|
||||
+void __init prom_init(void)
|
||||
+{
|
||||
+ setup_8250_early_printk_port(UART_BASE, UART_REG_SHIFT, 0);
|
||||
+ _machine_restart = hw_reset;
|
||||
+}
|
||||
+
|
||||
+/* 2. Parse the DT and find memory */
|
||||
+void __init plat_mem_setup(void)
|
||||
+{
|
||||
+ void *dtb;
|
||||
+
|
||||
+ set_io_port_base(KSEG1);
|
||||
+
|
||||
+ dtb = get_fdt();
|
||||
+ if (!dtb)
|
||||
+ panic("no dtb found");
|
||||
+
|
||||
+ __dt_setup_arch(dtb);
|
||||
+
|
||||
+ early_init_dt_scan_memory();
|
||||
+}
|
||||
+
|
||||
+/* 3. Overload __weak device_tree_init(), add SMP_UP ops */
|
||||
+void __init device_tree_init(void)
|
||||
+{
|
||||
+ unflatten_and_copy_device_tree();
|
||||
+
|
||||
+ register_up_smp_ops();
|
||||
+}
|
||||
+
|
||||
+const char *get_system_type(void)
|
||||
+{
|
||||
+ return "EcoNet-EN75xx";
|
||||
+}
|
||||
+
|
||||
+/* 4. Initialize the IRQ subsystem */
|
||||
+void __init arch_init_irq(void)
|
||||
+{
|
||||
+ irqchip_init();
|
||||
+}
|
||||
+
|
||||
+/* 5. Timers */
|
||||
+void __init plat_time_init(void)
|
||||
+{
|
||||
+ of_clk_init(NULL);
|
||||
+ timer_probe();
|
||||
+}
|
||||
@ -0,0 +1,29 @@
|
||||
From abc2d0bc2cb7c1412b8b254c0446f94b3e203c7c Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Wed, 7 May 2025 13:44:58 +0000
|
||||
Subject: [PATCH] dt-bindings: vendor-prefixes: Add SmartFiber
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add "smartfiber" vendor prefix for manufactorer of EcoNet based boards.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
|
||||
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
|
||||
---
|
||||
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
|
||||
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
|
||||
@@ -1368,6 +1368,8 @@ patternProperties:
|
||||
description: SKOV A/S
|
||||
"^skyworks,.*":
|
||||
description: Skyworks Solutions, Inc.
|
||||
+ "^smartfiber,.*":
|
||||
+ description: ShenZhen Smartfiber Technology Co, Ltd.
|
||||
"^smartlabs,.*":
|
||||
description: SmartLabs LLC
|
||||
"^smartrg,.*":
|
||||
@ -0,0 +1,149 @@
|
||||
From 0ec4887009729297f7c10368084e41a8a9fbbd0e Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Wed, 7 May 2025 13:44:59 +0000
|
||||
Subject: [PATCH] mips: dts: Add EcoNet DTS with EN751221 and SmartFiber
|
||||
XP8421-B board
|
||||
|
||||
Add DTS files in support of EcoNet platform, including SmartFiber XP8421-B,
|
||||
a low cost commercially available board based on EN751221.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
|
||||
---
|
||||
arch/mips/boot/dts/Makefile | 1 +
|
||||
arch/mips/boot/dts/econet/Makefile | 2 +
|
||||
arch/mips/boot/dts/econet/en751221.dtsi | 67 +++++++++++++++++++
|
||||
.../econet/en751221_smartfiber_xp8421-b.dts | 19 ++++++
|
||||
arch/mips/econet/Kconfig | 11 +++
|
||||
5 files changed, 100 insertions(+)
|
||||
create mode 100644 arch/mips/boot/dts/econet/Makefile
|
||||
create mode 100644 arch/mips/boot/dts/econet/en751221.dtsi
|
||||
create mode 100644 arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts
|
||||
|
||||
--- a/arch/mips/boot/dts/Makefile
|
||||
+++ b/arch/mips/boot/dts/Makefile
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
subdir-$(CONFIG_BMIPS_GENERIC) += brcm
|
||||
subdir-$(CONFIG_CAVIUM_OCTEON_SOC) += cavium-octeon
|
||||
+subdir-$(CONFIG_ECONET) += econet
|
||||
subdir-$(CONFIG_EYEQ) += mobileye
|
||||
subdir-$(CONFIG_FIT_IMAGE_FDT_MARDUK) += img
|
||||
subdir-$(CONFIG_FIT_IMAGE_FDT_BOSTON) += img
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/boot/dts/econet/Makefile
|
||||
@@ -0,0 +1,2 @@
|
||||
+# SPDX-License-Identifier: GPL-2.0
|
||||
+dtb-$(CONFIG_DTB_ECONET_SMARTFIBER_XP8421_B) += en751221_smartfiber_xp8421-b.dtb
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/boot/dts/econet/en751221.dtsi
|
||||
@@ -0,0 +1,67 @@
|
||||
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
+/dts-v1/;
|
||||
+
|
||||
+/ {
|
||||
+ compatible = "econet,en751221";
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+
|
||||
+ hpt_clock: clock {
|
||||
+ compatible = "fixed-clock";
|
||||
+ #clock-cells = <0>;
|
||||
+ clock-frequency = <200000000>; /* 200 MHz */
|
||||
+ };
|
||||
+
|
||||
+ cpus: cpus {
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+
|
||||
+ cpu@0 {
|
||||
+ device_type = "cpu";
|
||||
+ compatible = "mips,mips24KEc";
|
||||
+ reg = <0>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
||||
+ cpuintc: interrupt-controller {
|
||||
+ compatible = "mti,cpu-interrupt-controller";
|
||||
+ interrupt-controller;
|
||||
+ #address-cells = <0>;
|
||||
+ #interrupt-cells = <1>;
|
||||
+ };
|
||||
+
|
||||
+ intc: interrupt-controller@1fb40000 {
|
||||
+ compatible = "econet,en751221-intc";
|
||||
+ reg = <0x1fb40000 0x100>;
|
||||
+ interrupt-parent = <&cpuintc>;
|
||||
+ interrupts = <2>;
|
||||
+
|
||||
+ interrupt-controller;
|
||||
+ #interrupt-cells = <1>;
|
||||
+ econet,shadow-interrupts = <7 2>, <8 3>, <13 12>, <30 29>;
|
||||
+ };
|
||||
+
|
||||
+ uart: serial@1fbf0000 {
|
||||
+ compatible = "ns16550";
|
||||
+ reg = <0x1fbf0000 0x30>;
|
||||
+ reg-io-width = <4>;
|
||||
+ reg-shift = <2>;
|
||||
+ interrupt-parent = <&intc>;
|
||||
+ interrupts = <0>;
|
||||
+ /*
|
||||
+ * Conversion of baud rate to clock frequency requires a
|
||||
+ * computation that is not in the ns16550 driver, so this
|
||||
+ * uart is fixed at 115200 baud.
|
||||
+ */
|
||||
+ clock-frequency = <1843200>;
|
||||
+ };
|
||||
+
|
||||
+ timer_hpt: timer@1fbf0400 {
|
||||
+ compatible = "econet,en751221-timer";
|
||||
+ reg = <0x1fbf0400 0x100>;
|
||||
+
|
||||
+ interrupt-parent = <&intc>;
|
||||
+ interrupts = <30>;
|
||||
+ clocks = <&hpt_clock>;
|
||||
+ };
|
||||
+};
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts
|
||||
@@ -0,0 +1,19 @@
|
||||
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
+/dts-v1/;
|
||||
+
|
||||
+#include "en751221.dtsi"
|
||||
+
|
||||
+/ {
|
||||
+ model = "SmartFiber XP8421-B";
|
||||
+ compatible = "smartfiber,xp8421-b", "econet,en751221";
|
||||
+
|
||||
+ memory@0 {
|
||||
+ device_type = "memory";
|
||||
+ reg = <0x00000000 0x1c000000>;
|
||||
+ };
|
||||
+
|
||||
+ chosen {
|
||||
+ stdout-path = "/serial@1fbf0000:115200";
|
||||
+ linux,usable-memory-range = <0x00020000 0x1bfe0000>;
|
||||
+ };
|
||||
+};
|
||||
--- a/arch/mips/econet/Kconfig
|
||||
+++ b/arch/mips/econet/Kconfig
|
||||
@@ -32,6 +32,17 @@ choice
|
||||
|
||||
config DTB_ECONET_NONE
|
||||
bool "None"
|
||||
+
|
||||
+ config DTB_ECONET_SMARTFIBER_XP8421_B
|
||||
+ bool "EN751221 SmartFiber XP8421-B"
|
||||
+ depends on SOC_ECONET_EN751221
|
||||
+ select BUILTIN_DTB
|
||||
+ help
|
||||
+ The SmartFiber XP8421-B is a device based on the EN751221 SoC.
|
||||
+ It has 512MB of memory and 256MB of NAND flash. This kernel
|
||||
+ needs only an appended initramfs to boot. It can be loaded
|
||||
+ through XMODEM and booted from memory in the bootloader, or
|
||||
+ it can be packed in tclinux.trx format and written to flash.
|
||||
endchoice
|
||||
|
||||
endif
|
||||
@ -0,0 +1,38 @@
|
||||
From faefb0a59c5914b7b8f737e2ec5c82822e5bc4c7 Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Wed, 7 May 2025 13:45:00 +0000
|
||||
Subject: [PATCH] MAINTAINERS: Add entry for newly added EcoNet platform.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add a MAINTAINERS entry as part of integration of the EcoNet MIPS platform.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
|
||||
---
|
||||
MAINTAINERS | 12 ++++++++++++
|
||||
1 file changed, 12 insertions(+)
|
||||
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -8019,6 +8019,18 @@ W: https://linuxtv.org
|
||||
Q: http://patchwork.linuxtv.org/project/linux-media/list/
|
||||
F: drivers/media/dvb-frontends/ec100*
|
||||
|
||||
+ECONET MIPS PLATFORM
|
||||
+M: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
+L: linux-mips@vger.kernel.org
|
||||
+S: Maintained
|
||||
+F: Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
|
||||
+F: Documentation/devicetree/bindings/mips/econet.yaml
|
||||
+F: Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
|
||||
+F: arch/mips/boot/dts/econet/
|
||||
+F: arch/mips/econet/
|
||||
+F: drivers/clocksource/timer-econet-en751221.c
|
||||
+F: drivers/irqchip/irq-econet-en751221.c
|
||||
+
|
||||
ECRYPT FILE SYSTEM
|
||||
M: Tyler Hicks <code@tyhicks.com>
|
||||
L: ecryptfs@vger.kernel.org
|
||||
@ -0,0 +1,32 @@
|
||||
From 79ee1d20e37cd553cc961962fca8107e69a0c293 Mon Sep 17 00:00:00 2001
|
||||
From: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Date: Wed, 21 May 2025 21:33:33 +0000
|
||||
Subject: [PATCH] mips: econet: Fix incorrect Kconfig dependencies
|
||||
|
||||
config ECONET selects SERIAL_OF_PLATFORM and that depends on SERIAL_8250
|
||||
so we need to select SERIAL_8250 directly.
|
||||
Also do not enable DEBUG_ZBOOT unless DEBUG_KERNEL is set.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
Reported-by: kernel test robot <lkp@intel.com>
|
||||
Closes: https://lore.kernel.org/oe-kbuild-all/202505211654.CBdIsoTq-lkp@intel.com/
|
||||
Closes: https://lore.kernel.org/oe-kbuild-all/202505211451.WRjyf3a9-lkp@intel.com/
|
||||
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
|
||||
---
|
||||
arch/mips/Kconfig | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/mips/Kconfig
|
||||
+++ b/arch/mips/Kconfig
|
||||
@@ -392,9 +392,10 @@ config ECONET
|
||||
bool "EcoNet MIPS family"
|
||||
select BOOT_RAW
|
||||
select CPU_BIG_ENDIAN
|
||||
- select DEBUG_ZBOOT
|
||||
+ 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_HAS_CPU_MIPS32_R1
|
||||
@ -0,0 +1,341 @@
|
||||
--- a/drivers/spi/Kconfig
|
||||
+++ b/drivers/spi/Kconfig
|
||||
@@ -370,6 +370,12 @@ config SPI_DLN2
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called spi-dln2.
|
||||
|
||||
+config SPI_AIROHA_EN7523
|
||||
+ bool "Airoha EN7523 SPI controller support"
|
||||
+ depends on ARCH_AIROHA
|
||||
+ help
|
||||
+ This enables SPI controller support for the Airoha EN7523 SoC.
|
||||
+
|
||||
config SPI_EP93XX
|
||||
tristate "Cirrus Logic EP93xx SPI controller"
|
||||
depends on ARCH_EP93XX || COMPILE_TEST
|
||||
--- a/drivers/spi/Makefile
|
||||
+++ b/drivers/spi/Makefile
|
||||
@@ -52,6 +52,7 @@ obj-$(CONFIG_SPI_DW_BT1) += spi-dw-bt1.
|
||||
obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o
|
||||
obj-$(CONFIG_SPI_DW_PCI) += spi-dw-pci.o
|
||||
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
|
||||
+obj-$(CONFIG_SPI_AIROHA_EN7523) += spi-en7523.o
|
||||
obj-$(CONFIG_SPI_FALCON) += spi-falcon.o
|
||||
obj-$(CONFIG_SPI_FSI) += spi-fsi.o
|
||||
obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/spi/spi-en7523.c
|
||||
@@ -0,0 +1,313 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/mod_devicetable.h>
|
||||
+#include <linux/spi/spi.h>
|
||||
+
|
||||
+
|
||||
+#define ENSPI_READ_IDLE_EN 0x0004
|
||||
+#define ENSPI_MTX_MODE_TOG 0x0014
|
||||
+#define ENSPI_RDCTL_FSM 0x0018
|
||||
+#define ENSPI_MANUAL_EN 0x0020
|
||||
+#define ENSPI_MANUAL_OPFIFO_EMPTY 0x0024
|
||||
+#define ENSPI_MANUAL_OPFIFO_WDATA 0x0028
|
||||
+#define ENSPI_MANUAL_OPFIFO_FULL 0x002C
|
||||
+#define ENSPI_MANUAL_OPFIFO_WR 0x0030
|
||||
+#define ENSPI_MANUAL_DFIFO_FULL 0x0034
|
||||
+#define ENSPI_MANUAL_DFIFO_WDATA 0x0038
|
||||
+#define ENSPI_MANUAL_DFIFO_EMPTY 0x003C
|
||||
+#define ENSPI_MANUAL_DFIFO_RD 0x0040
|
||||
+#define ENSPI_MANUAL_DFIFO_RDATA 0x0044
|
||||
+#define ENSPI_IER 0x0090
|
||||
+#define ENSPI_NFI2SPI_EN 0x0130
|
||||
+
|
||||
+// TODO not in spi block
|
||||
+#define ENSPI_CLOCK_DIVIDER ((void __iomem *)0x1fa201c4)
|
||||
+
|
||||
+#define OP_CSH 0x00
|
||||
+#define OP_CSL 0x01
|
||||
+#define OP_CK 0x02
|
||||
+#define OP_OUTS 0x08
|
||||
+#define OP_OUTD 0x09
|
||||
+#define OP_OUTQ 0x0A
|
||||
+#define OP_INS 0x0C
|
||||
+#define OP_INS0 0x0D
|
||||
+#define OP_IND 0x0E
|
||||
+#define OP_INQ 0x0F
|
||||
+#define OP_OS2IS 0x10
|
||||
+#define OP_OS2ID 0x11
|
||||
+#define OP_OS2IQ 0x12
|
||||
+#define OP_OD2IS 0x13
|
||||
+#define OP_OD2ID 0x14
|
||||
+#define OP_OD2IQ 0x15
|
||||
+#define OP_OQ2IS 0x16
|
||||
+#define OP_OQ2ID 0x17
|
||||
+#define OP_OQ2IQ 0x18
|
||||
+#define OP_OSNIS 0x19
|
||||
+#define OP_ODNID 0x1A
|
||||
+
|
||||
+#define MATRIX_MODE_AUTO 1
|
||||
+#define CONF_MTX_MODE_AUTO 0
|
||||
+#define MANUALEN_AUTO 0
|
||||
+#define MATRIX_MODE_MANUAL 0
|
||||
+#define CONF_MTX_MODE_MANUAL 9
|
||||
+#define MANUALEN_MANUAL 1
|
||||
+
|
||||
+#define _ENSPI_MAX_XFER 0x1ff
|
||||
+
|
||||
+#define REG(x) (iobase + x)
|
||||
+
|
||||
+
|
||||
+static void __iomem *iobase;
|
||||
+
|
||||
+
|
||||
+static void opfifo_write(u32 cmd, u32 len)
|
||||
+{
|
||||
+ u32 tmp = ((cmd & 0x1f) << 9) | (len & 0x1ff);
|
||||
+
|
||||
+ writel(tmp, REG(ENSPI_MANUAL_OPFIFO_WDATA));
|
||||
+
|
||||
+ /* Wait for room in OPFIFO */
|
||||
+ while (readl(REG(ENSPI_MANUAL_OPFIFO_FULL)))
|
||||
+ ;
|
||||
+
|
||||
+ /* Shift command into OPFIFO */
|
||||
+ writel(1, REG(ENSPI_MANUAL_OPFIFO_WR));
|
||||
+
|
||||
+ /* Wait for command to finish */
|
||||
+ while (!readl(REG(ENSPI_MANUAL_OPFIFO_EMPTY)))
|
||||
+ ;
|
||||
+}
|
||||
+
|
||||
+static void set_cs(int state)
|
||||
+{
|
||||
+ if (state)
|
||||
+ opfifo_write(OP_CSH, 1);
|
||||
+ else
|
||||
+ opfifo_write(OP_CSL, 1);
|
||||
+}
|
||||
+
|
||||
+static void manual_begin_cmd(void)
|
||||
+{
|
||||
+ /* Disable read idle state */
|
||||
+ writel(0, REG(ENSPI_READ_IDLE_EN));
|
||||
+
|
||||
+ /* Wait for FSM to reach idle state */
|
||||
+ while (readl(REG(ENSPI_RDCTL_FSM)))
|
||||
+ ;
|
||||
+
|
||||
+ /* Set SPI core to manual mode */
|
||||
+ writel(CONF_MTX_MODE_MANUAL, REG(ENSPI_MTX_MODE_TOG));
|
||||
+ writel(MANUALEN_MANUAL, REG(ENSPI_MANUAL_EN));
|
||||
+}
|
||||
+
|
||||
+static void manual_end_cmd(void)
|
||||
+{
|
||||
+ /* Set SPI core to auto mode */
|
||||
+ writel(CONF_MTX_MODE_AUTO, REG(ENSPI_MTX_MODE_TOG));
|
||||
+ writel(MANUALEN_AUTO, REG(ENSPI_MANUAL_EN));
|
||||
+
|
||||
+ /* Enable read idle state */
|
||||
+ writel(1, REG(ENSPI_READ_IDLE_EN));
|
||||
+}
|
||||
+
|
||||
+static void dfifo_read(u8 *buf, int len)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < len; i++) {
|
||||
+ /* Wait for requested data to show up in DFIFO */
|
||||
+ while (readl(REG(ENSPI_MANUAL_DFIFO_EMPTY)))
|
||||
+ ;
|
||||
+ buf[i] = readl(REG(ENSPI_MANUAL_DFIFO_RDATA));
|
||||
+ /* Queue up next byte */
|
||||
+ writel(1, REG(ENSPI_MANUAL_DFIFO_RD));
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void dfifo_write(const u8 *buf, int len)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < len; i++) {
|
||||
+ /* Wait for room in DFIFO */
|
||||
+ while (readl(REG(ENSPI_MANUAL_DFIFO_FULL)))
|
||||
+ ;
|
||||
+ writel(buf[i], REG(ENSPI_MANUAL_DFIFO_WDATA));
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#if 0
|
||||
+static void set_spi_clock_speed(int freq_mhz)
|
||||
+{
|
||||
+ u32 tmp, val;
|
||||
+
|
||||
+ tmp = readl(ENSPI_CLOCK_DIVIDER);
|
||||
+ tmp &= 0xffff0000;
|
||||
+ writel(tmp, ENSPI_CLOCK_DIVIDER);
|
||||
+
|
||||
+ val = (400 / (freq_mhz * 2));
|
||||
+ tmp |= (val << 8) | 1;
|
||||
+ writel(tmp, ENSPI_CLOCK_DIVIDER);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+static void init_hw(void)
|
||||
+{
|
||||
+ /* Disable manual/auto mode clash interrupt */
|
||||
+ writel(0, REG(ENSPI_IER));
|
||||
+
|
||||
+ // TODO via clk framework
|
||||
+ // set_spi_clock_speed(50);
|
||||
+
|
||||
+ /* Disable DMA */
|
||||
+ writel(0, REG(ENSPI_NFI2SPI_EN));
|
||||
+}
|
||||
+
|
||||
+static int xfer_read(struct spi_transfer *xfer)
|
||||
+{
|
||||
+ int opcode;
|
||||
+ uint8_t *buf = xfer->rx_buf;
|
||||
+
|
||||
+ switch (xfer->rx_nbits) {
|
||||
+ case SPI_NBITS_SINGLE:
|
||||
+ opcode = OP_INS;
|
||||
+ break;
|
||||
+ case SPI_NBITS_DUAL:
|
||||
+ opcode = OP_IND;
|
||||
+ break;
|
||||
+ case SPI_NBITS_QUAD:
|
||||
+ opcode = OP_INQ;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ opfifo_write(opcode, xfer->len);
|
||||
+ dfifo_read(buf, xfer->len);
|
||||
+
|
||||
+ return xfer->len;
|
||||
+}
|
||||
+
|
||||
+static int xfer_write(struct spi_transfer *xfer, int next_xfer_is_rx)
|
||||
+{
|
||||
+ int opcode;
|
||||
+ const uint8_t *buf = xfer->tx_buf;
|
||||
+
|
||||
+ if (next_xfer_is_rx) {
|
||||
+ /* need to use Ox2Ix opcode to set the core to input afterwards */
|
||||
+ switch (xfer->tx_nbits) {
|
||||
+ case SPI_NBITS_SINGLE:
|
||||
+ opcode = OP_OS2IS;
|
||||
+ break;
|
||||
+ case SPI_NBITS_DUAL:
|
||||
+ opcode = OP_OS2ID;
|
||||
+ break;
|
||||
+ case SPI_NBITS_QUAD:
|
||||
+ opcode = OP_OS2IQ;
|
||||
+ break;
|
||||
+ }
|
||||
+ } else {
|
||||
+ switch (xfer->tx_nbits) {
|
||||
+ case SPI_NBITS_SINGLE:
|
||||
+ opcode = OP_OUTS;
|
||||
+ break;
|
||||
+ case SPI_NBITS_DUAL:
|
||||
+ opcode = OP_OUTD;
|
||||
+ break;
|
||||
+ case SPI_NBITS_QUAD:
|
||||
+ opcode = OP_OUTQ;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ opfifo_write(opcode, xfer->len);
|
||||
+ dfifo_write(buf, xfer->len);
|
||||
+
|
||||
+ return xfer->len;
|
||||
+}
|
||||
+
|
||||
+size_t max_transfer_size(struct spi_device *spi)
|
||||
+{
|
||||
+ return _ENSPI_MAX_XFER;
|
||||
+}
|
||||
+
|
||||
+int transfer_one_message(struct spi_controller *ctrl, struct spi_message *msg)
|
||||
+{
|
||||
+ struct spi_transfer *xfer;
|
||||
+ int next_xfer_is_rx = 0;
|
||||
+
|
||||
+ manual_begin_cmd();
|
||||
+ set_cs(0);
|
||||
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
+ if (xfer->tx_buf) {
|
||||
+ if (!list_is_last(&xfer->transfer_list, &msg->transfers)
|
||||
+ && list_next_entry(xfer, transfer_list)->rx_buf != NULL)
|
||||
+ next_xfer_is_rx = 1;
|
||||
+ else
|
||||
+ next_xfer_is_rx = 0;
|
||||
+ msg->actual_length += xfer_write(xfer, next_xfer_is_rx);
|
||||
+ } else if (xfer->rx_buf) {
|
||||
+ msg->actual_length += xfer_read(xfer);
|
||||
+ }
|
||||
+ }
|
||||
+ set_cs(1);
|
||||
+ manual_end_cmd();
|
||||
+
|
||||
+ msg->status = 0;
|
||||
+ spi_finalize_current_message(ctrl);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int spi_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct spi_controller *ctrl;
|
||||
+ int err;
|
||||
+
|
||||
+ ctrl = devm_spi_alloc_master(&pdev->dev, 0);
|
||||
+ if (!ctrl) {
|
||||
+ dev_err(&pdev->dev, "Error allocating SPI controller\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ iobase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
+ if (IS_ERR(iobase)) {
|
||||
+ dev_err(&pdev->dev, "Could not map SPI register address");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ init_hw();
|
||||
+
|
||||
+ ctrl->dev.of_node = pdev->dev.of_node;
|
||||
+ ctrl->flags = SPI_CONTROLLER_HALF_DUPLEX;
|
||||
+ ctrl->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL;
|
||||
+ ctrl->max_transfer_size = max_transfer_size;
|
||||
+ ctrl->transfer_one_message = transfer_one_message;
|
||||
+ err = devm_spi_register_controller(&pdev->dev, ctrl);
|
||||
+ if (err) {
|
||||
+ dev_err(&pdev->dev, "Could not register SPI controller\n");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id spi_of_ids[] = {
|
||||
+ { .compatible = "airoha,en7523-spi" },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, spi_of_ids);
|
||||
+
|
||||
+static struct platform_driver spi_driver = {
|
||||
+ .probe = spi_probe,
|
||||
+ .driver = {
|
||||
+ .name = "airoha-en7523-spi",
|
||||
+ .of_match_table = spi_of_ids,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(spi_driver);
|
||||
+
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>");
|
||||
+MODULE_DESCRIPTION("Airoha EN7523 SPI driver");
|
||||
@ -0,0 +1,56 @@
|
||||
Subject: Adapt Airoha EN7523 SPI to work with EcoNet EN751221
|
||||
|
||||
The SPI driver from Airoha EN7523 is copied here in it's original form
|
||||
so this patch makes three updates to it in order to make it work
|
||||
correctly in the EcoNet EN751221 context.
|
||||
|
||||
The main change here is that the chip select operation is sent twice.
|
||||
This pattern is borrowed from the vendor code and it prevents write
|
||||
operations from being lost sporadically on the EN751221.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
---
|
||||
--- a/drivers/spi/Kconfig
|
||||
+++ b/drivers/spi/Kconfig
|
||||
@@ -372,7 +372,7 @@ config SPI_DLN2
|
||||
|
||||
config SPI_AIROHA_EN7523
|
||||
bool "Airoha EN7523 SPI controller support"
|
||||
- depends on ARCH_AIROHA
|
||||
+ depends on ARCH_AIROHA || ECONET
|
||||
help
|
||||
This enables SPI controller support for the Airoha EN7523 SoC.
|
||||
|
||||
--- a/drivers/spi/spi-en7523.c
|
||||
+++ b/drivers/spi/spi-en7523.c
|
||||
@@ -82,10 +82,11 @@ static void opfifo_write(u32 cmd, u32 le
|
||||
|
||||
static void set_cs(int state)
|
||||
{
|
||||
- if (state)
|
||||
- opfifo_write(OP_CSH, 1);
|
||||
- else
|
||||
- opfifo_write(OP_CSL, 1);
|
||||
+ u32 cmd = state ? OP_CSH : OP_CSL;
|
||||
+
|
||||
+ /* EN751221 drops writes if we don't send this twice. */
|
||||
+ opfifo_write(cmd, 1);
|
||||
+ opfifo_write(cmd, 1);
|
||||
}
|
||||
|
||||
static void manual_begin_cmd(void)
|
||||
@@ -226,12 +227,12 @@ static int xfer_write(struct spi_transfe
|
||||
return xfer->len;
|
||||
}
|
||||
|
||||
-size_t max_transfer_size(struct spi_device *spi)
|
||||
+static size_t max_transfer_size(struct spi_device *spi)
|
||||
{
|
||||
return _ENSPI_MAX_XFER;
|
||||
}
|
||||
|
||||
-int transfer_one_message(struct spi_controller *ctrl, struct spi_message *msg)
|
||||
+static int transfer_one_message(struct spi_controller *ctrl, struct spi_message *msg)
|
||||
{
|
||||
struct spi_transfer *xfer;
|
||||
int next_xfer_is_rx = 0;
|
||||
@ -0,0 +1,40 @@
|
||||
Subject: Add EcoNet bad block table
|
||||
|
||||
The EcoNet BBT/BMT is used for resolving bad blocks in the flash.
|
||||
It is implemented in the EcoNet bootloader so you cannot safely
|
||||
access flash without using it.
|
||||
|
||||
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
|
||||
---
|
||||
--- a/drivers/mtd/nand/Makefile
|
||||
+++ b/drivers/mtd/nand/Makefile
|
||||
@@ -3,7 +3,7 @@
|
||||
nandcore-objs := core.o bbt.o
|
||||
obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
|
||||
obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o
|
||||
-obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o
|
||||
+obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o en75_bmt.o
|
||||
ifeq ($(CONFIG_SPI_QPIC_SNAND),y)
|
||||
obj-$(CONFIG_SPI_QPIC_SNAND) += qpic_common.o
|
||||
else
|
||||
--- a/drivers/mtd/nand/mtk_bmt.h
|
||||
+++ b/drivers/mtd/nand/mtk_bmt.h
|
||||
@@ -77,6 +77,7 @@ extern struct bmt_desc bmtd;
|
||||
extern const struct mtk_bmt_ops mtk_bmt_v2_ops;
|
||||
extern const struct mtk_bmt_ops mtk_bmt_bbt_ops;
|
||||
extern const struct mtk_bmt_ops mtk_bmt_nmbm_ops;
|
||||
+extern const struct mtk_bmt_ops en75_bmt_ops;
|
||||
|
||||
static inline u32 blk_pg(u16 block)
|
||||
{
|
||||
--- a/drivers/mtd/nand/mtk_bmt.c
|
||||
+++ b/drivers/mtd/nand/mtk_bmt.c
|
||||
@@ -422,6 +422,8 @@ int mtk_bmt_attach(struct mtd_info *mtd)
|
||||
bmtd.ops = &mtk_bmt_nmbm_ops;
|
||||
else if (of_property_read_bool(np, "mediatek,bbt"))
|
||||
bmtd.ops = &mtk_bmt_bbt_ops;
|
||||
+ else if (of_property_read_bool(np, "econet,bmt"))
|
||||
+ bmtd.ops = &en75_bmt_ops;
|
||||
else
|
||||
return 0;
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
--- a/drivers/mtd/nand/spi/core.c
|
||||
+++ b/drivers/mtd/nand/spi/core.c
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
+#include <linux/mtd/mtk_bmt.h>
|
||||
|
||||
static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
|
||||
{
|
||||
@@ -1525,6 +1526,7 @@ static int spinand_probe(struct spi_mem
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ mtk_bmt_attach(mtd);
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret)
|
||||
goto err_spinand_cleanup;
|
||||
@@ -1532,6 +1534,7 @@ static int spinand_probe(struct spi_mem
|
||||
return 0;
|
||||
|
||||
err_spinand_cleanup:
|
||||
+ mtk_bmt_detach(mtd);
|
||||
spinand_cleanup(spinand);
|
||||
|
||||
return ret;
|
||||
@@ -1550,6 +1553,7 @@ static int spinand_remove(struct spi_mem
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ mtk_bmt_detach(mtd);
|
||||
spinand_cleanup(spinand);
|
||||
|
||||
return 0;
|
||||
Loading…
Reference in New Issue
Block a user