From: Markus Stockhausen Date: Tue, 27 May 2025 13:10:27 +0000 (-0400) Subject: realtek: 6.12: create patches-6.12 from patches-6.6 X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=b943c746d2e9ef04e625cbf51725786f25aaf758;p=openwrt%2Fstaging%2Fpepe2k.git realtek: 6.12: create patches-6.12 from patches-6.6 Automatically generated commit. Signed-off-by: Markus Stockhausen Link: https://github.com/openwrt/openwrt/pull/18935 Signed-off-by: Robert Marko --- diff --git a/target/linux/realtek/patches-6.12/300-mips-add-rtl838x-platform.patch b/target/linux/realtek/patches-6.12/300-mips-add-rtl838x-platform.patch new file mode 100644 index 0000000000..bb89a7864f --- /dev/null +++ b/target/linux/realtek/patches-6.12/300-mips-add-rtl838x-platform.patch @@ -0,0 +1,89 @@ +From fce11f68491b46b93df69de0630cd9edb90bc772 Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Wed, 29 Dec 2021 21:54:21 +0100 +Subject: [PATCH] realtek: Create 4 different Realtek Platforms + +Creates RTL83XX as a basic kernel config parameter for the +RTL838X, RTL839x, RTL930X and RTL931X platforms with respective +configurations for the SoCs, which are introduced in addition. + +Submitted-by: Birger Koblitz +--- +--- a/arch/mips/Kbuild.platforms ++++ b/arch/mips/Kbuild.platforms +@@ -22,6 +22,7 @@ platform-$(CONFIG_MACH_NINTENDO64) += n6 + platform-$(CONFIG_PIC32MZDA) += pic32/ + platform-$(CONFIG_RALINK) += ralink/ + platform-$(CONFIG_MIKROTIK_RB532) += rb532/ ++platform-$(CONFIG_MACH_REALTEK_RTL) += rtl838x/ + platform-$(CONFIG_SGI_IP22) += sgi-ip22/ + platform-$(CONFIG_SGI_IP27) += sgi-ip27/ + platform-$(CONFIG_SGI_IP28) += sgi-ip22/ +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -620,21 +620,23 @@ config RALINK + + config MACH_REALTEK_RTL + bool "Realtek RTL838x/RTL839x based machines" +- select MIPS_GENERIC + select DMA_NONCOHERENT + select IRQ_MIPS_CPU +- select CSRC_R4K +- select CEVT_R4K + select SYS_HAS_CPU_MIPS32_R1 + select SYS_HAS_CPU_MIPS32_R2 + select SYS_SUPPORTS_BIG_ENDIAN + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_MIPS16 +- select SYS_SUPPORTS_MULTITHREADING +- select SYS_SUPPORTS_VPE_LOADER + select BOOT_RAW + select PINCTRL + select USE_OF ++ select NO_EXCEPT_FILL ++ select SYS_SUPPORTS_HIGHMEM ++ select SYS_HAS_EARLY_PRINTK ++ select SYS_HAS_EARLY_PRINTK_8250 ++ select USE_GENERIC_EARLY_PRINTK_8250 ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER + + config SGI_IP22 + bool "SGI IP22 (Indy/Indigo2)" +@@ -970,6 +972,36 @@ config CAVIUM_OCTEON_SOC + + endchoice + ++config RTL838X ++ bool "Realtek RTL838X based platforms" ++ depends on MACH_REALTEK_RTL ++ select CPU_SUPPORTS_CPUFREQ ++ select MIPS_EXTERNAL_TIMER ++ ++config RTL839X ++ bool "Realtek RTL839X based platforms" ++ depends on MACH_REALTEK_RTL ++ select CPU_SUPPORTS_CPUFREQ ++ select MIPS_EXTERNAL_TIMER ++ select SYS_SUPPORTS_MULTITHREADING ++ ++config RTL930X ++ bool "Realtek RTL930X based platforms" ++ depends on MACH_REALTEK_RTL ++ select MIPS_CPU_SCACHE ++ select MIPS_EXTERNAL_TIMER ++ select SYS_SUPPORTS_MULTITHREADING ++ ++config RTL931X ++ bool "Realtek RTL931X based platforms" ++ depends on RTL930X ++ select MIPS_GIC ++ select COMMON_CLK ++ select CLKSRC_MIPS_GIC ++ select SYS_SUPPORTS_VPE_LOADER ++ select SYS_SUPPORTS_SMP ++ select SYS_SUPPORTS_MIPS_CPS ++ + source "arch/mips/alchemy/Kconfig" + source "arch/mips/ath25/Kconfig" + source "arch/mips/ath79/Kconfig" diff --git a/target/linux/realtek/patches-6.12/302-clocksource-add-otto-driver.patch b/target/linux/realtek/patches-6.12/302-clocksource-add-otto-driver.patch new file mode 100644 index 0000000000..791ad0edb7 --- /dev/null +++ b/target/linux/realtek/patches-6.12/302-clocksource-add-otto-driver.patch @@ -0,0 +1,93 @@ +From 3cc8011171186d906c547bc6f0c1f8e350edc7cf Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Mon, 3 Oct 2022 14:45:21 +0200 +Subject: [PATCH] realtek: resurrect timer driver + +Now that we provide a clock driver for the Reltek SOCs the CPU frequency might +change on demand. This has direct visible effects during operation + +- the CEVT 4K timer is no longer a stable clocksource +- after CPU frequencies changes time calculation works wrong +- sched_clock falls back to kernel default interval (100 Hz) +- timestamps in dmesg have only 2 digits left + +[ 0.000000] sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps ... +[ 0.060000] pid_max: default: 32768 minimum: 301 +[ 0.070000] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear) +[ 0.070000] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear) +[ 0.080000] dyndbg: Ignore empty _ddebug table in a CONFIG_DYNAMIC_DEBUG_CORE build +[ 0.090000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, ... + +Looking around where we can start the CEVT timer for RTL930X is a good basis. +Initially it was developed as a clocksource driver for the broken timer in that +specific SOC series. Afterwards it was shifted around to the CEVT location, +got SMP enablement and lost its clocksource feature. So we at least have +something to copy from. As the timers on these devices are well understood +the implementation follows this way: + +- leave the RTL930X implementation as is +- provide a new driver for RTL83XX devices only +- swap RTL930X driver at a later time + +Like the clock driver this patch contains a self contained module that is SOC +independet and already provides full support for the RTL838X, RTL839X and +RTL930X devices. Some of the new (or reestablished) features are: + +- simplified initialization routines +- SMP setup with CPU hotplug framework +- derived from LXB clock speed +- supplied clocksource +- dedicated register functions for better readability +- documentation about some caveats + +Signed-off-by: Markus Stockhausen +[remove unused header includes, remove old CONFIG_MIPS dependency, add +REALTEK_ prefix to driver symbol] +Signed-off-by: Sander Vanheule + +--- + drivers/clocksource/Kconfig | 12 +++ + drivers/clocksource/Makefile | 1 + + include/linux/cpuhotplug.h | 1 + + 3 files changed, 14 insertions(+) + +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -134,6 +134,17 @@ config RDA_TIMER + help + Enables the support for the RDA Micro timer driver. + ++config REALTEK_OTTO_TIMER ++ bool "Clocksource/timer for the Realtek Otto platform" ++ select COMMON_CLK ++ select TIMER_OF ++ help ++ This driver adds support for the timers found in the Realtek RTL83xx ++ and RTL93xx SoCs series. This includes chips such as RTL8380, RTL8381 ++ and RTL832, as well as chips from the RTL839x series, such as RTL8390 ++ RT8391, RTL8392, RTL8393 and RTL8396 and chips of the RTL930x series ++ such as RTL9301, RTL9302 or RTL9303. ++ + config SUN4I_TIMER + bool "Sun4i timer driver" if COMPILE_TEST + depends on HAS_IOMEM +--- a/drivers/clocksource/Makefile ++++ b/drivers/clocksource/Makefile +@@ -59,6 +59,7 @@ obj-$(CONFIG_MILBEAUT_TIMER) += timer-mi + obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o + obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o + obj-$(CONFIG_RDA_TIMER) += timer-rda.o ++obj-$(CONFIG_REALTEK_OTTO_TIMER) += timer-rtl-otto.o + + obj-$(CONFIG_ARC_TIMERS) += arc_timer.o + obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o +--- a/include/linux/cpuhotplug.h ++++ b/include/linux/cpuhotplug.h +@@ -181,6 +181,7 @@ enum cpuhp_state { + CPUHP_AP_MARCO_TIMER_STARTING, + CPUHP_AP_MIPS_GIC_TIMER_STARTING, + CPUHP_AP_ARC_TIMER_STARTING, ++ CPUHP_AP_REALTEK_TIMER_STARTING, + CPUHP_AP_RISCV_TIMER_STARTING, + CPUHP_AP_CLINT_TIMER_STARTING, + CPUHP_AP_CSKY_TIMER_STARTING, diff --git a/target/linux/realtek/patches-6.12/310-add-i2c-rtl9300-support.patch b/target/linux/realtek/patches-6.12/310-add-i2c-rtl9300-support.patch new file mode 100644 index 0000000000..9eaed95d0d --- /dev/null +++ b/target/linux/realtek/patches-6.12/310-add-i2c-rtl9300-support.patch @@ -0,0 +1,46 @@ +From 63a0a4d85bc900464c5b046b13808a582345f8c8 Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Sat, 11 Dec 2021 20:14:47 +0100 +Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C controller + +This adds support for the RTL9300 and RTL9310 I2C controller. +The controller implements the SMBus protocol for SMBus transfers +over an I2C bus. The driver supports selecting one of the 2 possible +SCL pins and any of the 8 possible SDA pins. Bus speeds of +100kHz (standard speed) and 400kHz (high speed I2C) are supported. + +Submitted-by: Birger Koblitz +--- + drivers/i2c/busses/Kconfig | 10 +++++++++ + drivers/i2c/busses/Makefile | 1 + + 2 files changed, 11 insertions(+) + +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -1023,6 +1023,16 @@ config I2C_RK3X + This driver can also be built as a module. If so, the module will + be called i2c-rk3x. + ++config I2C_RTL9300 ++ tristate "Realtek RTL9300 I2C adapter" ++ depends on OF ++ help ++ Say Y here to include support for the I2C adapter in Realtek RTL9300 ++ and RTL9310 SoCs. ++ ++ This driver can also be built as a module. If so, the module will ++ be called i2c-rtl9300. ++ + config I2C_RZV2M + tristate "Renesas RZ/V2M adapter" + depends on ARCH_RENESAS || COMPILE_TEST +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -101,6 +101,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom- + obj-$(CONFIG_I2C_QUP) += i2c-qup.o + obj-$(CONFIG_I2C_RIIC) += i2c-riic.o + obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o ++obj-$(CONFIG_I2C_RTL9300) += i2c-rtl9300.o + obj-$(CONFIG_I2C_RZV2M) += i2c-rzv2m.o + obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o + obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o diff --git a/target/linux/realtek/patches-6.12/311-add-i2c-mux-rtl9300-support.patch b/target/linux/realtek/patches-6.12/311-add-i2c-mux-rtl9300-support.patch new file mode 100644 index 0000000000..d0bfba4538 --- /dev/null +++ b/target/linux/realtek/patches-6.12/311-add-i2c-mux-rtl9300-support.patch @@ -0,0 +1,46 @@ +From f4bdb7fdccdfe3fa382abe77f72a16c2f2e6add0 Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Sat, 11 Dec 2021 20:25:37 +0100 +Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C multiplexing + +The RTL9300/RTL9310 I2C controllers have support for 2 independent I2C +masters, each with a fixed SCL pin, that cannot be changed. Each of these +masters can use 8 (RTL9300) or 16 (RTL9310) different pins for SDA. +This multiplexer directly controls the two masters and their shared +IO configuration registers to allow multiplexing between any of these +busses. The two masters cannot be used in parallel as the multiplex +is protected by a standard multiplex lock. + +Submitted-by: Birger Koblitz +--- + drivers/i2c/muxes/Kconfig | 9 +++++++ + drivers/i2c/muxes/Makefile | 1 + + 2 files changed, 10 insertions(+) + +--- a/drivers/i2c/muxes/Kconfig ++++ b/drivers/i2c/muxes/Kconfig +@@ -99,6 +99,15 @@ config I2C_MUX_REG + This driver can also be built as a module. If so, the module + will be called i2c-mux-reg. + ++config I2C_MUX_RTL9300 ++ tristate "RTL9300 based I2C multiplexer" ++ help ++ If you say yes to this option, support will be included for a ++ RTL9300 based I2C multiplexer. ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-mux-reg. ++ + config I2C_DEMUX_PINCTRL + tristate "pinctrl-based I2C demultiplexer" + depends on PINCTRL && OF +--- a/drivers/i2c/muxes/Makefile ++++ b/drivers/i2c/muxes/Makefile +@@ -14,5 +14,6 @@ obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux + obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o + obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o + obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o ++obj-$(CONFIG_I2C_MUX_RTL9300) += i2c-mux-rtl9300.o + + ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/target/linux/realtek/patches-6.12/314-irqchip-irq-realtek-rtl-add-VPE-support.patch b/target/linux/realtek/patches-6.12/314-irqchip-irq-realtek-rtl-add-VPE-support.patch new file mode 100644 index 0000000000..4610ca8c19 --- /dev/null +++ b/target/linux/realtek/patches-6.12/314-irqchip-irq-realtek-rtl-add-VPE-support.patch @@ -0,0 +1,427 @@ +From 6c18e9c491959ac0674ebe36b09f9ddc3f2c9bce Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Fri, 31 Dec 2021 11:56:49 +0100 +Subject: [PATCH] realtek: Add VPE support for the IRQ driver + +In order to support VSMP, enable support for both VPEs of the RTL839X +and RTL930X SoCs in the irq-realtek-rtl driver. Add support for IRQ +affinity setting. + +Up to kernel 5.15 this patch was divided into two parts + +315-irqchip-irq-realtek-rtl-add-VPE-support.patch +319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch + +As both parts will only work in combination they have been merged into +one patch. + +Submitted-by: Birger Koblitz +Submitted-by: INAGAKI Hiroshi +Signed-off-by: Markus Stockhausen +--- + drivers/irqchip/irq-realtek-rtl.c | 296 +++++++++++++++++++++++++----- + 1 file changed, 249 insertions(+), 47 deletions(-) + +--- a/drivers/irqchip/irq-realtek-rtl.c ++++ b/drivers/irqchip/irq-realtek-rtl.c +@@ -22,22 +22,58 @@ + #define RTL_ICTL_IRR3 0x14 + + #define RTL_ICTL_NUM_INPUTS 32 +- +-#define REG(x) (realtek_ictl_base + x) ++#define RTL_ICTL_NUM_OUTPUTS 15 + + static DEFINE_RAW_SPINLOCK(irq_lock); +-static void __iomem *realtek_ictl_base; ++ ++#define REG(offset, cpu) (realtek_ictl_base[cpu] + offset) ++ ++static u32 realtek_ictl_unmask[NR_CPUS]; ++static void __iomem *realtek_ictl_base[NR_CPUS]; ++static cpumask_t realtek_ictl_cpu_configurable; ++ ++struct realtek_ictl_output { ++ /* IRQ controller data */ ++ struct fwnode_handle *fwnode; ++ /* Output specific data */ ++ unsigned int output_index; ++ struct irq_domain *domain; ++ u32 child_mask; ++}; + + /* +- * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering, +- * placing IRQ 31 in the first four bits. A routing value of '0' means the +- * interrupt is left disconnected. Routing values {1..15} connect to output +- * lines {0..14}. ++ * Per CPU we have a set of 5 registers that determine interrupt handling for ++ * 32 external interrupts. GIMR (enable/disable interrupt) plus IRR0-IRR3 that ++ * contain "routing" or "priority" values. GIMR uses one bit for each interrupt ++ * and IRRx store 4 bits per interrupt. Realtek uses inverted numbering, ++ * placing IRQ 31 in the first four bits. The register combinations give the ++ * following results for a single interrupt in the wild: ++ * ++ * a) GIMR = 0 / IRRx > 0 -> no interrupts ++ * b) GIMR = 0 / IRRx = 0 -> no interrupts ++ * c) GIMR = 1 / IRRx > 0 -> interrupts ++ * d) GIMR = 1 / IRRx = 0 -> rare interrupts in SMP environment ++ * ++ * Combination d) seems to trigger interrupts only on a VPE if the other VPE ++ * has GIMR = 0 and IRRx > 0. E.g. busy without interrupts allowed. To provide ++ * IRQ balancing features in SMP this driver will handle the registers as ++ * follows: ++ * ++ * 1) set IRRx > 0 for VPE where the interrupt is desired ++ * 2) set IRRx = 0 for VPE where the interrupt is not desired ++ * 3) set both GIMR = 0 to mask (disabled) interrupt ++ * 4) set GIMR = 1 to unmask (enable) interrupt but only for VPE where IRRx > 0 + */ ++ + #define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32)) + #define IRR_SHIFT(idx) ((idx * 4) % 32) + +-static void write_irr(void __iomem *irr0, int idx, u32 value) ++static inline u32 read_irr(void __iomem *irr0, int idx) ++{ ++ return (readl(irr0 + IRR_OFFSET(idx)) >> IRR_SHIFT(idx)) & 0xf; ++} ++ ++static inline void write_irr(void __iomem *irr0, int idx, u32 value) + { + unsigned int offset = IRR_OFFSET(idx); + unsigned int shift = IRR_SHIFT(idx); +@@ -48,16 +84,33 @@ static void write_irr(void __iomem *irr0 + writel(irr, irr0 + offset); + } + ++static inline void enable_gimr(int hwirq, int cpu) ++{ ++ u32 value; ++ ++ value = readl(REG(RTL_ICTL_GIMR, cpu)); ++ value |= (BIT(hwirq) & realtek_ictl_unmask[cpu]); ++ writel(value, REG(RTL_ICTL_GIMR, cpu)); ++} ++ ++static inline void disable_gimr(int hwirq, int cpu) ++{ ++ u32 value; ++ ++ value = readl(REG(RTL_ICTL_GIMR, cpu)); ++ value &= ~BIT(hwirq); ++ writel(value, REG(RTL_ICTL_GIMR, cpu)); ++} ++ + static void realtek_ictl_unmask_irq(struct irq_data *i) + { + unsigned long flags; +- u32 value; ++ int cpu; + + raw_spin_lock_irqsave(&irq_lock, flags); + +- value = readl(REG(RTL_ICTL_GIMR)); +- value |= BIT(i->hwirq); +- writel(value, REG(RTL_ICTL_GIMR)); ++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) ++ enable_gimr(i->hwirq, cpu); + + raw_spin_unlock_irqrestore(&irq_lock, flags); + } +@@ -65,110 +118,259 @@ static void realtek_ictl_unmask_irq(stru + static void realtek_ictl_mask_irq(struct irq_data *i) + { + unsigned long flags; +- u32 value; ++ int cpu; + + raw_spin_lock_irqsave(&irq_lock, flags); + +- value = readl(REG(RTL_ICTL_GIMR)); +- value &= ~BIT(i->hwirq); +- writel(value, REG(RTL_ICTL_GIMR)); ++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) ++ disable_gimr(i->hwirq, cpu); + + raw_spin_unlock_irqrestore(&irq_lock, flags); + } + ++static int __maybe_unused realtek_ictl_irq_affinity(struct irq_data *i, ++ const struct cpumask *dest, bool force) ++{ ++ struct realtek_ictl_output *output = i->domain->host_data; ++ cpumask_t cpu_configure; ++ cpumask_t cpu_disable; ++ cpumask_t cpu_enable; ++ unsigned long flags; ++ int cpu; ++ ++ raw_spin_lock_irqsave(&irq_lock, flags); ++ ++ cpumask_and(&cpu_configure, cpu_present_mask, &realtek_ictl_cpu_configurable); ++ ++ cpumask_and(&cpu_enable, &cpu_configure, dest); ++ cpumask_andnot(&cpu_disable, &cpu_configure, dest); ++ ++ for_each_cpu(cpu, &cpu_disable) { ++ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, 0); ++ realtek_ictl_unmask[cpu] &= ~BIT(i->hwirq); ++ disable_gimr(i->hwirq, cpu); ++ } ++ ++ for_each_cpu(cpu, &cpu_enable) { ++ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, output->output_index + 1); ++ realtek_ictl_unmask[cpu] |= BIT(i->hwirq); ++ enable_gimr(i->hwirq, cpu); ++ } ++ ++ irq_data_update_effective_affinity(i, &cpu_enable); ++ ++ raw_spin_unlock_irqrestore(&irq_lock, flags); ++ ++ return IRQ_SET_MASK_OK; ++} ++ + static struct irq_chip realtek_ictl_irq = { + .name = "realtek-rtl-intc", + .irq_mask = realtek_ictl_mask_irq, + .irq_unmask = realtek_ictl_unmask_irq, ++#ifdef CONFIG_SMP ++ .irq_set_affinity = realtek_ictl_irq_affinity, ++#endif + }; + + static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) + { ++ struct realtek_ictl_output *output = d->host_data; + unsigned long flags; + + irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq); + + raw_spin_lock_irqsave(&irq_lock, flags); +- write_irr(REG(RTL_ICTL_IRR0), hw, 1); ++ ++ output->child_mask |= BIT(hw); ++ write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1); ++ realtek_ictl_unmask[0] |= BIT(hw); ++ + raw_spin_unlock_irqrestore(&irq_lock, flags); + + return 0; + } + ++static int intc_select(struct irq_domain *d, struct irq_fwspec *fwspec, ++ enum irq_domain_bus_token bus_token) ++{ ++ struct realtek_ictl_output *output = d->host_data; ++ bool routed_elsewhere; ++ unsigned long flags; ++ u32 routing_old; ++ int cpu; ++ ++ if (fwspec->fwnode != output->fwnode) ++ return false; ++ ++ /* Original specifiers had only one parameter */ ++ if (fwspec->param_count < 2) ++ return true; ++ ++ raw_spin_lock_irqsave(&irq_lock, flags); ++ ++ /* ++ * Inputs can only be routed to one output, so they shouldn't be ++ * allowed to end up in multiple domains. ++ */ ++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) { ++ routing_old = read_irr(REG(RTL_ICTL_IRR0, cpu), fwspec->param[0]); ++ routed_elsewhere = routing_old && fwspec->param[1] != routing_old - 1; ++ if (routed_elsewhere) { ++ pr_warn("soc int %d already routed to output %d\n", ++ fwspec->param[0], routing_old - 1); ++ break; ++ } ++ } ++ ++ raw_spin_unlock_irqrestore(&irq_lock, flags); ++ ++ return !routed_elsewhere && fwspec->param[1] == output->output_index; ++} ++ + static const struct irq_domain_ops irq_domain_ops = { + .map = intc_map, ++ .select = intc_select, + .xlate = irq_domain_xlate_onecell, + }; + + static void realtek_irq_dispatch(struct irq_desc *desc) + { ++ struct realtek_ictl_output *output = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); +- struct irq_domain *domain; ++ int cpu = smp_processor_id(); + unsigned long pending; + unsigned int soc_int; + + chained_irq_enter(chip, desc); +- pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR)); ++ pending = readl(REG(RTL_ICTL_GIMR, cpu)) & readl(REG(RTL_ICTL_GISR, cpu)) ++ & output->child_mask; + + if (unlikely(!pending)) { + spurious_interrupt(); + goto out; + } + +- domain = irq_desc_get_handler_data(desc); +- for_each_set_bit(soc_int, &pending, 32) +- generic_handle_domain_irq(domain, soc_int); ++ for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS) ++ generic_handle_domain_irq(output->domain, soc_int); + + out: + chained_irq_exit(chip, desc); + } + ++/* ++ * SoC interrupts are cascaded to MIPS CPU interrupts according to the ++ * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for ++ * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts ++ * thus go into 4 IRRs. A routing value of '0' means the interrupt is left ++ * disconnected. Routing values {1..15} connect to output lines {0..14}. ++ */ ++static int __init setup_parent_interrupts(struct device_node *node, int *parents, ++ unsigned int num_parents) ++{ ++ struct realtek_ictl_output *outputs; ++ struct realtek_ictl_output *output; ++ struct irq_domain *domain; ++ unsigned int p; ++ ++ outputs = kcalloc(num_parents, sizeof(*outputs), GFP_KERNEL); ++ if (!outputs) ++ return -ENOMEM; ++ ++ for (p = 0; p < num_parents; p++) { ++ output = outputs + p; ++ ++ domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, output); ++ if (!domain) ++ goto domain_err; ++ ++ output->fwnode = of_node_to_fwnode(node); ++ output->output_index = p; ++ output->domain = domain; ++ ++ irq_set_chained_handler_and_data(parents[p], realtek_irq_dispatch, output); ++ } ++ ++ return 0; ++ ++domain_err: ++ while (p--) { ++ irq_set_chained_handler_and_data(parents[p], NULL, NULL); ++ irq_domain_remove(outputs[p].domain); ++ } ++ ++ kfree(outputs); ++ ++ return -ENOMEM; ++} ++ + static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent) + { ++ int parent_irqs[RTL_ICTL_NUM_OUTPUTS]; + struct of_phandle_args oirq; +- struct irq_domain *domain; ++ unsigned int num_parents; + unsigned int soc_irq; +- int parent_irq; ++ unsigned int p; ++ int cpu; ++ ++ cpumask_clear(&realtek_ictl_cpu_configurable); + +- realtek_ictl_base = of_iomap(node, 0); +- if (!realtek_ictl_base) ++ for (cpu = 0; cpu < NR_CPUS; cpu++) { ++ realtek_ictl_base[cpu] = of_iomap(node, cpu); ++ if (realtek_ictl_base[cpu]) { ++ cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable); ++ ++ /* Disable all cascaded interrupts and clear routing */ ++ for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) { ++ write_irr(REG(RTL_ICTL_IRR0, cpu), soc_irq, 0); ++ realtek_ictl_unmask[cpu] &= ~BIT(soc_irq); ++ disable_gimr(soc_irq, cpu); ++ } ++ } ++ } ++ ++ if (cpumask_empty(&realtek_ictl_cpu_configurable)) + return -ENXIO; + +- /* Disable all cascaded interrupts and clear routing */ +- writel(0, REG(RTL_ICTL_GIMR)); +- for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) +- write_irr(REG(RTL_ICTL_IRR0), soc_irq, 0); ++ num_parents = of_irq_count(node); ++ if (num_parents > RTL_ICTL_NUM_OUTPUTS) { ++ pr_err("too many parent interrupts\n"); ++ return -EINVAL; ++ } + +- if (WARN_ON(!of_irq_count(node))) { ++ for (p = 0; p < num_parents; p++) ++ parent_irqs[p] = of_irq_get(node, p); ++ ++ if (WARN_ON(!num_parents)) { + /* + * If DT contains no parent interrupts, assume MIPS CPU IRQ 2 + * (HW0) is connected to the first output. This is the case for + * all known hardware anyway. "interrupt-map" is deprecated, so + * don't bother trying to parse that. ++ * Since this is to account for old devicetrees with one-cell ++ * interrupt specifiers, only one output domain is needed. + */ + oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller"); +- oirq.args_count = 1; +- oirq.args[0] = 2; +- +- parent_irq = irq_create_of_mapping(&oirq); ++ if (oirq.np) { ++ oirq.args_count = 1; ++ oirq.args[0] = 2; ++ ++ parent_irqs[0] = irq_create_of_mapping(&oirq); ++ num_parents = 1; ++ } + + of_node_put(oirq.np); +- } else { +- parent_irq = of_irq_get(node, 0); + } + +- if (parent_irq < 0) +- return parent_irq; +- else if (!parent_irq) +- return -ENODEV; +- +- domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL); +- if (!domain) +- return -ENOMEM; +- +- irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, domain); ++ /* Ensure we haven't collected any errors before proceeding */ ++ for (p = 0; p < num_parents; p++) { ++ if (parent_irqs[p] < 0) ++ return parent_irqs[p]; ++ if (!parent_irqs[p]) ++ return -ENODEV; ++ } + +- return 0; ++ return setup_parent_interrupts(node, &parent_irqs[0], num_parents); + } + + IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init); diff --git a/target/linux/realtek/patches-6.12/318-add-rtl83xx-clk-support.patch b/target/linux/realtek/patches-6.12/318-add-rtl83xx-clk-support.patch new file mode 100644 index 0000000000..e75260a638 --- /dev/null +++ b/target/linux/realtek/patches-6.12/318-add-rtl83xx-clk-support.patch @@ -0,0 +1,33 @@ +From 800d5fb3c6a16661932c932bacd660e38d06b727 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Thu, 25 Aug 2022 08:22:36 +0200 +Subject: [PATCH] realtek: add patch to enable new clock driver in kernel + +Allow building the clock driver with kernel config options. + +Submitted-by: Markus Stockhausen +--- + drivers/clk/Kconfig | 1 + + drivers/clk/Makefile | 1 + + 2 files changed, 2 insertions(+) + +--- a/drivers/clk/Kconfig ++++ b/drivers/clk/Kconfig +@@ -484,6 +484,7 @@ source "drivers/clk/mvebu/Kconfig" + source "drivers/clk/nuvoton/Kconfig" + source "drivers/clk/pistachio/Kconfig" + source "drivers/clk/qcom/Kconfig" ++source "drivers/clk/realtek/Kconfig" + source "drivers/clk/ralink/Kconfig" + source "drivers/clk/renesas/Kconfig" + source "drivers/clk/rockchip/Kconfig" +--- a/drivers/clk/Makefile ++++ b/drivers/clk/Makefile +@@ -112,6 +112,7 @@ obj-$(CONFIG_COMMON_CLK_PISTACHIO) += pi + obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ + obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ + obj-y += ralink/ ++obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/ + obj-y += renesas/ + obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ + obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ diff --git a/target/linux/realtek/patches-6.12/330-add-realtek-thernal-driver.patch b/target/linux/realtek/patches-6.12/330-add-realtek-thernal-driver.patch new file mode 100644 index 0000000000..af550a8c20 --- /dev/null +++ b/target/linux/realtek/patches-6.12/330-add-realtek-thernal-driver.patch @@ -0,0 +1,21 @@ +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -522,4 +522,11 @@ config LOONGSON2_THERMAL + is higher than the high temperature threshold or lower than the low + temperature threshold, the interrupt will occur. + ++config REALTEK_THERMAL ++ tristate "Realtek RTL838x and RTL930x thermal sensor support" ++ depends on RTL838X || RTL839X || RTL930X || COMPILE_TEST ++ depends on THERMAL_OF ++ help ++ Support thermal sensor in Realtek RTL838x, RTL839x and RTL930x SoCs ++ + endif +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -64,3 +64,4 @@ obj-$(CONFIG_AMLOGIC_THERMAL) += aml + obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o + obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o + obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o ++obj-$(CONFIG_REALTEK_THERMAL) += realtek-thermal.o diff --git a/target/linux/realtek/patches-6.12/700-dsa-mdio-increase-max-ports-for-rtl839x-rtl931x.patch b/target/linux/realtek/patches-6.12/700-dsa-mdio-increase-max-ports-for-rtl839x-rtl931x.patch new file mode 100644 index 0000000000..147e2c587b --- /dev/null +++ b/target/linux/realtek/patches-6.12/700-dsa-mdio-increase-max-ports-for-rtl839x-rtl931x.patch @@ -0,0 +1,132 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: realtek dsa/phy: Increase max ports for RTL839X/RTL931X + +Linux standard can only support up to 32 devices per mdio bus and up to +12 ports per DSA switch. This is not enough for the large RTL839X and +RTL931X devices. Increase the max values accordingly. Additionally take +care about the functions that work on bit masks. + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +Signed-off-by: Markus Stockhausen +--- + drivers/net/mdio/fwnode_mdio.c | 2 +- + include/linux/phy.h | 6 +++--- + include/linux/platform_data/dsa.h | 2 +- + include/net/dsa.h | 14 +++++++------- + net/dsa/slave.c | 4 ++-- + 5 files changed, 14 insertions(+), 14 deletions(-) + +--- a/drivers/net/mdio/fwnode_mdio.c ++++ b/drivers/net/mdio/fwnode_mdio.c +@@ -87,7 +87,7 @@ int fwnode_mdiobus_phy_device_register(s + } + + if (fwnode_property_read_bool(child, "broken-turn-around")) +- mdio->phy_ignore_ta_mask |= 1 << addr; ++ mdio->phy_ignore_ta_mask |= BIT_ULL(addr); + + fwnode_property_read_u32(child, "reset-assert-us", + &phy->mdio.reset_assert_delay); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -294,7 +294,7 @@ static inline const char *phy_modes(phy_ + #define PHY_INIT_TIMEOUT 100000 + #define PHY_FORCE_TIMEOUT 10 + +-#define PHY_MAX_ADDR 32 ++#define PHY_MAX_ADDR 64 + + /* Used when trying to connect to a specific phy (mii bus id:phy device id) */ + #define PHY_ID_FMT "%s:%02x" +@@ -414,10 +414,10 @@ struct mii_bus { + struct mdio_device *mdio_map[PHY_MAX_ADDR]; + + /** @phy_mask: PHY addresses to be ignored when probing */ +- u32 phy_mask; ++ u64 phy_mask; + + /** @phy_ignore_ta_mask: PHY addresses to ignore the TA/read failure */ +- u32 phy_ignore_ta_mask; ++ u64 phy_ignore_ta_mask; + + /** + * @irq: An array of interrupts, each PHY's interrupt at the index +--- a/include/linux/platform_data/dsa.h ++++ b/include/linux/platform_data/dsa.h +@@ -6,7 +6,7 @@ struct device; + struct net_device; + + #define DSA_MAX_SWITCHES 4 +-#define DSA_MAX_PORTS 12 ++#define DSA_MAX_PORTS 54 + #define DSA_RTABLE_NONE -1 + + struct dsa_chip_data { +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -465,7 +465,7 @@ struct dsa_switch { + /* + * Slave mii_bus and devices for the individual ports. + */ +- u32 phys_mii_mask; ++ u64 phys_mii_mask; + struct mii_bus *slave_mii_bus; + + /* Ageing Time limits in msecs */ +@@ -597,24 +597,24 @@ static inline bool dsa_is_user_port(stru + dsa_switch_for_each_port_continue_reverse((_dp), (_ds)) \ + if (dsa_port_is_cpu((_dp))) + +-static inline u32 dsa_user_ports(struct dsa_switch *ds) ++static inline u64 dsa_user_ports(struct dsa_switch *ds) + { + struct dsa_port *dp; +- u32 mask = 0; ++ u64 mask = 0; + + dsa_switch_for_each_user_port(dp, ds) +- mask |= BIT(dp->index); ++ mask |= BIT_ULL(dp->index); + + return mask; + } + +-static inline u32 dsa_cpu_ports(struct dsa_switch *ds) ++static inline u64 dsa_cpu_ports(struct dsa_switch *ds) + { + struct dsa_port *cpu_dp; +- u32 mask = 0; ++ u64 mask = 0; + + dsa_switch_for_each_cpu_port(cpu_dp, ds) +- mask |= BIT(cpu_dp->index); ++ mask |= BIT_ULL(cpu_dp->index); + + return mask; + } +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -320,7 +320,7 @@ static int dsa_slave_phy_read(struct mii + { + struct dsa_switch *ds = bus->priv; + +- if (ds->phys_mii_mask & (1 << addr)) ++ if (ds->phys_mii_mask & BIT_ULL(addr)) + return ds->ops->phy_read(ds, addr, reg); + + return 0xffff; +@@ -330,7 +330,7 @@ static int dsa_slave_phy_write(struct mi + { + struct dsa_switch *ds = bus->priv; + +- if (ds->phys_mii_mask & (1 << addr)) ++ if (ds->phys_mii_mask & BIT_ULL(addr)) + return ds->ops->phy_write(ds, addr, reg, val); + + return 0; diff --git a/target/linux/realtek/patches-6.12/702-include-linux-add-phy-hsgmii-mode.patch b/target/linux/realtek/patches-6.12/702-include-linux-add-phy-hsgmii-mode.patch new file mode 100644 index 0000000000..a43b49c1ff --- /dev/null +++ b/target/linux/realtek/patches-6.12/702-include-linux-add-phy-hsgmii-mode.patch @@ -0,0 +1,79 @@ +From 9d9bf16aa8d966834ac1280f96c37d22552c33d1 Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Wed, 8 Sep 2021 16:13:18 +0200 +Subject: realtek phy: Add PHY hsgmii mode + +This adds RTL93xx-specific MAC configuration routines that allow also configuration +of 10GBit links for phylink. There is support for the Realtek-specific HSGMII +protocol. + +Submitted-by: Birger Koblitz +--- + drivers/net/phy/phy-core.c | 1 + + drivers/net/phy/phylink.c | 4 ++++ + include/linux/phy.h | 3 +++ + 3 files changed, 8 insertions(+) + +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -126,6 +126,7 @@ int phy_interface_num_ports(phy_interfac + case PHY_INTERFACE_MODE_MOCA: + case PHY_INTERFACE_MODE_TRGMII: + case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_HSGMII: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_SMII: + case PHY_INTERFACE_MODE_1000BASEX: +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -124,6 +124,7 @@ do { \ + static const phy_interface_t phylink_sfp_interface_preference[] = { + PHY_INTERFACE_MODE_25GBASER, + PHY_INTERFACE_MODE_USXGMII, ++ PHY_INTERFACE_MODE_HSGMII, + PHY_INTERFACE_MODE_10GBASER, + PHY_INTERFACE_MODE_5GBASER, + PHY_INTERFACE_MODE_2500BASEX, +@@ -238,6 +239,7 @@ static int phylink_interface_max_speed(p + + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_HSGMII: + case PHY_INTERFACE_MODE_XAUI: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_10GKR: +@@ -547,6 +549,7 @@ unsigned long phylink_get_capabilities(p + break; + + case PHY_INTERFACE_MODE_XGMII: ++ case PHY_INTERFACE_MODE_HSGMII: + case PHY_INTERFACE_MODE_RXAUI: + case PHY_INTERFACE_MODE_XAUI: + case PHY_INTERFACE_MODE_10GBASER: +@@ -957,6 +960,7 @@ static int phylink_parse_mode(struct phy + fallthrough; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10GKR: ++ case PHY_INTERFACE_MODE_HSGMII: + case PHY_INTERFACE_MODE_10GBASER: + phylink_set(pl->supported, 10baseT_Half); + phylink_set(pl->supported, 10baseT_Full); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -148,6 +148,7 @@ typedef enum { + PHY_INTERFACE_MODE_XGMII, + PHY_INTERFACE_MODE_XLGMII, + PHY_INTERFACE_MODE_MOCA, ++ PHY_INTERFACE_MODE_HSGMII, + PHY_INTERFACE_MODE_PSGMII, + PHY_INTERFACE_MODE_QSGMII, + PHY_INTERFACE_MODE_TRGMII, +@@ -256,6 +257,8 @@ static inline const char *phy_modes(phy_ + return "xlgmii"; + case PHY_INTERFACE_MODE_MOCA: + return "moca"; ++ case PHY_INTERFACE_MODE_HSGMII: ++ return "hsgmii"; + case PHY_INTERFACE_MODE_PSGMII: + return "psgmii"; + case PHY_INTERFACE_MODE_QSGMII: diff --git a/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch b/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch new file mode 100644 index 0000000000..3404d55ed3 --- /dev/null +++ b/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch @@ -0,0 +1,32 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: phy: Add PHY ops for rtl838x EEE + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + include/linux/phy.h | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1181,6 +1181,8 @@ struct phy_driver { + */ + int (*led_polarity_set)(struct phy_device *dev, int index, + unsigned long modes); ++ int (*get_eee)(struct phy_device *dev, struct ethtool_eee *e); ++ int (*set_eee)(struct phy_device *dev, struct ethtool_eee *e); + }; + #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ + struct phy_driver, mdiodrv) diff --git a/target/linux/realtek/patches-6.12/708-drivers-net-phy-eee-support-for-rtl838x.patch b/target/linux/realtek/patches-6.12/708-drivers-net-phy-eee-support-for-rtl838x.patch new file mode 100644 index 0000000000..44461992b1 --- /dev/null +++ b/target/linux/realtek/patches-6.12/708-drivers-net-phy-eee-support-for-rtl838x.patch @@ -0,0 +1,49 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: net: phy: EEE support for rtl838x + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + drivers/net/phy/phylink. | 14 +++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -2805,8 +2805,11 @@ int phylink_ethtool_get_eee(struct phyli + + ASSERT_RTNL(); + +- if (pl->phydev) ++ if (pl->phydev) { ++ if (pl->phydev->drv->get_eee) ++ return pl->phydev->drv->get_eee(pl->phydev, eee); + ret = phy_ethtool_get_eee(pl->phydev, eee); ++ } + + return ret; + } +@@ -2823,8 +2826,11 @@ int phylink_ethtool_set_eee(struct phyli + + ASSERT_RTNL(); + +- if (pl->phydev) ++ if (pl->phydev) { ++ if (pl->phydev->drv->set_eee) ++ return pl->phydev->drv->set_eee(pl->phydev, eee); + ret = phy_ethtool_set_eee(pl->phydev, eee); ++ } + + return ret; + } diff --git a/target/linux/realtek/patches-6.12/712-net-phy-add-an-MDIO-SMBus-library.patch b/target/linux/realtek/patches-6.12/712-net-phy-add-an-MDIO-SMBus-library.patch new file mode 100644 index 0000000000..229e2612ea --- /dev/null +++ b/target/linux/realtek/patches-6.12/712-net-phy-add-an-MDIO-SMBus-library.patch @@ -0,0 +1,428 @@ +From d585c55b9f70cf9e8c66820d7efe7130c683f19e Mon Sep 17 00:00:00 2001 +From: Antoine Tenart +Date: Fri, 21 Feb 2020 11:51:27 +0100 +Subject: [PATCH 2/3] net: phy: add an MDIO SMBus library + +Signed-off-by: Antoine Tenart +--- + drivers/net/mdio/Kconfig | 11 +++++++ + drivers/net/mdio/Makefile | 1 + + drivers/net/mdio/mdio-smbus.c | 62 +++++++++++++++++++++++++++++++++++ + drivers/net/phy/Kconfig | 1 + + include/linux/mdio/mdio-i2c.h | 16 +++++++++ + 5 files changed, 91 insertions(+) + create mode 100644 drivers/net/mdio/mdio-smbus.c + +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -54,6 +54,17 @@ config MDIO_SUN4I + interface units of the Allwinner SoC that have an EMAC (A10, + A12, A10s, etc.) + ++config MDIO_SMBUS ++ tristate ++ depends on I2C_SMBUS ++ help ++ Support SMBus based PHYs. This provides a MDIO bus bridged ++ to SMBus to allow PHYs connected in SMBus mode to be accessed ++ using the existing infrastructure. ++ ++ This is library mode. ++ ++ + config MDIO_XGENE + tristate "APM X-Gene SoC MDIO bus controller" + depends on ARCH_XGENE || COMPILE_TEST +--- a/drivers/net/mdio/Makefile ++++ b/drivers/net/mdio/Makefile +@@ -20,6 +20,7 @@ obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-ms + obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o + obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o + obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o ++obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o + obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o + obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o + obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o +--- /dev/null ++++ b/drivers/net/mdio/mdio-smbus.c +@@ -0,0 +1,341 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * MDIO SMBus bridge ++ * ++ * Copyright (C) 2020 Antoine Tenart ++ * Copyright (C) 2025 Bjørn Mork ++ * ++ * Network PHYs can appear on SMBus when they are part of SFP modules. ++ */ ++#include ++#include ++#include ++#include ++ ++static int smbus_mii_read_c45(struct mii_bus *mii, int phy_id, int devad, int reg) ++{ ++ u16 bus_addr = i2c_mii_phy_addr(phy_id); ++ struct i2c_adapter *i2c = mii->priv; ++ union i2c_smbus_data data; ++ size_t addrlen; ++ u8 buf[5], *p; ++ int i, ret; ++ ++ if (!i2c_mii_valid_phy_id(phy_id)) ++ return 0xffff; ++ ++ p = buf; ++ if (devad >= 0) { ++ *p++ = 0x20 | devad; ++ *p++ = reg >> 8; ++ } ++ *p++ = reg; ++ addrlen = p - buf; ++ ++ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT); ++ if (addrlen > 1) { ++ for (i = 1; i < addrlen; i++) { ++ data.byte = buf[i]; ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, buf[0], I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ goto unlock; ++ } ++ } ++ ++ for (i = addrlen; i < addrlen + 2; i++) { ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, buf[0], I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ goto unlock; ++ buf[i] = data.byte; ++ } ++ ++unlock: ++ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); ++ if (ret < 0) ++ return 0xffff; ++ return buf[addrlen] << 8 | buf[addrlen + 1]; ++} ++ ++static int smbus_mii_write_c45(struct mii_bus *mii, int phy_id, int devad, int reg, u16 val) ++{ ++ u16 bus_addr = i2c_mii_phy_addr(phy_id); ++ struct i2c_adapter *i2c = mii->priv; ++ union i2c_smbus_data data; ++ size_t buflen; ++ u8 buf[5], *p; ++ int i, ret; ++ ++ if (!i2c_mii_valid_phy_id(phy_id)) ++ return 0; ++ ++ p = buf; ++ if (devad >= 0) { ++ *p++ = devad; ++ *p++ = reg >> 8; ++ } ++ *p++ = reg; ++ *p++ = val >> 8; ++ *p++ = val; ++ buflen = p - buf; ++ ++ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT); ++ for (i = 1; i < buflen; i++) { ++ data.byte = buf[i]; ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, buf[0], I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ goto unlock; ++ } ++unlock: ++ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); ++ return ret < 0 ? ret : 0; ++} ++ ++static int smbus_mii_read_c22(struct mii_bus *bus, int phy_id, int reg) ++{ ++ return smbus_mii_read_c45(bus, phy_id, -1, reg); ++} ++ ++static int smbus_mii_write_c22(struct mii_bus *bus, int phy_id, int reg, u16 val) ++{ ++ return smbus_mii_write_c45(bus, phy_id, -1, reg, val); ++} ++ ++/* From mdio-i2c.c: ++ * ++ * RollBall SFPs do not access internal PHY via I2C address 0x56, but ++ * instead via address 0x51, when SFP page is set to 0x03 and password to ++ * 0xffffffff. ++ * ++ * address size contents description ++ * ------- ---- -------- ----------- ++ * 0x80 1 CMD 0x01/0x02/0x04 for write/read/done ++ * 0x81 1 DEV Clause 45 device ++ * 0x82 2 REG Clause 45 register ++ * 0x84 2 VAL Register value ++ */ ++#define ROLLBALL_PHY_I2C_ADDR 0x51 ++ ++#define ROLLBALL_PASSWORD (SFP_VSL + 3) ++ ++#define ROLLBALL_CMD_ADDR 0x80 ++#define ROLLBALL_DATA_ADDR 0x81 ++ ++#define ROLLBALL_CMD_WRITE 0x01 ++#define ROLLBALL_CMD_READ 0x02 ++#define ROLLBALL_CMD_DONE 0x04 ++ ++#define SFP_PAGE_ROLLBALL_MDIO 3 ++ ++static int smbus_set_sfp_page_lock(struct i2c_adapter *i2c, int bus_addr, u8 page) ++{ ++ union i2c_smbus_data data; ++ u8 oldpage; ++ int ret; ++ ++ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT); ++ ++ /* read current page */ ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, SFP_PAGE, I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ goto unlock; ++ ++ oldpage = data.byte; ++ data.byte = page; ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, SFP_PAGE, I2C_SMBUS_BYTE_DATA, &data); ++ if (ret == 0) ++ return oldpage; ++ ++unlock: ++ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); ++ ++ return ret; ++} ++ ++static int __smbus_set_sfp_page_unlock(struct i2c_adapter *i2c, int bus_addr, u8 page) ++{ ++ union i2c_smbus_data data; ++ int ret; ++ ++ data.byte = page; ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, SFP_PAGE, I2C_SMBUS_BYTE_DATA, &data); ++ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); ++ ++ return ret; ++} ++ ++/* Wait for the ROLLBALL_CMD_ADDR register to read ROLLBALL_CMD_DONE, ++ * indicating that the previous command has completed. ++ * ++ * Quoting from the mdio-i2c.c implementation: ++ * ++ * By experiment it takes up to 70 ms to access a register for these ++ * SFPs. Sleep 20ms between iterations and try 10 times. ++ */ ++static int __smbus_rollball_mii_poll(struct i2c_adapter *i2c , int bus_addr) ++{ ++ union i2c_smbus_data data; ++ int i, ret; ++ ++ i = 10; ++ do { ++ msleep(20); ++ ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, ROLLBALL_CMD_ADDR, I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ return ret; ++ ++ if (data.byte == ROLLBALL_CMD_DONE) ++ return 0; ++ } while (i-- > 0); ++ dev_dbg(&i2c->dev, "poll timed out\n"); ++ return -ETIMEDOUT; ++} ++ ++static int smbus_mii_read_rollball(struct mii_bus *bus, int phy_id, int devad, int reg) ++{ ++ struct i2c_adapter *i2c = bus->priv; ++ union i2c_smbus_data data; ++ int i, bus_addr, old, ret; ++ u8 buf[6]; ++ ++ bus_addr = i2c_mii_phy_addr(phy_id); ++ if (bus_addr != ROLLBALL_PHY_I2C_ADDR) ++ return 0xffff; ++ ++ old = smbus_set_sfp_page_lock(i2c, bus_addr, SFP_PAGE_ROLLBALL_MDIO); ++ if (old < 0) ++ return 0xffff; ++ ++ /* set address */ ++ buf[0] = ROLLBALL_CMD_READ; ++ buf[1] = devad; ++ buf[2] = reg >> 8; ++ buf[3] = reg & 0xff; ++ ++ /* send address */ ++ for (i = 0; i < 4; i++) { ++ data.byte = buf[i]; ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, ROLLBALL_CMD_ADDR + i, I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ goto unlock; ++ } ++ ++ /* wait for command to complete */ ++ ret = __smbus_rollball_mii_poll(i2c, bus_addr); ++ if (ret) ++ goto unlock; ++ ++ /* read result */ ++ for (i = 4; i < 6; i++) { ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, ROLLBALL_CMD_ADDR + i, I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ goto unlock; ++ buf[i] = data.byte; ++ } ++ ++unlock: ++ __smbus_set_sfp_page_unlock(i2c, bus_addr, old); ++ if (ret < 0) ++ return 0xffff; ++ return buf[4] << 8 | buf[5]; ++} ++ ++static int smbus_mii_write_rollball(struct mii_bus *bus, int phy_id, int devad, int reg, u16 val) ++{ ++ struct i2c_adapter *i2c = bus->priv; ++ union i2c_smbus_data data; ++ int i, bus_addr, old, ret; ++ u8 buf[6]; ++ ++ bus_addr = i2c_mii_phy_addr(phy_id); ++ if (bus_addr != ROLLBALL_PHY_I2C_ADDR) ++ return 0; ++ ++ old = smbus_set_sfp_page_lock(i2c, bus_addr, SFP_PAGE_ROLLBALL_MDIO); ++ if (old < 0) ++ return old; ++ ++ /* set address */ ++ buf[0] = ROLLBALL_CMD_WRITE; ++ buf[1] = devad; ++ buf[2] = reg >> 8; ++ buf[3] = reg & 0xff; ++ buf[4] = val >> 8; ++ buf[5] = val & 0xff; ++ ++ /* send address and value */ ++ for (i = 0; i < 6; i++) { ++ data.byte = buf[i]; ++ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, ROLLBALL_CMD_ADDR + i, I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ goto unlock; ++ } ++ ++ /* wait for command to complete */ ++ ret = __smbus_rollball_mii_poll(i2c, bus_addr); ++ ++unlock: ++ __smbus_set_sfp_page_unlock(i2c, bus_addr, old); ++ return ret; ++} ++ ++/* write "password" - four 0xff bytes - to the ROLLBALL_PASSWORD register */ ++static int smbus_mii_init_rollball(struct i2c_adapter *i2c) ++{ ++ union i2c_smbus_data data; ++ int i, ret; ++ ++ data.byte = 0xff; ++ for (i = 0; i < 4; i++) { ++ ret = i2c_smbus_xfer(i2c, ROLLBALL_PHY_I2C_ADDR, 0, I2C_SMBUS_WRITE, ROLLBALL_PASSWORD + i, I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ return ret; ++ } ++ return 0; ++} ++ ++struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c, ++ enum mdio_i2c_proto protocol) ++{ ++ struct mii_bus *mii; ++ int ret; ++ ++ if (!i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return ERR_PTR(-EINVAL); ++ ++ mii = mdiobus_alloc(); ++ if (!mii) ++ return ERR_PTR(-ENOMEM); ++ ++ snprintf(mii->id, MII_BUS_ID_SIZE, "smbus:%s", dev_name(parent)); ++ mii->parent = parent; ++ mii->priv = i2c; ++ ++ switch (protocol) { ++ case MDIO_I2C_ROLLBALL: ++ ret = smbus_mii_init_rollball(i2c); ++ if (ret < 0) { ++ dev_err(parent, ++ "Cannot initialize RollBall MDIO protocol on SMBus: %d\n", ++ ret); ++ mdiobus_free(mii); ++ return ERR_PTR(ret); ++ } ++ ++ mii->read_c45 = smbus_mii_read_rollball; ++ mii->write_c45 = smbus_mii_write_rollball; ++ break; ++ default: ++ mii->read = smbus_mii_read_c22; ++ mii->write = smbus_mii_write_c22; ++ mii->read_c45 = smbus_mii_read_c45; ++ mii->write_c45 = smbus_mii_write_c45; ++ break; ++ } ++ ++ return mii; ++} ++ ++MODULE_AUTHOR("Antoine Tenart"); ++MODULE_DESCRIPTION("MDIO SMBus bridge library"); ++MODULE_LICENSE("GPL"); +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -65,6 +65,7 @@ config SFP + depends on I2C && PHYLINK + depends on HWMON || HWMON=n + select MDIO_I2C ++ select MDIO_SMBUS + + comment "Switch configuration API + drivers" + +--- a/include/linux/mdio/mdio-i2c.h ++++ b/include/linux/mdio/mdio-i2c.h +@@ -20,5 +20,9 @@ enum mdio_i2c_proto { + + struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c, + enum mdio_i2c_proto protocol); ++struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c, ++ enum mdio_i2c_proto protocol); ++bool i2c_mii_valid_phy_id(int phy_id); ++unsigned int i2c_mii_phy_addr(int phy_id); + + #endif +--- a/drivers/net/mdio/mdio-i2c.c ++++ b/drivers/net/mdio/mdio-i2c.c +@@ -20,12 +20,12 @@ + * specified to be present in SFP modules. These correspond with PHY + * addresses 16 and 17. Disallow access to these "phy" addresses. + */ +-static bool i2c_mii_valid_phy_id(int phy_id) ++bool i2c_mii_valid_phy_id(int phy_id) + { + return phy_id != 0x10 && phy_id != 0x11; + } + +-static unsigned int i2c_mii_phy_addr(int phy_id) ++unsigned int i2c_mii_phy_addr(int phy_id) + { + return phy_id + 0x40; + } diff --git a/target/linux/realtek/patches-6.12/714-net-phy-sfp-add-support-for-SMBus.patch b/target/linux/realtek/patches-6.12/714-net-phy-sfp-add-support-for-SMBus.patch new file mode 100644 index 0000000000..4fb1e7f293 --- /dev/null +++ b/target/linux/realtek/patches-6.12/714-net-phy-sfp-add-support-for-SMBus.patch @@ -0,0 +1,126 @@ +From 3cb0bde365d913c484d20224367a54a0eac780a7 Mon Sep 17 00:00:00 2001 +From: Antoine Tenart +Date: Fri, 21 Feb 2020 11:55:29 +0100 +Subject: [PATCH 3/3] net: phy: sfp: add support for SMBus + +Signed-off-by: Antoine Tenart +--- + drivers/net/phy/sfp.c | 92 +++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 88 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -675,10 +675,64 @@ static int sfp_i2c_write(struct sfp *sfp + return ret == ARRAY_SIZE(msgs) ? len : 0; + } + ++static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, ++ size_t len) ++{ ++ u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf; ++ union i2c_smbus_data data; ++ int ret; ++ ++ bus_addr -= 0x40; ++ ++ while (len > 0) { ++ ret = i2c_smbus_xfer(sfp->i2c, i2c_mii_phy_addr(bus_addr), 0, ++ I2C_SMBUS_READ, dev_addr, ++ I2C_SMBUS_BYTE_DATA, &data); ++ if (ret) ++ return ret; ++ *val++ = data.byte; ++ dev_addr++; ++ len--; ++ } ++ ++ return val - (u8 *)buf; ++} ++ ++static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, ++ size_t len) ++{ ++ u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf; ++ union i2c_smbus_data data; ++ int ret; ++ ++ bus_addr -= 0x40; ++ ++ while (len > 0) { ++ data.byte = *val++; ++ ret = i2c_smbus_xfer(sfp->i2c, i2c_mii_phy_addr(bus_addr), 0, ++ I2C_SMBUS_WRITE, dev_addr, ++ I2C_SMBUS_BYTE_DATA, &data); ++ if (ret) ++ return ret; ++ dev_addr++; ++ len--; ++ } ++ ++ return val - (u8 *)buf; ++} ++ + static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) + { +- if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) +- return -EINVAL; ++ if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) { ++ if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ sfp->i2c = i2c; ++ sfp->read = sfp_smbus_read; ++ sfp->write = sfp_smbus_write; ++ ++ return 0; ++ } else ++ return -EINVAL; ++ } + + sfp->i2c = i2c; + sfp->read = sfp_i2c_read; +@@ -710,6 +764,29 @@ static int sfp_i2c_mdiobus_create(struct + return 0; + } + ++static int sfp_sm_mdiobus_create(struct sfp *sfp) ++{ ++ struct mii_bus *sm_mii; ++ int ret; ++ ++ sm_mii = mdio_smbus_alloc(sfp->dev, sfp->i2c, sfp->mdio_protocol); ++ if (IS_ERR(sm_mii)) ++ return PTR_ERR(sm_mii); ++ ++ sm_mii->name = "SFP SMBus"; ++ sm_mii->phy_mask = ~0; ++ ++ ret = mdiobus_register(sm_mii); ++ if (ret < 0) { ++ mdiobus_free(sm_mii); ++ return ret; ++ } ++ ++ sfp->i2c_mii = sm_mii; ++ ++ return 0; ++} ++ + static void sfp_i2c_mdiobus_destroy(struct sfp *sfp) + { + mdiobus_unregister(sfp->i2c_mii); +@@ -1884,9 +1961,15 @@ static void sfp_sm_fault(struct sfp *sfp + + static int sfp_sm_add_mdio_bus(struct sfp *sfp) + { +- if (sfp->mdio_protocol != MDIO_I2C_NONE) ++ if (sfp->mdio_protocol == MDIO_I2C_NONE) ++ return 0; ++ ++ if (i2c_check_functionality(sfp->i2c, I2C_FUNC_I2C)) + return sfp_i2c_mdiobus_create(sfp); + ++ if (i2c_check_functionality(sfp->i2c, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return sfp_sm_mdiobus_create(sfp); ++ + return 0; + } + diff --git a/target/linux/realtek/patches-6.12/716-net-ethernet-add-support-for-rtl838x-ethernet.patch b/target/linux/realtek/patches-6.12/716-net-ethernet-add-support-for-rtl838x-ethernet.patch new file mode 100644 index 0000000000..e0c10bca63 --- /dev/null +++ b/target/linux/realtek/patches-6.12/716-net-ethernet-add-support-for-rtl838x-ethernet.patch @@ -0,0 +1,48 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: net: ethernet: Add support for RTL838x ethernet + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + drivers/net/ethernet/Kconfig | 7 +- + drivers/net/ethernet/Makefile | 1 + + 2 files changed, 8 insertions(+) + +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -170,6 +170,13 @@ source "drivers/net/ethernet/rdc/Kconfig + source "drivers/net/ethernet/realtek/Kconfig" + source "drivers/net/ethernet/renesas/Kconfig" + source "drivers/net/ethernet/rocker/Kconfig" ++ ++config NET_RTL838X ++ tristate "Realtek rtl838x Ethernet MAC support" ++ depends on MACH_REALTEK_RTL ++ help ++ Say Y here if you want to use the Realtek rtl838x Gbps Ethernet MAC. ++ + source "drivers/net/ethernet/samsung/Kconfig" + source "drivers/net/ethernet/seeq/Kconfig" + source "drivers/net/ethernet/sgi/Kconfig" +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -81,6 +81,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += real + obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/ + obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ + obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/ ++obj-$(CONFIG_NET_RTL838X) += rtl838x_eth.o + obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/ + obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/ + obj-$(CONFIG_NET_VENDOR_SILAN) += silan/ diff --git a/target/linux/realtek/patches-6.12/718-net-dsa-add-support-for-rtl838x-switch.patch b/target/linux/realtek/patches-6.12/718-net-dsa-add-support-for-rtl838x-switch.patch new file mode 100644 index 0000000000..786fc37091 --- /dev/null +++ b/target/linux/realtek/patches-6.12/718-net-dsa-add-support-for-rtl838x-switch.patch @@ -0,0 +1,42 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: net: dsa: Add support for rtl838x switch + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + drivers/net/dsa/rtl83xx/Kconfig | 2 ++ + drivers/net/dsa/rtl83xx/Makefile | 1 + + 2 files changed, 3 insertions(+) + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -89,6 +89,8 @@ source "drivers/net/dsa/xrs700x/Kconfig" + + source "drivers/net/dsa/realtek/Kconfig" + ++source "drivers/net/dsa/rtl83xx/Kconfig" ++ + config NET_DSA_RZN1_A5PSW + tristate "Renesas RZ/N1 A5PSW Ethernet switch support" + depends on OF && ARCH_RZN1 +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -24,5 +24,6 @@ obj-y += mv88e6xxx/ + obj-y += ocelot/ + obj-y += qca/ + obj-y += realtek/ ++obj-y += rtl83xx/ + obj-y += sja1105/ + obj-y += xrs700x/ diff --git a/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch b/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch new file mode 100644 index 0000000000..c7078cea4e --- /dev/null +++ b/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch @@ -0,0 +1,39 @@ +From 89f71ebb355c624320c2b0ace8ae9488ff53cbeb Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Tue, 5 Jan 2021 20:40:52 +0100 +Subject: PHY: Add realtek PHY + +This fixes the build problems for the REALTEK target by adding a proper +configuration option for the phy module. + +Submitted-by: Birger Koblitz +--- + drivers/net/phy/Kconfig | 6 ++++++ + drivers/net/phy/Makefile | 1 + + 2 files changed, 7 insertions(+) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -409,6 +409,12 @@ source "drivers/net/phy/realtek/Kconfig" + + source "drivers/net/phy/rtl8261n/Kconfig" + ++config REALTEK_SOC_PHY ++ tristate "Realtek SoC PHYs" ++ depends on MACH_REALTEK_RTL ++ help ++ Supports the PHYs found in combination with Realtek Switch SoCs ++ + config RENESAS_PHY + tristate "Renesas PHYs" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -101,6 +101,7 @@ obj-y += qcom/ + obj-$(CONFIG_QSEMI_PHY) += qsemi.o + obj-$(CONFIG_REALTEK_PHY) += realtek/ + obj-y += rtl8261n/ ++obj-$(CONFIG_REALTEK_SOC_PHY) += rtl83xx-phy.o + obj-$(CONFIG_RENESAS_PHY) += uPD60620.o + obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o + obj-$(CONFIG_SMSC_PHY) += smsc.o diff --git a/target/linux/realtek/patches-6.12/722-net-dsa-add-rtl838x-support-for-tag-trailer.patch b/target/linux/realtek/patches-6.12/722-net-dsa-add-rtl838x-support-for-tag-trailer.patch new file mode 100644 index 0000000000..348c59092a --- /dev/null +++ b/target/linux/realtek/patches-6.12/722-net-dsa-add-rtl838x-support-for-tag-trailer.patch @@ -0,0 +1,61 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: net: dsa: Add rtl838x support for tag trailer + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + net/dsa/tag_trailer.c | 16 +++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +--- a/net/dsa/tag_trailer.c ++++ b/net/dsa/tag_trailer.c +@@ -19,7 +19,12 @@ static struct sk_buff *trailer_xmit(stru + + trailer = skb_put(skb, 4); + trailer[0] = 0x80; ++ ++#ifdef CONFIG_NET_DSA_RTL83XX ++ trailer[1] = dp->index; ++#else + trailer[1] = 1 << dp->index; ++#endif /* CONFIG_NET_DSA_RTL838X */ + trailer[2] = 0x10; + trailer[3] = 0x00; + +@@ -35,12 +40,23 @@ static struct sk_buff *trailer_rcv(struc + return NULL; + + trailer = skb_tail_pointer(skb) - 4; ++ ++#ifdef CONFIG_NET_DSA_RTL83XX ++ if (trailer[0] != 0x80 || (trailer[1] & 0x80) != 0x00 || ++ (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) ++ return NULL; ++ ++ if (trailer[1] & 0x40) ++ skb->offload_fwd_mark = 1; ++ ++ source_port = trailer[1] & 0x3f; ++#else + if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 || + (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) + return NULL; + + source_port = trailer[1] & 7; +- ++#endif + skb->dev = dsa_master_find_slave(dev, 0, source_port); + if (!skb->dev) + return NULL; diff --git a/target/linux/realtek/patches-6.12/723-net-mdio-Add-Realtek-Otto-auxiliary-controller.patch b/target/linux/realtek/patches-6.12/723-net-mdio-Add-Realtek-Otto-auxiliary-controller.patch new file mode 100644 index 0000000000..7315899b3f --- /dev/null +++ b/target/linux/realtek/patches-6.12/723-net-mdio-Add-Realtek-Otto-auxiliary-controller.patch @@ -0,0 +1,227 @@ +From ffb7da9aa25765b2115e7ff3ee4f6dafa60f5421 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Fri, 27 Dec 2024 14:55:31 +0100 +Subject: [PATCH] net: mdio: Add Realtek Otto auxiliary controller + +SoCs in Realtek's Otto platform such as the RTL8380, RTL8391, and +RTL9302 have a simple auxiliary MDIO controller that is commonly used to +manage RTL8231 GPIO expanders on switch devices. + +Add a new MDIO controller driver supporting the RTL838x (maple), RTL839x +(cypress), and RTL930x (longan) SoCs. + +Signed-off-by: Sander Vanheule +--- + drivers/net/mdio/Kconfig | 10 ++ + drivers/net/mdio/Makefile | 1 + + drivers/net/mdio/mdio-realtek-otto-aux.c | 175 +++++++++++++++++++++++ + 3 files changed, 186 insertions(+) + create mode 100644 drivers/net/mdio/mdio-realtek-otto-aux.c + +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -207,6 +207,16 @@ config MDIO_REGMAP + regmap. Users willing to use this driver must explicitly select + REGMAP. + ++config MDIO_REALTEK_OTTO_AUX ++ tristate "Realtek Otto auxiliary MDIO interface support" ++ default MACH_REALTEK_RTL ++ depends on MACH_REALTEK_RTL ++ depends on MFD_SYSCON ++ select MDIO_DEVRES ++ help ++ This driver supports the auxilairy MDIO bus on RTL838x SoCs. This bus ++ is typically used to attach RTL8231 GPIO extenders. ++ + config MDIO_THUNDER + tristate "ThunderX SOCs MDIO buses" + depends on 64BIT +--- a/drivers/net/mdio/Makefile ++++ b/drivers/net/mdio/Makefile +@@ -20,6 +20,7 @@ obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-ms + obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o + obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o + obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o ++obj-$(CONFIG_MDIO_REALTEK_OTTO_AUX) += mdio-realtek-otto-aux.o + obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o + obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o + obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o +--- /dev/null ++++ b/drivers/net/mdio/mdio-realtek-otto-aux.c +@@ -0,0 +1,175 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RTL8380_EXT_GPIO_INDIRECT_ACCESS 0xA09C ++#define RTL8390_EXT_GPIO_INDIRECT_ACCESS 0x0224 ++#define RTL9300_EXT_GPIO_INDIRECT_ACCESS 0xC620 ++ ++#define RTL83XX_AUX_MDIO_DATA_OFFSET 16 ++#define RTL83XX_AUX_MDIO_RCMD_FAIL 0 ++ ++#define RTL93XX_AUX_MDIO_DATA_OFFSET 12 ++#define RTL93XX_AUX_MDIO_RCMD_FAIL BIT(28) ++ ++#define REALTEK_AUX_MDIO_REG GENMASK(11, 7) ++#define REALTEK_AUX_MDIO_PHY_ADDR GENMASK(6, 2) ++#define REALTEK_AUX_MDIO_WRITE BIT(1) ++#define REALTEK_AUX_MDIO_READ 0 ++#define REALTEK_AUX_MDIO_EXEC BIT(0) ++ ++struct realtek_aux_mdio_info { ++ unsigned int cmd_reg; ++ unsigned int data_offset; ++ unsigned int rcmd_fail_mask; ++ unsigned int timeout_us; ++}; ++ ++static const struct realtek_aux_mdio_info info_rtl838x = { ++ .cmd_reg = RTL8380_EXT_GPIO_INDIRECT_ACCESS, ++ .data_offset = RTL83XX_AUX_MDIO_DATA_OFFSET, ++ .rcmd_fail_mask = RTL83XX_AUX_MDIO_RCMD_FAIL, ++ .timeout_us = 1700, ++}; ++ ++static const struct realtek_aux_mdio_info info_rtl839x = { ++ .cmd_reg = RTL8390_EXT_GPIO_INDIRECT_ACCESS, ++ .data_offset = RTL83XX_AUX_MDIO_DATA_OFFSET, ++ .rcmd_fail_mask = RTL83XX_AUX_MDIO_RCMD_FAIL, ++ .timeout_us = 4120, ++}; ++ ++static const struct realtek_aux_mdio_info info_rtl930x = { ++ .cmd_reg = RTL9300_EXT_GPIO_INDIRECT_ACCESS, ++ .data_offset = RTL93XX_AUX_MDIO_DATA_OFFSET, ++ .rcmd_fail_mask = RTL93XX_AUX_MDIO_RCMD_FAIL, ++ .timeout_us = 19000, ++}; ++ ++struct realtek_aux_mdio_ctrl { ++ struct device *dev; ++ struct regmap *map; ++ const struct realtek_aux_mdio_info *info; ++}; ++ ++#define mii_bus_to_ctrl(bus) ((struct realtek_aux_mdio_ctrl *) bus->priv) ++ ++static int realtek_aux_mdio_cmd(struct realtek_aux_mdio_ctrl *ctrl, int addr, int regnum, ++ u32 rw_bit, u16 *data) ++{ ++ unsigned int cmd; ++ int err; ++ ++ cmd = rw_bit | REALTEK_AUX_MDIO_EXEC; ++ cmd |= FIELD_PREP(REALTEK_AUX_MDIO_PHY_ADDR, addr); ++ cmd |= FIELD_PREP(REALTEK_AUX_MDIO_REG, regnum); ++ ++ if (rw_bit == REALTEK_AUX_MDIO_WRITE) ++ cmd |= *data << ctrl->info->data_offset; ++ ++ err = regmap_write(ctrl->map, ctrl->info->cmd_reg, cmd); ++ if (err) ++ return err; ++ ++ err = regmap_read_poll_timeout_atomic(ctrl->map, ctrl->info->cmd_reg, cmd, ++ !(cmd & REALTEK_AUX_MDIO_EXEC), 3, ctrl->info->timeout_us); ++ if (err) ++ return err; ++ ++ if (rw_bit == REALTEK_AUX_MDIO_READ) { ++ if (cmd & ctrl->info->rcmd_fail_mask) ++ return -EIO; ++ ++ *data = (cmd >> ctrl->info->data_offset) & GENMASK(15, 0); ++ } ++ ++ return 0; ++} ++ ++static int realtek_aux_mdio_read(struct mii_bus *bus, int addr, int regnum) ++{ ++ struct realtek_aux_mdio_ctrl *ctrl = mii_bus_to_ctrl(bus); ++ u16 data; ++ int err; ++ ++ err = realtek_aux_mdio_cmd(ctrl, addr, regnum, REALTEK_AUX_MDIO_READ, &data); ++ ++ if (err) ++ return err; ++ else ++ return data; ++} ++ ++static int realtek_aux_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) ++{ ++ struct realtek_aux_mdio_ctrl *ctrl = mii_bus_to_ctrl(bus); ++ ++ return realtek_aux_mdio_cmd(ctrl, addr, regnum, REALTEK_AUX_MDIO_WRITE, &val); ++} ++ ++static int realtek_aux_mdio_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct realtek_aux_mdio_ctrl *ctrl; ++ struct mii_bus *bus; ++ ++ bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*ctrl)); ++ if (!bus) ++ return -ENOMEM; ++ ++ ctrl = bus->priv; ++ ctrl->dev = &pdev->dev; ++ ctrl->info = (const struct realtek_aux_mdio_info *) device_get_match_data(ctrl->dev); ++ ctrl->map = syscon_node_to_regmap(np->parent); ++ if (IS_ERR(ctrl->map)) ++ return PTR_ERR(ctrl->map); ++ ++ bus->name = "Realtek auxiliary MDIO bus"; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "realtek-aux-mdio") ; ++ bus->parent = ctrl->dev; ++ bus->read = realtek_aux_mdio_read; ++ bus->write = realtek_aux_mdio_write; ++ /* Don't have interrupts */ ++ for (unsigned int i = 0; i < PHY_MAX_ADDR; i++) ++ bus->irq[i] = PHY_POLL; ++ ++ return devm_of_mdiobus_register(ctrl->dev, bus, np); ++} ++ ++static const struct of_device_id realtek_aux_mdio_of_match[] = { ++ { ++ .compatible = "realtek,rtl8380-aux-mdio", ++ .data = &info_rtl838x, ++ }, ++ { ++ .compatible = "realtek,rtl8390-aux-mdio", ++ .data = &info_rtl839x, ++ }, ++ { ++ .compatible = "realtek,rtl9300-aux-mdio", ++ .data = &info_rtl930x, ++ }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, realtek_aux_mdio_of_match); ++ ++static struct platform_driver realtek_aux_mdio_driver = { ++ .driver = { ++ .name = "realtek-otto-aux-mdio", ++ .of_match_table = realtek_aux_mdio_of_match ++ }, ++ .probe = realtek_aux_mdio_probe, ++}; ++module_platform_driver(realtek_aux_mdio_driver); ++ ++MODULE_AUTHOR("Sander Vanheule "); ++MODULE_DESCRIPTION("Realtek otto auxiliary MDIO bus"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/realtek/patches-6.12/800-gpio-regmap-Bypass-cache-for-shadowed-outputs.patch b/target/linux/realtek/patches-6.12/800-gpio-regmap-Bypass-cache-for-shadowed-outputs.patch new file mode 100644 index 0000000000..b4dbf320b2 --- /dev/null +++ b/target/linux/realtek/patches-6.12/800-gpio-regmap-Bypass-cache-for-shadowed-outputs.patch @@ -0,0 +1,56 @@ +From b3f79468c90d8770f007d628a1e32b2d5d44a5c2 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Sat, 15 May 2021 11:57:32 +0200 +Subject: [PATCH] gpio: regmap: Bypass cache for shadowed outputs + +Some chips have the read-only input and write-only output data registers +aliased to the same offset, but do not perform direction multiplexing on +writes. Upon writing the register, this then always updates the output +value, even when the pin is configured as input. As a result it is not +safe to perform read-modify-writes on output pins, when other pins are +still configured as input. + +For example, on a bit-banged I2C bus, where the lines are switched +between out-low and in (with external pull-up) + + OUT(L) IN OUT(H) +SCK ....../''''''|'''''' + +SDA '''''''''\.......... + ^ ^- SCK switches to direction to OUT, but now has a high + | value, breaking the clock. + | + \- Perform RMW to update SDA. This reads the current input + value for SCK, updates the SDA value and writes back a 1 + for SCK as well. + +If a register is used for both the data input and data output (and is +not marked as volatile) the driver should ensure the cache is not +updated on register reads. This ensures proper functioning of writing +the output register with regmap_update_bits(), which will then use and +update the cache only on register writes. + +Signed-off-by: Sander Vanheule +--- + drivers/gpio/gpio-regmap.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/gpio/gpio-regmap.c ++++ b/drivers/gpio/gpio-regmap.c +@@ -74,7 +74,15 @@ static int gpio_regmap_get(struct gpio_c + if (ret) + return ret; + +- ret = regmap_read(gpio->regmap, reg, &val); ++ /* ++ * Ensure we don't spoil the register cache with pin input values and ++ * perform a bypassed read. This way the cache (if any) is only used and ++ * updated on register writes. ++ */ ++ if (gpio->reg_dat_base == gpio->reg_set_base) ++ ret = regmap_read_bypassed(gpio->regmap, reg, &val); ++ else ++ ret = regmap_read(gpio->regmap, reg, &val); + if (ret) + return ret; + diff --git a/target/linux/realtek/patches-6.12/802-mfd-Add-RTL8231-core-device.patch b/target/linux/realtek/patches-6.12/802-mfd-Add-RTL8231-core-device.patch new file mode 100644 index 0000000000..df4d4aa143 --- /dev/null +++ b/target/linux/realtek/patches-6.12/802-mfd-Add-RTL8231-core-device.patch @@ -0,0 +1,330 @@ +From 4e3455e058d40eb2a7326016494e3c81dc506c33 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Mon, 10 May 2021 18:33:01 +0200 +Subject: [PATCH] mfd: Add RTL8231 core device + +The RTL8231 is implemented as an MDIO device, and provides a regmap +interface for register access by the core and child devices. + +The chip can also be a device on an SMI bus, an I2C-like bus by Realtek. +Since kernel support for SMI is limited, and no real-world SMI +implementations have been encountered for this device, this is currently +unimplemented. The use of the regmap interface should make any future +support relatively straightforward. + +After reset, all pins are muxed to GPIO inputs before the pin drivers +are enabled. This is done to prevent accidental system resets, when a +pin is connected to the parent SoC's reset line. + +To provide different read and write semantics for the GPIO data +registers, a secondary virtual register range is used to enable separate +caching properties of pin input and output values. + +Signed-off-by: Sander Vanheule +--- + drivers/mfd/Kconfig | 9 ++ + drivers/mfd/Makefile | 1 + + drivers/mfd/rtl8231.c | 193 ++++++++++++++++++++++++++++++++++++ + include/linux/mfd/rtl8231.h | 71 +++++++++++++ + 4 files changed, 274 insertions(+) + create mode 100644 drivers/mfd/rtl8231.c + create mode 100644 include/linux/mfd/rtl8231.h + +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -1171,6 +1171,15 @@ config MFD_RDC321X + southbridge which provides access to GPIOs and Watchdog using the + southbridge PCI device configuration space. + ++config MFD_RTL8231 ++ tristate "Realtek RTL8231 GPIO and LED expander" ++ select MFD_CORE ++ select REGMAP_MDIO ++ help ++ Support for the Realtek RTL8231 GPIO and LED expander. ++ Provides up to 37 GPIOs, 88 LEDs, and one PWM output. ++ When built as a module, this module will be named rtl8231. ++ + config MFD_RT4831 + tristate "Richtek RT4831 four channel WLED and Display Bias Voltage" + depends on I2C +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -240,6 +240,7 @@ obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421- + obj-$(CONFIG_MFD_HI6421_SPMI) += hi6421-spmi-pmic.o + obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o + obj-$(CONFIG_MFD_DLN2) += dln2.o ++obj-$(CONFIG_MFD_RTL8231) += rtl8231.o + obj-$(CONFIG_MFD_RT4831) += rt4831.o + obj-$(CONFIG_MFD_RT5033) += rt5033.o + obj-$(CONFIG_MFD_RT5120) += rt5120.o +--- /dev/null ++++ b/drivers/mfd/rtl8231.c +@@ -0,0 +1,193 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static bool rtl8231_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ /* ++ * Registers with self-clearing bits, strapping pin values. ++ * Don't mark the data registers as volatile, since we need ++ * caching for the output values. ++ */ ++ case RTL8231_REG_FUNC0: ++ case RTL8231_REG_FUNC1: ++ case RTL8231_REG_PIN_HI_CFG: ++ case RTL8231_REG_LED_END: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static const struct reg_field RTL8231_FIELD_LED_START = REG_FIELD(RTL8231_REG_FUNC0, 1, 1); ++ ++static const struct mfd_cell rtl8231_cells[] = { ++ { ++ .name = "rtl8231-pinctrl", ++ }, ++ { ++ .name = "rtl8231-leds", ++ .of_compatible = "realtek,rtl8231-leds", ++ }, ++}; ++ ++static int rtl8231_soft_reset(struct regmap *map) ++{ ++ const unsigned int all_pins_mask = GENMASK(RTL8231_BITS_VAL - 1, 0); ++ unsigned int val; ++ int err; ++ ++ /* SOFT_RESET bit self-clears when done */ ++ regmap_write_bits(map, RTL8231_REG_PIN_HI_CFG, ++ RTL8231_PIN_HI_CFG_SOFT_RESET, RTL8231_PIN_HI_CFG_SOFT_RESET); ++ err = regmap_read_poll_timeout(map, RTL8231_REG_PIN_HI_CFG, val, ++ !(val & RTL8231_PIN_HI_CFG_SOFT_RESET), 50, 1000); ++ if (err) ++ return err; ++ ++ regcache_mark_dirty(map); ++ ++ /* ++ * Chip reset results in a pin configuration that is a mix of LED and GPIO outputs. ++ * Select GPI functionality for all pins before enabling pin outputs. ++ */ ++ regmap_write(map, RTL8231_REG_PIN_MODE0, all_pins_mask); ++ regmap_write(map, RTL8231_REG_GPIO_DIR0, all_pins_mask); ++ regmap_write(map, RTL8231_REG_PIN_MODE1, all_pins_mask); ++ regmap_write(map, RTL8231_REG_GPIO_DIR1, all_pins_mask); ++ regmap_write(map, RTL8231_REG_PIN_HI_CFG, ++ RTL8231_PIN_HI_CFG_MODE_MASK | RTL8231_PIN_HI_CFG_DIR_MASK); ++ ++ return 0; ++} ++ ++static int rtl8231_init(struct device *dev, struct regmap *map) ++{ ++ struct regmap_field *led_start; ++ unsigned int started; ++ unsigned int val; ++ int err; ++ ++ err = regmap_read(map, RTL8231_REG_FUNC1, &val); ++ if (err) { ++ dev_err(dev, "failed to read READY_CODE\n"); ++ return err; ++ } ++ ++ val = FIELD_GET(RTL8231_FUNC1_READY_CODE_MASK, val); ++ if (val != RTL8231_FUNC1_READY_CODE_VALUE) { ++ dev_err(dev, "RTL8231 not present or ready 0x%x != 0x%x\n", ++ val, RTL8231_FUNC1_READY_CODE_VALUE); ++ return -ENODEV; ++ } ++ ++ led_start = dev_get_drvdata(dev); ++ err = regmap_field_read(led_start, &started); ++ if (err) ++ return err; ++ ++ if (!started) { ++ err = rtl8231_soft_reset(map); ++ if (err) ++ return err; ++ /* LED_START enables power to output pins, and starts the LED engine */ ++ err = regmap_field_force_write(led_start, 1); ++ } ++ ++ return err; ++} ++ ++static const struct regmap_config rtl8231_mdio_regmap_config = { ++ .val_bits = RTL8231_BITS_VAL, ++ .reg_bits = RTL8231_BITS_REG, ++ .volatile_reg = rtl8231_volatile_reg, ++ .max_register = RTL8231_REG_COUNT - 1, ++ .use_single_read = true, ++ .use_single_write = true, ++ .reg_format_endian = REGMAP_ENDIAN_BIG, ++ .val_format_endian = REGMAP_ENDIAN_BIG, ++ /* Cannot use REGCACHE_FLAT because it's not smart enough about cache invalidation */ ++ .cache_type = REGCACHE_RBTREE, ++}; ++ ++static int rtl8231_mdio_probe(struct mdio_device *mdiodev) ++{ ++ struct device *dev = &mdiodev->dev; ++ struct regmap_field *led_start; ++ struct regmap *map; ++ int err; ++ ++ map = devm_regmap_init_mdio(mdiodev, &rtl8231_mdio_regmap_config); ++ if (IS_ERR(map)) { ++ dev_err(dev, "failed to init regmap\n"); ++ return PTR_ERR(map); ++ } ++ ++ led_start = devm_regmap_field_alloc(dev, map, RTL8231_FIELD_LED_START); ++ if (IS_ERR(led_start)) ++ return PTR_ERR(led_start); ++ ++ dev_set_drvdata(dev, led_start); ++ ++ mdiodev->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(mdiodev->reset_gpio)) ++ return PTR_ERR(mdiodev->reset_gpio); ++ ++ device_property_read_u32(dev, "reset-assert-delay", &mdiodev->reset_assert_delay); ++ device_property_read_u32(dev, "reset-deassert-delay", &mdiodev->reset_deassert_delay); ++ ++ err = rtl8231_init(dev, map); ++ if (err) ++ return err; ++ ++ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, rtl8231_cells, ++ ARRAY_SIZE(rtl8231_cells), NULL, 0, NULL); ++} ++ ++__maybe_unused static int rtl8231_suspend(struct device *dev) ++{ ++ struct regmap_field *led_start = dev_get_drvdata(dev); ++ ++ return regmap_field_force_write(led_start, 0); ++} ++ ++__maybe_unused static int rtl8231_resume(struct device *dev) ++{ ++ struct regmap_field *led_start = dev_get_drvdata(dev); ++ ++ return regmap_field_force_write(led_start, 1); ++} ++ ++static SIMPLE_DEV_PM_OPS(rtl8231_pm_ops, rtl8231_suspend, rtl8231_resume); ++ ++static const struct of_device_id rtl8231_of_match[] = { ++ { .compatible = "realtek,rtl8231" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, rtl8231_of_match); ++ ++static struct mdio_driver rtl8231_mdio_driver = { ++ .mdiodrv.driver = { ++ .name = "rtl8231-expander", ++ .of_match_table = rtl8231_of_match, ++ .pm = pm_ptr(&rtl8231_pm_ops), ++ }, ++ .probe = rtl8231_mdio_probe, ++}; ++mdio_module_driver(rtl8231_mdio_driver); ++ ++MODULE_AUTHOR("Sander Vanheule "); ++MODULE_DESCRIPTION("Realtek RTL8231 GPIO and LED expander"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/linux/mfd/rtl8231.h +@@ -0,0 +1,71 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Register definitions the RTL8231 GPIO and LED expander chip ++ */ ++ ++#ifndef __LINUX_MFD_RTL8231_H ++#define __LINUX_MFD_RTL8231_H ++ ++#include ++ ++/* ++ * Registers addresses are 5 bit, values are 16 bit ++ * Also define a duplicated range of virtual addresses, to enable ++ * different read/write behaviour on the GPIO data registers ++ */ ++#define RTL8231_BITS_VAL 16 ++#define RTL8231_BITS_REG 5 ++ ++/* Chip control */ ++#define RTL8231_REG_FUNC0 0x00 ++#define RTL8231_FUNC0_SCAN_MODE BIT(0) ++#define RTL8231_FUNC0_SCAN_SINGLE 0 ++#define RTL8231_FUNC0_SCAN_BICOLOR BIT(0) ++ ++#define RTL8231_REG_FUNC1 0x01 ++#define RTL8231_FUNC1_READY_CODE_VALUE 0x37 ++#define RTL8231_FUNC1_READY_CODE_MASK GENMASK(9, 4) ++#define RTL8231_FUNC1_DEBOUNCE_MASK GENMASK(15, 10) ++ ++/* Pin control */ ++#define RTL8231_REG_PIN_MODE0 0x02 ++#define RTL8231_REG_PIN_MODE1 0x03 ++ ++#define RTL8231_PIN_MODE_LED 0 ++#define RTL8231_PIN_MODE_GPIO 1 ++ ++/* Pin high config: pin and GPIO control for pins 32-26 */ ++#define RTL8231_REG_PIN_HI_CFG 0x04 ++#define RTL8231_PIN_HI_CFG_MODE_MASK GENMASK(4, 0) ++#define RTL8231_PIN_HI_CFG_DIR_MASK GENMASK(9, 5) ++#define RTL8231_PIN_HI_CFG_INV_MASK GENMASK(14, 10) ++#define RTL8231_PIN_HI_CFG_SOFT_RESET BIT(15) ++ ++/* GPIO control registers */ ++#define RTL8231_REG_GPIO_DIR0 0x05 ++#define RTL8231_REG_GPIO_DIR1 0x06 ++#define RTL8231_REG_GPIO_INVERT0 0x07 ++#define RTL8231_REG_GPIO_INVERT1 0x08 ++ ++#define RTL8231_GPIO_DIR_IN 1 ++#define RTL8231_GPIO_DIR_OUT 0 ++ ++/* ++ * GPIO data registers ++ * Only the output data can be written to these registers, and only the input ++ * data can be read. ++ */ ++#define RTL8231_REG_GPIO_DATA0 0x1c ++#define RTL8231_REG_GPIO_DATA1 0x1d ++#define RTL8231_REG_GPIO_DATA2 0x1e ++#define RTL8231_PIN_HI_DATA_MASK GENMASK(4, 0) ++ ++/* LED control base registers */ ++#define RTL8231_REG_LED0_BASE 0x09 ++#define RTL8231_REG_LED1_BASE 0x10 ++#define RTL8231_REG_LED2_BASE 0x17 ++#define RTL8231_REG_LED_END 0x1b ++ ++#define RTL8231_REG_COUNT 0x1f ++ ++#endif /* __LINUX_MFD_RTL8231_H */ diff --git a/target/linux/realtek/patches-6.12/803-pinctrl-Add-RTL8231-pin-control-and-GPIO-support.patch b/target/linux/realtek/patches-6.12/803-pinctrl-Add-RTL8231-pin-control-and-GPIO-support.patch new file mode 100644 index 0000000000..de0f5ee868 --- /dev/null +++ b/target/linux/realtek/patches-6.12/803-pinctrl-Add-RTL8231-pin-control-and-GPIO-support.patch @@ -0,0 +1,581 @@ +From 098324288a63a6dcc44e96cc381aef3d5c48d89e Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Mon, 10 May 2021 22:15:31 +0200 +Subject: [PATCH] pinctrl: Add RTL8231 pin control and GPIO support + +This driver implements the GPIO and pin muxing features provided by the +RTL8231. The device should be instantiated as an MFD child, where the +parent device has already configured the regmap used for register +access. + +Debouncing is only available for the six highest GPIOs, and must be +emulated when other pins are used for (button) inputs. Although +described in the bindings, drive strength selection is currently not +implemented. + +Signed-off-by: Sander Vanheule +--- + drivers/pinctrl/Kconfig | 11 + + drivers/pinctrl/Makefile | 1 + + drivers/pinctrl/pinctrl-rtl8231.c | 521 ++++++++++++++++++++++++++++++ + 3 files changed, 533 insertions(+) + create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c + +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -417,6 +417,17 @@ config PINCTRL_ROCKCHIP + help + This support pinctrl and GPIO driver for Rockchip SoCs. + ++config PINCTRL_RTL8231 ++ tristate "Realtek RTL8231 GPIO expander's pin controller" ++ depends on MFD_RTL8231 ++ default MFD_RTL8231 ++ select GPIO_REGMAP ++ select GENERIC_PINCONF ++ select GENERIC_PINMUX_FUNCTIONS ++ help ++ Support for RTL8231 expander's GPIOs and pin controller. ++ When built as a module, the module will be called pinctrl-rtl8231. ++ + config PINCTRL_SINGLE + tristate "One-register-per-pin type device tree based pinctrl driver" + depends on OF +--- a/drivers/pinctrl/Makefile ++++ b/drivers/pinctrl/Makefile +@@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-p + obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o + obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o + obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o ++obj-$(CONFIG_PINCTRL_RTL8231) += pinctrl-rtl8231.o + obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o + obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o + obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o +--- /dev/null ++++ b/drivers/pinctrl/pinctrl-rtl8231.c +@@ -0,0 +1,525 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++#include "pinmux.h" ++#include ++ ++#define RTL8231_NUM_GPIOS 37 ++#define RTL8231_DEBOUNCE_USEC 100000 ++#define RTL8231_DEBOUNCE_MIN_OFFSET 31 ++ ++struct rtl8231_pin_ctrl { ++ struct pinctrl_desc pctl_desc; ++ struct regmap *map; ++}; ++ ++/* ++ * Pin controller functionality ++ */ ++static const char * const rtl8231_pin_function_names[] = { ++ "gpio", ++ "led", ++ "pwm", ++}; ++ ++enum rtl8231_pin_function { ++ RTL8231_PIN_FUNCTION_GPIO = BIT(0), ++ RTL8231_PIN_FUNCTION_LED = BIT(1), ++ RTL8231_PIN_FUNCTION_PWM = BIT(2), ++}; ++ ++struct rtl8231_pin_desc { ++ const enum rtl8231_pin_function functions; ++ const u8 reg; ++ const u8 offset; ++ const u8 gpio_function_value; ++}; ++ ++#define RTL8231_PIN_DESC(_num, _func, _reg, _fld, _val) \ ++ [_num] = { \ ++ .functions = RTL8231_PIN_FUNCTION_GPIO | _func, \ ++ .reg = _reg, \ ++ .offset = _fld, \ ++ .gpio_function_value = _val, \ ++ } ++#define RTL8231_GPIO_PIN_DESC(_num, _reg, _fld) \ ++ RTL8231_PIN_DESC(_num, 0, _reg, _fld, RTL8231_PIN_MODE_GPIO) ++#define RTL8231_LED_PIN_DESC(_num, _reg, _fld) \ ++ RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_LED, _reg, _fld, RTL8231_PIN_MODE_GPIO) ++#define RTL8231_PWM_PIN_DESC(_num, _reg, _fld) \ ++ RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_PWM, _reg, _fld, 0) ++ ++/* ++ * All pins have a GPIO/LED mux bit, but the bits for pins 35/36 are read-only. Use this bit ++ * for the GPIO-only pin instead of a placeholder, so the rest of the logic can stay generic. ++ */ ++static struct rtl8231_pin_desc rtl8231_pin_data[RTL8231_NUM_GPIOS] = { ++ RTL8231_LED_PIN_DESC(0, RTL8231_REG_PIN_MODE0, 0), ++ RTL8231_LED_PIN_DESC(1, RTL8231_REG_PIN_MODE0, 1), ++ RTL8231_LED_PIN_DESC(2, RTL8231_REG_PIN_MODE0, 2), ++ RTL8231_LED_PIN_DESC(3, RTL8231_REG_PIN_MODE0, 3), ++ RTL8231_LED_PIN_DESC(4, RTL8231_REG_PIN_MODE0, 4), ++ RTL8231_LED_PIN_DESC(5, RTL8231_REG_PIN_MODE0, 5), ++ RTL8231_LED_PIN_DESC(6, RTL8231_REG_PIN_MODE0, 6), ++ RTL8231_LED_PIN_DESC(7, RTL8231_REG_PIN_MODE0, 7), ++ RTL8231_LED_PIN_DESC(8, RTL8231_REG_PIN_MODE0, 8), ++ RTL8231_LED_PIN_DESC(9, RTL8231_REG_PIN_MODE0, 9), ++ RTL8231_LED_PIN_DESC(10, RTL8231_REG_PIN_MODE0, 10), ++ RTL8231_LED_PIN_DESC(11, RTL8231_REG_PIN_MODE0, 11), ++ RTL8231_LED_PIN_DESC(12, RTL8231_REG_PIN_MODE0, 12), ++ RTL8231_LED_PIN_DESC(13, RTL8231_REG_PIN_MODE0, 13), ++ RTL8231_LED_PIN_DESC(14, RTL8231_REG_PIN_MODE0, 14), ++ RTL8231_LED_PIN_DESC(15, RTL8231_REG_PIN_MODE0, 15), ++ RTL8231_LED_PIN_DESC(16, RTL8231_REG_PIN_MODE1, 0), ++ RTL8231_LED_PIN_DESC(17, RTL8231_REG_PIN_MODE1, 1), ++ RTL8231_LED_PIN_DESC(18, RTL8231_REG_PIN_MODE1, 2), ++ RTL8231_LED_PIN_DESC(19, RTL8231_REG_PIN_MODE1, 3), ++ RTL8231_LED_PIN_DESC(20, RTL8231_REG_PIN_MODE1, 4), ++ RTL8231_LED_PIN_DESC(21, RTL8231_REG_PIN_MODE1, 5), ++ RTL8231_LED_PIN_DESC(22, RTL8231_REG_PIN_MODE1, 6), ++ RTL8231_LED_PIN_DESC(23, RTL8231_REG_PIN_MODE1, 7), ++ RTL8231_LED_PIN_DESC(24, RTL8231_REG_PIN_MODE1, 8), ++ RTL8231_LED_PIN_DESC(25, RTL8231_REG_PIN_MODE1, 9), ++ RTL8231_LED_PIN_DESC(26, RTL8231_REG_PIN_MODE1, 10), ++ RTL8231_LED_PIN_DESC(27, RTL8231_REG_PIN_MODE1, 11), ++ RTL8231_LED_PIN_DESC(28, RTL8231_REG_PIN_MODE1, 12), ++ RTL8231_LED_PIN_DESC(29, RTL8231_REG_PIN_MODE1, 13), ++ RTL8231_LED_PIN_DESC(30, RTL8231_REG_PIN_MODE1, 14), ++ RTL8231_LED_PIN_DESC(31, RTL8231_REG_PIN_MODE1, 15), ++ RTL8231_LED_PIN_DESC(32, RTL8231_REG_PIN_HI_CFG, 0), ++ RTL8231_LED_PIN_DESC(33, RTL8231_REG_PIN_HI_CFG, 1), ++ RTL8231_LED_PIN_DESC(34, RTL8231_REG_PIN_HI_CFG, 2), ++ RTL8231_PWM_PIN_DESC(35, RTL8231_REG_FUNC1, 3), ++ RTL8231_GPIO_PIN_DESC(36, RTL8231_REG_PIN_HI_CFG, 4), ++}; ++ ++#define RTL8231_PIN(_num) \ ++ { \ ++ .number = _num, \ ++ .name = "gpio" #_num, \ ++ .drv_data = &rtl8231_pin_data[_num] \ ++ } ++ ++static const struct pinctrl_pin_desc rtl8231_pins[RTL8231_NUM_GPIOS] = { ++ RTL8231_PIN(0), ++ RTL8231_PIN(1), ++ RTL8231_PIN(2), ++ RTL8231_PIN(3), ++ RTL8231_PIN(4), ++ RTL8231_PIN(5), ++ RTL8231_PIN(6), ++ RTL8231_PIN(7), ++ RTL8231_PIN(8), ++ RTL8231_PIN(9), ++ RTL8231_PIN(10), ++ RTL8231_PIN(11), ++ RTL8231_PIN(12), ++ RTL8231_PIN(13), ++ RTL8231_PIN(14), ++ RTL8231_PIN(15), ++ RTL8231_PIN(16), ++ RTL8231_PIN(17), ++ RTL8231_PIN(18), ++ RTL8231_PIN(19), ++ RTL8231_PIN(20), ++ RTL8231_PIN(21), ++ RTL8231_PIN(22), ++ RTL8231_PIN(23), ++ RTL8231_PIN(24), ++ RTL8231_PIN(25), ++ RTL8231_PIN(26), ++ RTL8231_PIN(27), ++ RTL8231_PIN(28), ++ RTL8231_PIN(29), ++ RTL8231_PIN(30), ++ RTL8231_PIN(31), ++ RTL8231_PIN(32), ++ RTL8231_PIN(33), ++ RTL8231_PIN(34), ++ RTL8231_PIN(35), ++ RTL8231_PIN(36), ++}; ++ ++static int rtl8231_get_groups_count(struct pinctrl_dev *pctldev) ++{ ++ return ARRAY_SIZE(rtl8231_pins); ++} ++ ++static const char *rtl8231_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector) ++{ ++ return rtl8231_pins[selector].name; ++} ++ ++static int rtl8231_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, ++ const unsigned int **pins, unsigned int *num_pins) ++{ ++ if (selector >= ARRAY_SIZE(rtl8231_pins)) ++ return -EINVAL; ++ ++ *pins = &rtl8231_pins[selector].number; ++ *num_pins = 1; ++ ++ return 0; ++} ++ ++static const struct pinctrl_ops rtl8231_pinctrl_ops = { ++ .get_groups_count = rtl8231_get_groups_count, ++ .get_group_name = rtl8231_get_group_name, ++ .get_group_pins = rtl8231_get_group_pins, ++ .dt_node_to_map = pinconf_generic_dt_node_to_map_all, ++ .dt_free_map = pinconf_generic_dt_free_map, ++}; ++ ++static int rtl8231_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, ++ unsigned int group_selector) ++{ ++ const struct function_desc *func = pinmux_generic_get_function(pctldev, func_selector); ++ const struct rtl8231_pin_desc *desc = rtl8231_pins[group_selector].drv_data; ++ const struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); ++ unsigned int func_flag = (uintptr_t) func->data; ++ unsigned int function_mask; ++ unsigned int gpio_function; ++ ++ if (!(desc->functions & func_flag)) ++ return -EINVAL; ++ ++ function_mask = BIT(desc->offset); ++ gpio_function = desc->gpio_function_value << desc->offset; ++ ++ if (func_flag == RTL8231_PIN_FUNCTION_GPIO) ++ return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function); ++ else ++ return regmap_update_bits(ctrl->map, desc->reg, function_mask, ~gpio_function); ++} ++ ++static int rtl8231_gpio_request_enable(struct pinctrl_dev *pctldev, ++ struct pinctrl_gpio_range *range, unsigned int offset) ++{ ++ const struct rtl8231_pin_desc *desc = rtl8231_pins[offset].drv_data; ++ struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); ++ unsigned int function_mask; ++ unsigned int gpio_function; ++ ++ function_mask = BIT(desc->offset); ++ gpio_function = desc->gpio_function_value << desc->offset; ++ ++ return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function); ++} ++ ++static const struct pinmux_ops rtl8231_pinmux_ops = { ++ .get_functions_count = pinmux_generic_get_function_count, ++ .get_function_name = pinmux_generic_get_function_name, ++ .get_function_groups = pinmux_generic_get_function_groups, ++ .set_mux = rtl8231_set_mux, ++ .gpio_request_enable = rtl8231_gpio_request_enable, ++ .strict = true, ++}; ++ ++static int rtl8231_pin_config_get(struct pinctrl_dev *pctldev, unsigned int offset, ++ unsigned long *config) ++{ ++ struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); ++ unsigned int param = pinconf_to_config_param(*config); ++ unsigned int arg; ++ int err; ++ int v; ++ ++ switch (param) { ++ case PIN_CONFIG_INPUT_DEBOUNCE: ++ if (offset < RTL8231_DEBOUNCE_MIN_OFFSET) ++ return -EINVAL; ++ ++ err = regmap_read(ctrl->map, RTL8231_REG_FUNC1, &v); ++ if (err) ++ return err; ++ ++ v = FIELD_GET(RTL8231_FUNC1_DEBOUNCE_MASK, v); ++ if (v & BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET)) ++ arg = RTL8231_DEBOUNCE_USEC; ++ else ++ arg = 0; ++ break; ++ default: ++ return -ENOTSUPP; ++ } ++ ++ *config = pinconf_to_config_packed(param, arg); ++ ++ return 0; ++} ++ ++static int rtl8231_pin_config_set(struct pinctrl_dev *pctldev, unsigned int offset, ++ unsigned long *configs, unsigned int num_configs) ++{ ++ struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); ++ unsigned int param, arg; ++ unsigned int pin_mask; ++ int err; ++ int i; ++ ++ for (i = 0; i < num_configs; i++) { ++ param = pinconf_to_config_param(configs[i]); ++ arg = pinconf_to_config_argument(configs[i]); ++ ++ switch (param) { ++ case PIN_CONFIG_INPUT_DEBOUNCE: ++ if (offset < RTL8231_DEBOUNCE_MIN_OFFSET) ++ return -EINVAL; ++ ++ pin_mask = FIELD_PREP(RTL8231_FUNC1_DEBOUNCE_MASK, ++ BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET)); ++ ++ switch (arg) { ++ case 0: ++ err = regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1, ++ pin_mask, 0); ++ break; ++ case RTL8231_DEBOUNCE_USEC: ++ err = regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1, ++ pin_mask, pin_mask); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ break; ++ default: ++ return -ENOTSUPP; ++ } ++ } ++ ++ return err; ++} ++ ++static const struct pinconf_ops rtl8231_pinconf_ops = { ++ .is_generic = true, ++ .pin_config_get = rtl8231_pin_config_get, ++ .pin_config_set = rtl8231_pin_config_set, ++}; ++ ++static int rtl8231_pinctrl_init_functions(struct pinctrl_dev *pctl, struct rtl8231_pin_ctrl *ctrl) ++{ ++ const char *function_name; ++ const char **groups; ++ unsigned int f_idx; ++ unsigned int pin; ++ int num_groups; ++ int err; ++ ++ for (f_idx = 0; f_idx < ARRAY_SIZE(rtl8231_pin_function_names); f_idx++) { ++ function_name = rtl8231_pin_function_names[f_idx]; ++ ++ for (pin = 0, num_groups = 0; pin < ctrl->pctl_desc.npins; pin++) ++ if (rtl8231_pin_data[pin].functions & BIT(f_idx)) ++ num_groups++; ++ ++ groups = devm_kcalloc(pctl->dev, num_groups, sizeof(*groups), GFP_KERNEL); ++ if (!groups) ++ return -ENOMEM; ++ ++ for (pin = 0, num_groups = 0; pin < ctrl->pctl_desc.npins; pin++) ++ if (rtl8231_pin_data[pin].functions & BIT(f_idx)) ++ groups[num_groups++] = rtl8231_pins[pin].name; ++ ++ err = pinmux_generic_add_function(pctl, function_name, groups, num_groups, ++ (void *) BIT(f_idx)); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++struct pin_field_info { ++ const struct reg_field gpio_data; ++ const struct reg_field gpio_dir; ++ const struct reg_field mode; ++}; ++ ++static const struct pin_field_info pin_fields[] = { ++ { ++ .gpio_data = REG_FIELD(RTL8231_REG_GPIO_DATA0, 0, 15), ++ .gpio_dir = REG_FIELD(RTL8231_REG_GPIO_DIR0, 0, 15), ++ .mode = REG_FIELD(RTL8231_REG_PIN_MODE0, 0, 15), ++ }, ++ { ++ .gpio_data = REG_FIELD(RTL8231_REG_GPIO_DATA1, 0, 15), ++ .gpio_dir = REG_FIELD(RTL8231_REG_GPIO_DIR1, 0, 15), ++ .mode = REG_FIELD(RTL8231_REG_PIN_MODE1, 0, 15), ++ }, ++ { ++ .gpio_data = REG_FIELD(RTL8231_REG_GPIO_DATA2, 0, 4), ++ .gpio_dir = REG_FIELD(RTL8231_REG_PIN_HI_CFG, 5, 9), ++ .mode = REG_FIELD(RTL8231_REG_PIN_HI_CFG, 0, 4), ++ }, ++}; ++ ++static int rtl8231_configure_safe(struct device *dev, struct regmap *map) ++{ ++ struct regmap_field *field_data; ++ struct regmap_field *field_mode; ++ struct regmap_field *field_dir; ++ unsigned int is_output; ++ unsigned int is_gpio; ++ unsigned int data; ++ unsigned int mode; ++ unsigned int dir; ++ int err; ++ ++ for (unsigned int i = 0; i < ARRAY_SIZE(pin_fields); i++) { ++ field_data = devm_regmap_field_alloc(dev, map, pin_fields[i].gpio_data); ++ if (IS_ERR(field_data)) ++ return PTR_ERR(field_data); ++ ++ field_dir = devm_regmap_field_alloc(dev, map, pin_fields[i].gpio_dir); ++ if (IS_ERR(field_dir)) ++ return PTR_ERR(field_dir); ++ ++ field_mode = devm_regmap_field_alloc(dev, map, pin_fields[i].mode); ++ if (IS_ERR(field_mode)) ++ return PTR_ERR(field_mode); ++ ++ /* The register cache is invalid at start-up, so this should read from HW */ ++ err = regmap_field_read(field_data, &data); ++ if (err) ++ return err; ++ ++ err = regmap_field_read(field_dir, &dir); ++ if (err) ++ return err; ++ ++ err = regmap_field_read(field_mode, &mode); ++ if (err) ++ return err; ++ ++ /* Write back only the GPIO-out values to fix the cache */ ++ data &= ~dir; ++ regmap_field_write(field_data, data); ++ ++ /* ++ * Set every pin that is configured as gpio-output but muxed for the alternative ++ * (LED) function to gpio-in. That way the pin will be high impedance when it is ++ * muxed to GPIO, preventing unwanted glitches. ++ * The pin muxes are left as-is, so there are no signal changes. ++ */ ++ is_gpio = mode; ++ is_output = ~dir; ++ regmap_field_write(field_dir, dir | (~is_gpio & is_output)); ++ ++ devm_regmap_field_free(dev, field_data); ++ devm_regmap_field_free(dev, field_dir); ++ devm_regmap_field_free(dev, field_mode); ++ } ++ ++ return 0; ++} ++ ++static int rtl8231_pinctrl_init(struct device *dev, struct rtl8231_pin_ctrl *ctrl) ++{ ++ struct pinctrl_dev *pctldev; ++ int err; ++ ++ ctrl->pctl_desc.name = "rtl8231-pinctrl"; ++ ctrl->pctl_desc.owner = THIS_MODULE; ++ ctrl->pctl_desc.confops = &rtl8231_pinconf_ops; ++ ctrl->pctl_desc.pctlops = &rtl8231_pinctrl_ops; ++ ctrl->pctl_desc.pmxops = &rtl8231_pinmux_ops; ++ ctrl->pctl_desc.npins = ARRAY_SIZE(rtl8231_pins); ++ ctrl->pctl_desc.pins = rtl8231_pins; ++ ++ err = devm_pinctrl_register_and_init(dev->parent, &ctrl->pctl_desc, ctrl, &pctldev); ++ if (err) { ++ dev_err(dev, "failed to register pin controller\n"); ++ return err; ++ } ++ ++ err = rtl8231_pinctrl_init_functions(pctldev, ctrl); ++ if (err) ++ return err; ++ ++ err = pinctrl_enable(pctldev); ++ if (err) ++ dev_err(dev, "failed to enable pin controller\n"); ++ ++ return err; ++} ++ ++/* ++ * GPIO controller functionality ++ */ ++static int rtl8231_gpio_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base, ++ unsigned int offset, unsigned int *reg, unsigned int *mask) ++{ ++ unsigned int pin_mask = BIT(offset % RTL8231_BITS_VAL); ++ ++ if (base == RTL8231_REG_GPIO_DATA0 || offset < 32) { ++ *reg = base + offset / RTL8231_BITS_VAL; ++ *mask = pin_mask; ++ } else if (base == RTL8231_REG_GPIO_DIR0) { ++ *reg = RTL8231_REG_PIN_HI_CFG; ++ *mask = FIELD_PREP(RTL8231_PIN_HI_CFG_DIR_MASK, pin_mask); ++ } else { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rtl8231_pinctrl_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rtl8231_pin_ctrl *ctrl; ++ struct gpio_regmap_config gpio_cfg = {}; ++ int err; ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ ctrl->map = dev_get_regmap(dev->parent, NULL); ++ if (!ctrl->map) ++ return -ENODEV; ++ ++ err = rtl8231_configure_safe(dev, ctrl->map); ++ if (err) ++ return err; ++ ++ err = rtl8231_pinctrl_init(dev, ctrl); ++ if (err) ++ return err; ++ ++ gpio_cfg.regmap = ctrl->map; ++ gpio_cfg.parent = dev->parent; ++ gpio_cfg.ngpio = RTL8231_NUM_GPIOS; ++ gpio_cfg.ngpio_per_reg = RTL8231_BITS_VAL; ++ ++ gpio_cfg.reg_dat_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0); ++ gpio_cfg.reg_set_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0); ++ gpio_cfg.reg_dir_in_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DIR0); ++ ++ gpio_cfg.reg_mask_xlate = rtl8231_gpio_reg_mask_xlate; ++ ++ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_cfg)); ++} ++ ++static struct platform_driver rtl8231_pinctrl_driver = { ++ .driver = { ++ .name = "rtl8231-pinctrl", ++ }, ++ .probe = rtl8231_pinctrl_probe, ++}; ++module_platform_driver(rtl8231_pinctrl_driver); ++ ++MODULE_AUTHOR("Sander Vanheule "); ++MODULE_DESCRIPTION("Realtek RTL8231 pin control and GPIO support"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/realtek/patches-6.12/804-leds-Add-support-for-RTL8231-LED-scan-matrix.patch b/target/linux/realtek/patches-6.12/804-leds-Add-support-for-RTL8231-LED-scan-matrix.patch new file mode 100644 index 0000000000..37542b8230 --- /dev/null +++ b/target/linux/realtek/patches-6.12/804-leds-Add-support-for-RTL8231-LED-scan-matrix.patch @@ -0,0 +1,338 @@ +From 6b797a97c007e46d6081fc6f4b41ce8407078605 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Mon, 10 May 2021 22:16:11 +0200 +Subject: [PATCH] leds: Add support for RTL8231 LED scan matrix + +Both single and bi-color scanning modes are supported. The driver will +verify that the addresses are valid for the current mode, before +registering the LEDs. LEDs can be turned on, off, or toggled at one of +six predefined rates from 40ms to 1280ms. + +Implements a platform device for use as a child device with RTL8231 MFD, +and uses the parent regmap to access the required registers. + +Signed-off-by: Sander Vanheule +--- + drivers/leds/Kconfig | 10 ++ + drivers/leds/Makefile | 1 + + drivers/leds/leds-rtl8231.c | 291 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 302 insertions(+) + create mode 100644 drivers/leds/leds-rtl8231.c + +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -586,6 +586,16 @@ config LEDS_REGULATOR + help + This option enables support for regulator driven LEDs. + ++config LEDS_RTL8231 ++ tristate "RTL8231 LED matrix support" ++ depends on LEDS_CLASS ++ depends on MFD_RTL8231 ++ default MFD_RTL8231 ++ help ++ This option enables support for using the LED scanning matrix output ++ of the RTL8231 GPIO and LED expander chip. ++ When built as a module, this module will be named leds-rtl8231. ++ + config LEDS_BD2606MVV + tristate "LED driver for BD2606MVV" + depends on LEDS_CLASS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -77,6 +77,7 @@ obj-$(CONFIG_LEDS_PM8058) += leds-pm805 + obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o + obj-$(CONFIG_LEDS_PWM) += leds-pwm.o + obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o ++obj-$(CONFIG_LEDS_RTL8231) += leds-rtl8231.o + obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o + obj-$(CONFIG_LEDS_ST1202) += leds-st1202.o + obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o +--- /dev/null ++++ b/drivers/leds/leds-rtl8231.c +@@ -0,0 +1,285 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/** ++ * struct led_toggle_rate - description of an LED blinking mode ++ * @interval_ms: LED toggle rate in milliseconds ++ * @mode: Register field value used to activate this mode ++ * ++ * For LED hardware accelerated blinking, with equal on and off delay. ++ * Both delays are given by @interval, so the interval at which the LED blinks ++ * (i.e. turn on and off once) is double this value. ++ */ ++struct led_toggle_rate { ++ u16 interval_ms; ++ u8 mode; ++}; ++ ++/** ++ * struct led_modes - description of all LED modes ++ * @toggle_rates: Array of led_toggle_rate values, sorted by ascending interval ++ * @num_toggle_rates: Number of elements in @led_toggle_rate ++ * @off: Register field value to turn LED off ++ * @on: Register field value to turn LED on ++ */ ++struct led_modes { ++ const struct led_toggle_rate *toggle_rates; ++ unsigned int num_toggle_rates; ++ u8 off; ++ u8 on; ++}; ++ ++struct rtl8231_led { ++ struct led_classdev led; ++ const struct led_modes *modes; ++ struct regmap_field *reg_field; ++}; ++#define to_rtl8231_led(_cdev) container_of(_cdev, struct rtl8231_led, led) ++ ++#define RTL8231_NUM_LEDS 3 ++#define RTL8231_LED_PER_REG 5 ++#define RTL8231_BITS_PER_LED 3 ++ ++static const unsigned int rtl8231_led_port_counts_single[RTL8231_NUM_LEDS] = {32, 32, 24}; ++static const unsigned int rtl8231_led_port_counts_bicolor[RTL8231_NUM_LEDS] = {24, 24, 24}; ++ ++static const unsigned int rtl8231_led_base[RTL8231_NUM_LEDS] = { ++ RTL8231_REG_LED0_BASE, ++ RTL8231_REG_LED1_BASE, ++ RTL8231_REG_LED2_BASE, ++}; ++ ++#define RTL8231_DEFAULT_TOGGLE_INTERVAL_MS 500 ++ ++static const struct led_toggle_rate rtl8231_toggle_rates[] = { ++ { 40, 1}, ++ { 80, 2}, ++ { 160, 3}, ++ { 320, 4}, ++ { 640, 5}, ++ {1280, 6}, ++}; ++ ++static const struct led_modes rtl8231_led_modes = { ++ .off = 0, ++ .on = 7, ++ .num_toggle_rates = ARRAY_SIZE(rtl8231_toggle_rates), ++ .toggle_rates = rtl8231_toggle_rates, ++}; ++ ++static void rtl8231_led_brightness_set(struct led_classdev *led_cdev, ++ enum led_brightness brightness) ++{ ++ struct rtl8231_led *pled = to_rtl8231_led(led_cdev); ++ ++ if (brightness) ++ regmap_field_write(pled->reg_field, pled->modes->on); ++ else ++ regmap_field_write(pled->reg_field, pled->modes->off); ++} ++ ++static enum led_brightness rtl8231_led_brightness_get(struct led_classdev *led_cdev) ++{ ++ struct rtl8231_led *pled = to_rtl8231_led(led_cdev); ++ u32 current_mode = pled->modes->off; ++ ++ regmap_field_read(pled->reg_field, ¤t_mode); ++ ++ if (current_mode == pled->modes->off) ++ return LED_OFF; ++ else ++ return LED_ON; ++} ++ ++static unsigned int rtl8231_led_current_interval(struct rtl8231_led *pled) ++{ ++ unsigned int mode; ++ unsigned int i; ++ ++ if (regmap_field_read(pled->reg_field, &mode)) ++ return 0; ++ ++ for (i = 0; i < pled->modes->num_toggle_rates; i++) ++ if (mode == pled->modes->toggle_rates[i].mode) ++ return pled->modes->toggle_rates[i].interval_ms; ++ ++ return 0; ++} ++ ++static int rtl8231_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ struct rtl8231_led *pled = to_rtl8231_led(led_cdev); ++ const struct led_toggle_rate *rates = pled->modes->toggle_rates; ++ unsigned int num_rates = pled->modes->num_toggle_rates; ++ unsigned int interval_ms; ++ unsigned int i; ++ int err; ++ ++ if (*delay_on == 0 && *delay_off == 0) { ++ interval_ms = RTL8231_DEFAULT_TOGGLE_INTERVAL_MS; ++ } else { ++ /* ++ * If the current mode is blinking, choose the delay that (likely) changed. ++ * Otherwise, choose the interval that would have the same total delay. ++ */ ++ interval_ms = rtl8231_led_current_interval(pled); ++ if (interval_ms > 0 && interval_ms == *delay_off) ++ interval_ms = *delay_on; ++ else if (interval_ms > 0 && interval_ms == *delay_on) ++ interval_ms = *delay_off; ++ else ++ interval_ms = (*delay_on + *delay_off) / 2; ++ } ++ ++ /* Find clamped toggle interval */ ++ for (i = 0; i < (num_rates - 1); i++) ++ if (interval_ms > rates[i].interval_ms) ++ break; ++ ++ interval_ms = rates[i].interval_ms; ++ ++ err = regmap_field_write(pled->reg_field, rates[i].mode); ++ if (err) ++ return err; ++ ++ *delay_on = interval_ms; ++ *delay_off = interval_ms; ++ ++ return 0; ++} ++ ++static int rtl8231_led_read_address(struct fwnode_handle *fwnode, unsigned int *addr_port, ++ unsigned int *addr_led) ++{ ++ u32 addr[2]; ++ int err; ++ ++ err = fwnode_property_count_u32(fwnode, "reg"); ++ if (err < 0) ++ return err; ++ if (err != ARRAY_SIZE(addr)) ++ return -EINVAL; ++ ++ err = fwnode_property_read_u32_array(fwnode, "reg", addr, ARRAY_SIZE(addr)); ++ if (err) ++ return err; ++ ++ *addr_port = addr[0]; ++ *addr_led = addr[1]; ++ ++ return 0; ++} ++ ++static const struct regmap_field *rtl8231_led_get_field(struct device *dev, struct regmap *map, ++ unsigned int port_index, unsigned int led_index) ++{ ++ unsigned int offset = port_index / RTL8231_LED_PER_REG; ++ unsigned int shift = (port_index % RTL8231_LED_PER_REG) * RTL8231_BITS_PER_LED; ++ const struct reg_field field = REG_FIELD(rtl8231_led_base[led_index] + offset, shift, ++ shift + RTL8231_BITS_PER_LED - 1); ++ ++ return devm_regmap_field_alloc(dev, map, field); ++} ++ ++static int rtl8231_led_probe_single(struct device *dev, struct regmap *map, ++ const unsigned int *port_counts, struct fwnode_handle *fwnode) ++{ ++ struct led_init_data init_data = {}; ++ struct rtl8231_led *pled; ++ unsigned int port_index; ++ unsigned int led_index; ++ int err; ++ ++ pled = devm_kzalloc(dev, sizeof(*pled), GFP_KERNEL); ++ if (!pled) ++ return -ENOMEM; ++ ++ err = rtl8231_led_read_address(fwnode, &port_index, &led_index); ++ if (err) { ++ dev_err(dev, "LED address invalid"); ++ return err; ++ } ++ ++ if (led_index >= RTL8231_NUM_LEDS || port_index >= port_counts[led_index]) { ++ dev_err(dev, "LED address (%d.%d) invalid", port_index, led_index); ++ return -EINVAL; ++ } ++ ++ pled->reg_field = rtl8231_led_get_field(dev, map, port_index, led_index); ++ if (IS_ERR(pled->reg_field)) ++ return PTR_ERR(pled->reg_field); ++ ++ pled->modes = &rtl8231_led_modes; ++ ++ pled->led.max_brightness = 1; ++ pled->led.brightness_get = rtl8231_led_brightness_get; ++ pled->led.brightness_set = rtl8231_led_brightness_set; ++ pled->led.blink_set = rtl8231_led_blink_set; ++ ++ init_data.fwnode = fwnode; ++ ++ return devm_led_classdev_register_ext(dev, &pled->led, &init_data); ++} ++ ++static int rtl8231_led_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const unsigned int *port_counts; ++ struct fwnode_handle *child; ++ struct regmap *map; ++ int err; ++ ++ map = dev_get_regmap(dev->parent, NULL); ++ if (!map) ++ return -ENODEV; ++ ++ if (device_property_match_string(dev, "realtek,led-scan-mode", "single-color") >= 0) { ++ port_counts = rtl8231_led_port_counts_single; ++ regmap_update_bits(map, RTL8231_REG_FUNC0, ++ RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_SINGLE); ++ } else if (device_property_match_string(dev, "realtek,led-scan-mode", "bi-color") >= 0) { ++ port_counts = rtl8231_led_port_counts_bicolor; ++ regmap_update_bits(map, RTL8231_REG_FUNC0, ++ RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_BICOLOR); ++ } else { ++ dev_err(dev, "scan mode missing or invalid"); ++ return -EINVAL; ++ } ++ ++ fwnode_for_each_available_child_node(dev->fwnode, child) { ++ err = rtl8231_led_probe_single(dev, map, port_counts, child); ++ if (err) ++ dev_warn(dev, "failed to register LED %pfwP", child); ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id of_rtl8231_led_match[] = { ++ { .compatible = "realtek,rtl8231-leds" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, of_rtl8231_led_match); ++ ++static struct platform_driver rtl8231_led_driver = { ++ .driver = { ++ .name = "rtl8231-leds", ++ .of_match_table = of_rtl8231_led_match, ++ }, ++ .probe = rtl8231_led_probe, ++}; ++module_platform_driver(rtl8231_led_driver); ++ ++MODULE_AUTHOR("Sander Vanheule "); ++MODULE_DESCRIPTION("Realtek RTL8231 LED support"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/realtek/patches-6.6/300-mips-add-rtl838x-platform.patch b/target/linux/realtek/patches-6.6/300-mips-add-rtl838x-platform.patch deleted file mode 100644 index bb89a7864f..0000000000 --- a/target/linux/realtek/patches-6.6/300-mips-add-rtl838x-platform.patch +++ /dev/null @@ -1,89 +0,0 @@ -From fce11f68491b46b93df69de0630cd9edb90bc772 Mon Sep 17 00:00:00 2001 -From: Birger Koblitz -Date: Wed, 29 Dec 2021 21:54:21 +0100 -Subject: [PATCH] realtek: Create 4 different Realtek Platforms - -Creates RTL83XX as a basic kernel config parameter for the -RTL838X, RTL839x, RTL930X and RTL931X platforms with respective -configurations for the SoCs, which are introduced in addition. - -Submitted-by: Birger Koblitz ---- ---- a/arch/mips/Kbuild.platforms -+++ b/arch/mips/Kbuild.platforms -@@ -22,6 +22,7 @@ platform-$(CONFIG_MACH_NINTENDO64) += n6 - platform-$(CONFIG_PIC32MZDA) += pic32/ - platform-$(CONFIG_RALINK) += ralink/ - platform-$(CONFIG_MIKROTIK_RB532) += rb532/ -+platform-$(CONFIG_MACH_REALTEK_RTL) += rtl838x/ - platform-$(CONFIG_SGI_IP22) += sgi-ip22/ - platform-$(CONFIG_SGI_IP27) += sgi-ip27/ - platform-$(CONFIG_SGI_IP28) += sgi-ip22/ ---- a/arch/mips/Kconfig -+++ b/arch/mips/Kconfig -@@ -620,21 +620,23 @@ config RALINK - - config MACH_REALTEK_RTL - bool "Realtek RTL838x/RTL839x based machines" -- select MIPS_GENERIC - select DMA_NONCOHERENT - select IRQ_MIPS_CPU -- select CSRC_R4K -- select CEVT_R4K - select SYS_HAS_CPU_MIPS32_R1 - select SYS_HAS_CPU_MIPS32_R2 - select SYS_SUPPORTS_BIG_ENDIAN - select SYS_SUPPORTS_32BIT_KERNEL - select SYS_SUPPORTS_MIPS16 -- select SYS_SUPPORTS_MULTITHREADING -- select SYS_SUPPORTS_VPE_LOADER - select BOOT_RAW - select PINCTRL - select USE_OF -+ select NO_EXCEPT_FILL -+ select SYS_SUPPORTS_HIGHMEM -+ select SYS_HAS_EARLY_PRINTK -+ select SYS_HAS_EARLY_PRINTK_8250 -+ select USE_GENERIC_EARLY_PRINTK_8250 -+ select ARCH_HAS_RESET_CONTROLLER -+ select RESET_CONTROLLER - - config SGI_IP22 - bool "SGI IP22 (Indy/Indigo2)" -@@ -970,6 +972,36 @@ config CAVIUM_OCTEON_SOC - - endchoice - -+config RTL838X -+ bool "Realtek RTL838X based platforms" -+ depends on MACH_REALTEK_RTL -+ select CPU_SUPPORTS_CPUFREQ -+ select MIPS_EXTERNAL_TIMER -+ -+config RTL839X -+ bool "Realtek RTL839X based platforms" -+ depends on MACH_REALTEK_RTL -+ select CPU_SUPPORTS_CPUFREQ -+ select MIPS_EXTERNAL_TIMER -+ select SYS_SUPPORTS_MULTITHREADING -+ -+config RTL930X -+ bool "Realtek RTL930X based platforms" -+ depends on MACH_REALTEK_RTL -+ select MIPS_CPU_SCACHE -+ select MIPS_EXTERNAL_TIMER -+ select SYS_SUPPORTS_MULTITHREADING -+ -+config RTL931X -+ bool "Realtek RTL931X based platforms" -+ depends on RTL930X -+ select MIPS_GIC -+ select COMMON_CLK -+ select CLKSRC_MIPS_GIC -+ select SYS_SUPPORTS_VPE_LOADER -+ select SYS_SUPPORTS_SMP -+ select SYS_SUPPORTS_MIPS_CPS -+ - source "arch/mips/alchemy/Kconfig" - source "arch/mips/ath25/Kconfig" - source "arch/mips/ath79/Kconfig" diff --git a/target/linux/realtek/patches-6.6/302-clocksource-add-otto-driver.patch b/target/linux/realtek/patches-6.6/302-clocksource-add-otto-driver.patch deleted file mode 100644 index 791ad0edb7..0000000000 --- a/target/linux/realtek/patches-6.6/302-clocksource-add-otto-driver.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 3cc8011171186d906c547bc6f0c1f8e350edc7cf Mon Sep 17 00:00:00 2001 -From: Markus Stockhausen -Date: Mon, 3 Oct 2022 14:45:21 +0200 -Subject: [PATCH] realtek: resurrect timer driver - -Now that we provide a clock driver for the Reltek SOCs the CPU frequency might -change on demand. This has direct visible effects during operation - -- the CEVT 4K timer is no longer a stable clocksource -- after CPU frequencies changes time calculation works wrong -- sched_clock falls back to kernel default interval (100 Hz) -- timestamps in dmesg have only 2 digits left - -[ 0.000000] sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps ... -[ 0.060000] pid_max: default: 32768 minimum: 301 -[ 0.070000] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear) -[ 0.070000] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear) -[ 0.080000] dyndbg: Ignore empty _ddebug table in a CONFIG_DYNAMIC_DEBUG_CORE build -[ 0.090000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, ... - -Looking around where we can start the CEVT timer for RTL930X is a good basis. -Initially it was developed as a clocksource driver for the broken timer in that -specific SOC series. Afterwards it was shifted around to the CEVT location, -got SMP enablement and lost its clocksource feature. So we at least have -something to copy from. As the timers on these devices are well understood -the implementation follows this way: - -- leave the RTL930X implementation as is -- provide a new driver for RTL83XX devices only -- swap RTL930X driver at a later time - -Like the clock driver this patch contains a self contained module that is SOC -independet and already provides full support for the RTL838X, RTL839X and -RTL930X devices. Some of the new (or reestablished) features are: - -- simplified initialization routines -- SMP setup with CPU hotplug framework -- derived from LXB clock speed -- supplied clocksource -- dedicated register functions for better readability -- documentation about some caveats - -Signed-off-by: Markus Stockhausen -[remove unused header includes, remove old CONFIG_MIPS dependency, add -REALTEK_ prefix to driver symbol] -Signed-off-by: Sander Vanheule - ---- - drivers/clocksource/Kconfig | 12 +++ - drivers/clocksource/Makefile | 1 + - include/linux/cpuhotplug.h | 1 + - 3 files changed, 14 insertions(+) - ---- a/drivers/clocksource/Kconfig -+++ b/drivers/clocksource/Kconfig -@@ -134,6 +134,17 @@ config RDA_TIMER - help - Enables the support for the RDA Micro timer driver. - -+config REALTEK_OTTO_TIMER -+ bool "Clocksource/timer for the Realtek Otto platform" -+ select COMMON_CLK -+ select TIMER_OF -+ help -+ This driver adds support for the timers found in the Realtek RTL83xx -+ and RTL93xx SoCs series. This includes chips such as RTL8380, RTL8381 -+ and RTL832, as well as chips from the RTL839x series, such as RTL8390 -+ RT8391, RTL8392, RTL8393 and RTL8396 and chips of the RTL930x series -+ such as RTL9301, RTL9302 or RTL9303. -+ - config SUN4I_TIMER - bool "Sun4i timer driver" if COMPILE_TEST - depends on HAS_IOMEM ---- a/drivers/clocksource/Makefile -+++ b/drivers/clocksource/Makefile -@@ -59,6 +59,7 @@ obj-$(CONFIG_MILBEAUT_TIMER) += timer-mi - obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o - obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o - obj-$(CONFIG_RDA_TIMER) += timer-rda.o -+obj-$(CONFIG_REALTEK_OTTO_TIMER) += timer-rtl-otto.o - - obj-$(CONFIG_ARC_TIMERS) += arc_timer.o - obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o ---- a/include/linux/cpuhotplug.h -+++ b/include/linux/cpuhotplug.h -@@ -181,6 +181,7 @@ enum cpuhp_state { - CPUHP_AP_MARCO_TIMER_STARTING, - CPUHP_AP_MIPS_GIC_TIMER_STARTING, - CPUHP_AP_ARC_TIMER_STARTING, -+ CPUHP_AP_REALTEK_TIMER_STARTING, - CPUHP_AP_RISCV_TIMER_STARTING, - CPUHP_AP_CLINT_TIMER_STARTING, - CPUHP_AP_CSKY_TIMER_STARTING, diff --git a/target/linux/realtek/patches-6.6/310-add-i2c-rtl9300-support.patch b/target/linux/realtek/patches-6.6/310-add-i2c-rtl9300-support.patch deleted file mode 100644 index 9eaed95d0d..0000000000 --- a/target/linux/realtek/patches-6.6/310-add-i2c-rtl9300-support.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 63a0a4d85bc900464c5b046b13808a582345f8c8 Mon Sep 17 00:00:00 2001 -From: Birger Koblitz -Date: Sat, 11 Dec 2021 20:14:47 +0100 -Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C controller - -This adds support for the RTL9300 and RTL9310 I2C controller. -The controller implements the SMBus protocol for SMBus transfers -over an I2C bus. The driver supports selecting one of the 2 possible -SCL pins and any of the 8 possible SDA pins. Bus speeds of -100kHz (standard speed) and 400kHz (high speed I2C) are supported. - -Submitted-by: Birger Koblitz ---- - drivers/i2c/busses/Kconfig | 10 +++++++++ - drivers/i2c/busses/Makefile | 1 + - 2 files changed, 11 insertions(+) - ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -1023,6 +1023,16 @@ config I2C_RK3X - This driver can also be built as a module. If so, the module will - be called i2c-rk3x. - -+config I2C_RTL9300 -+ tristate "Realtek RTL9300 I2C adapter" -+ depends on OF -+ help -+ Say Y here to include support for the I2C adapter in Realtek RTL9300 -+ and RTL9310 SoCs. -+ -+ This driver can also be built as a module. If so, the module will -+ be called i2c-rtl9300. -+ - config I2C_RZV2M - tristate "Renesas RZ/V2M adapter" - depends on ARCH_RENESAS || COMPILE_TEST ---- a/drivers/i2c/busses/Makefile -+++ b/drivers/i2c/busses/Makefile -@@ -101,6 +101,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom- - obj-$(CONFIG_I2C_QUP) += i2c-qup.o - obj-$(CONFIG_I2C_RIIC) += i2c-riic.o - obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o -+obj-$(CONFIG_I2C_RTL9300) += i2c-rtl9300.o - obj-$(CONFIG_I2C_RZV2M) += i2c-rzv2m.o - obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o - obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o diff --git a/target/linux/realtek/patches-6.6/311-add-i2c-mux-rtl9300-support.patch b/target/linux/realtek/patches-6.6/311-add-i2c-mux-rtl9300-support.patch deleted file mode 100644 index d0bfba4538..0000000000 --- a/target/linux/realtek/patches-6.6/311-add-i2c-mux-rtl9300-support.patch +++ /dev/null @@ -1,46 +0,0 @@ -From f4bdb7fdccdfe3fa382abe77f72a16c2f2e6add0 Mon Sep 17 00:00:00 2001 -From: Birger Koblitz -Date: Sat, 11 Dec 2021 20:25:37 +0100 -Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C multiplexing - -The RTL9300/RTL9310 I2C controllers have support for 2 independent I2C -masters, each with a fixed SCL pin, that cannot be changed. Each of these -masters can use 8 (RTL9300) or 16 (RTL9310) different pins for SDA. -This multiplexer directly controls the two masters and their shared -IO configuration registers to allow multiplexing between any of these -busses. The two masters cannot be used in parallel as the multiplex -is protected by a standard multiplex lock. - -Submitted-by: Birger Koblitz ---- - drivers/i2c/muxes/Kconfig | 9 +++++++ - drivers/i2c/muxes/Makefile | 1 + - 2 files changed, 10 insertions(+) - ---- a/drivers/i2c/muxes/Kconfig -+++ b/drivers/i2c/muxes/Kconfig -@@ -99,6 +99,15 @@ config I2C_MUX_REG - This driver can also be built as a module. If so, the module - will be called i2c-mux-reg. - -+config I2C_MUX_RTL9300 -+ tristate "RTL9300 based I2C multiplexer" -+ help -+ If you say yes to this option, support will be included for a -+ RTL9300 based I2C multiplexer. -+ -+ This driver can also be built as a module. If so, the module -+ will be called i2c-mux-reg. -+ - config I2C_DEMUX_PINCTRL - tristate "pinctrl-based I2C demultiplexer" - depends on PINCTRL && OF ---- a/drivers/i2c/muxes/Makefile -+++ b/drivers/i2c/muxes/Makefile -@@ -14,5 +14,6 @@ obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux - obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o - obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o - obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o -+obj-$(CONFIG_I2C_MUX_RTL9300) += i2c-mux-rtl9300.o - - ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/target/linux/realtek/patches-6.6/314-irqchip-irq-realtek-rtl-add-VPE-support.patch b/target/linux/realtek/patches-6.6/314-irqchip-irq-realtek-rtl-add-VPE-support.patch deleted file mode 100644 index 4610ca8c19..0000000000 --- a/target/linux/realtek/patches-6.6/314-irqchip-irq-realtek-rtl-add-VPE-support.patch +++ /dev/null @@ -1,427 +0,0 @@ -From 6c18e9c491959ac0674ebe36b09f9ddc3f2c9bce Mon Sep 17 00:00:00 2001 -From: Birger Koblitz -Date: Fri, 31 Dec 2021 11:56:49 +0100 -Subject: [PATCH] realtek: Add VPE support for the IRQ driver - -In order to support VSMP, enable support for both VPEs of the RTL839X -and RTL930X SoCs in the irq-realtek-rtl driver. Add support for IRQ -affinity setting. - -Up to kernel 5.15 this patch was divided into two parts - -315-irqchip-irq-realtek-rtl-add-VPE-support.patch -319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch - -As both parts will only work in combination they have been merged into -one patch. - -Submitted-by: Birger Koblitz -Submitted-by: INAGAKI Hiroshi -Signed-off-by: Markus Stockhausen ---- - drivers/irqchip/irq-realtek-rtl.c | 296 +++++++++++++++++++++++++----- - 1 file changed, 249 insertions(+), 47 deletions(-) - ---- a/drivers/irqchip/irq-realtek-rtl.c -+++ b/drivers/irqchip/irq-realtek-rtl.c -@@ -22,22 +22,58 @@ - #define RTL_ICTL_IRR3 0x14 - - #define RTL_ICTL_NUM_INPUTS 32 -- --#define REG(x) (realtek_ictl_base + x) -+#define RTL_ICTL_NUM_OUTPUTS 15 - - static DEFINE_RAW_SPINLOCK(irq_lock); --static void __iomem *realtek_ictl_base; -+ -+#define REG(offset, cpu) (realtek_ictl_base[cpu] + offset) -+ -+static u32 realtek_ictl_unmask[NR_CPUS]; -+static void __iomem *realtek_ictl_base[NR_CPUS]; -+static cpumask_t realtek_ictl_cpu_configurable; -+ -+struct realtek_ictl_output { -+ /* IRQ controller data */ -+ struct fwnode_handle *fwnode; -+ /* Output specific data */ -+ unsigned int output_index; -+ struct irq_domain *domain; -+ u32 child_mask; -+}; - - /* -- * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering, -- * placing IRQ 31 in the first four bits. A routing value of '0' means the -- * interrupt is left disconnected. Routing values {1..15} connect to output -- * lines {0..14}. -+ * Per CPU we have a set of 5 registers that determine interrupt handling for -+ * 32 external interrupts. GIMR (enable/disable interrupt) plus IRR0-IRR3 that -+ * contain "routing" or "priority" values. GIMR uses one bit for each interrupt -+ * and IRRx store 4 bits per interrupt. Realtek uses inverted numbering, -+ * placing IRQ 31 in the first four bits. The register combinations give the -+ * following results for a single interrupt in the wild: -+ * -+ * a) GIMR = 0 / IRRx > 0 -> no interrupts -+ * b) GIMR = 0 / IRRx = 0 -> no interrupts -+ * c) GIMR = 1 / IRRx > 0 -> interrupts -+ * d) GIMR = 1 / IRRx = 0 -> rare interrupts in SMP environment -+ * -+ * Combination d) seems to trigger interrupts only on a VPE if the other VPE -+ * has GIMR = 0 and IRRx > 0. E.g. busy without interrupts allowed. To provide -+ * IRQ balancing features in SMP this driver will handle the registers as -+ * follows: -+ * -+ * 1) set IRRx > 0 for VPE where the interrupt is desired -+ * 2) set IRRx = 0 for VPE where the interrupt is not desired -+ * 3) set both GIMR = 0 to mask (disabled) interrupt -+ * 4) set GIMR = 1 to unmask (enable) interrupt but only for VPE where IRRx > 0 - */ -+ - #define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32)) - #define IRR_SHIFT(idx) ((idx * 4) % 32) - --static void write_irr(void __iomem *irr0, int idx, u32 value) -+static inline u32 read_irr(void __iomem *irr0, int idx) -+{ -+ return (readl(irr0 + IRR_OFFSET(idx)) >> IRR_SHIFT(idx)) & 0xf; -+} -+ -+static inline void write_irr(void __iomem *irr0, int idx, u32 value) - { - unsigned int offset = IRR_OFFSET(idx); - unsigned int shift = IRR_SHIFT(idx); -@@ -48,16 +84,33 @@ static void write_irr(void __iomem *irr0 - writel(irr, irr0 + offset); - } - -+static inline void enable_gimr(int hwirq, int cpu) -+{ -+ u32 value; -+ -+ value = readl(REG(RTL_ICTL_GIMR, cpu)); -+ value |= (BIT(hwirq) & realtek_ictl_unmask[cpu]); -+ writel(value, REG(RTL_ICTL_GIMR, cpu)); -+} -+ -+static inline void disable_gimr(int hwirq, int cpu) -+{ -+ u32 value; -+ -+ value = readl(REG(RTL_ICTL_GIMR, cpu)); -+ value &= ~BIT(hwirq); -+ writel(value, REG(RTL_ICTL_GIMR, cpu)); -+} -+ - static void realtek_ictl_unmask_irq(struct irq_data *i) - { - unsigned long flags; -- u32 value; -+ int cpu; - - raw_spin_lock_irqsave(&irq_lock, flags); - -- value = readl(REG(RTL_ICTL_GIMR)); -- value |= BIT(i->hwirq); -- writel(value, REG(RTL_ICTL_GIMR)); -+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) -+ enable_gimr(i->hwirq, cpu); - - raw_spin_unlock_irqrestore(&irq_lock, flags); - } -@@ -65,110 +118,259 @@ static void realtek_ictl_unmask_irq(stru - static void realtek_ictl_mask_irq(struct irq_data *i) - { - unsigned long flags; -- u32 value; -+ int cpu; - - raw_spin_lock_irqsave(&irq_lock, flags); - -- value = readl(REG(RTL_ICTL_GIMR)); -- value &= ~BIT(i->hwirq); -- writel(value, REG(RTL_ICTL_GIMR)); -+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) -+ disable_gimr(i->hwirq, cpu); - - raw_spin_unlock_irqrestore(&irq_lock, flags); - } - -+static int __maybe_unused realtek_ictl_irq_affinity(struct irq_data *i, -+ const struct cpumask *dest, bool force) -+{ -+ struct realtek_ictl_output *output = i->domain->host_data; -+ cpumask_t cpu_configure; -+ cpumask_t cpu_disable; -+ cpumask_t cpu_enable; -+ unsigned long flags; -+ int cpu; -+ -+ raw_spin_lock_irqsave(&irq_lock, flags); -+ -+ cpumask_and(&cpu_configure, cpu_present_mask, &realtek_ictl_cpu_configurable); -+ -+ cpumask_and(&cpu_enable, &cpu_configure, dest); -+ cpumask_andnot(&cpu_disable, &cpu_configure, dest); -+ -+ for_each_cpu(cpu, &cpu_disable) { -+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, 0); -+ realtek_ictl_unmask[cpu] &= ~BIT(i->hwirq); -+ disable_gimr(i->hwirq, cpu); -+ } -+ -+ for_each_cpu(cpu, &cpu_enable) { -+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, output->output_index + 1); -+ realtek_ictl_unmask[cpu] |= BIT(i->hwirq); -+ enable_gimr(i->hwirq, cpu); -+ } -+ -+ irq_data_update_effective_affinity(i, &cpu_enable); -+ -+ raw_spin_unlock_irqrestore(&irq_lock, flags); -+ -+ return IRQ_SET_MASK_OK; -+} -+ - static struct irq_chip realtek_ictl_irq = { - .name = "realtek-rtl-intc", - .irq_mask = realtek_ictl_mask_irq, - .irq_unmask = realtek_ictl_unmask_irq, -+#ifdef CONFIG_SMP -+ .irq_set_affinity = realtek_ictl_irq_affinity, -+#endif - }; - - static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) - { -+ struct realtek_ictl_output *output = d->host_data; - unsigned long flags; - - irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq); - - raw_spin_lock_irqsave(&irq_lock, flags); -- write_irr(REG(RTL_ICTL_IRR0), hw, 1); -+ -+ output->child_mask |= BIT(hw); -+ write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1); -+ realtek_ictl_unmask[0] |= BIT(hw); -+ - raw_spin_unlock_irqrestore(&irq_lock, flags); - - return 0; - } - -+static int intc_select(struct irq_domain *d, struct irq_fwspec *fwspec, -+ enum irq_domain_bus_token bus_token) -+{ -+ struct realtek_ictl_output *output = d->host_data; -+ bool routed_elsewhere; -+ unsigned long flags; -+ u32 routing_old; -+ int cpu; -+ -+ if (fwspec->fwnode != output->fwnode) -+ return false; -+ -+ /* Original specifiers had only one parameter */ -+ if (fwspec->param_count < 2) -+ return true; -+ -+ raw_spin_lock_irqsave(&irq_lock, flags); -+ -+ /* -+ * Inputs can only be routed to one output, so they shouldn't be -+ * allowed to end up in multiple domains. -+ */ -+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) { -+ routing_old = read_irr(REG(RTL_ICTL_IRR0, cpu), fwspec->param[0]); -+ routed_elsewhere = routing_old && fwspec->param[1] != routing_old - 1; -+ if (routed_elsewhere) { -+ pr_warn("soc int %d already routed to output %d\n", -+ fwspec->param[0], routing_old - 1); -+ break; -+ } -+ } -+ -+ raw_spin_unlock_irqrestore(&irq_lock, flags); -+ -+ return !routed_elsewhere && fwspec->param[1] == output->output_index; -+} -+ - static const struct irq_domain_ops irq_domain_ops = { - .map = intc_map, -+ .select = intc_select, - .xlate = irq_domain_xlate_onecell, - }; - - static void realtek_irq_dispatch(struct irq_desc *desc) - { -+ struct realtek_ictl_output *output = irq_desc_get_handler_data(desc); - struct irq_chip *chip = irq_desc_get_chip(desc); -- struct irq_domain *domain; -+ int cpu = smp_processor_id(); - unsigned long pending; - unsigned int soc_int; - - chained_irq_enter(chip, desc); -- pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR)); -+ pending = readl(REG(RTL_ICTL_GIMR, cpu)) & readl(REG(RTL_ICTL_GISR, cpu)) -+ & output->child_mask; - - if (unlikely(!pending)) { - spurious_interrupt(); - goto out; - } - -- domain = irq_desc_get_handler_data(desc); -- for_each_set_bit(soc_int, &pending, 32) -- generic_handle_domain_irq(domain, soc_int); -+ for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS) -+ generic_handle_domain_irq(output->domain, soc_int); - - out: - chained_irq_exit(chip, desc); - } - -+/* -+ * SoC interrupts are cascaded to MIPS CPU interrupts according to the -+ * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for -+ * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts -+ * thus go into 4 IRRs. A routing value of '0' means the interrupt is left -+ * disconnected. Routing values {1..15} connect to output lines {0..14}. -+ */ -+static int __init setup_parent_interrupts(struct device_node *node, int *parents, -+ unsigned int num_parents) -+{ -+ struct realtek_ictl_output *outputs; -+ struct realtek_ictl_output *output; -+ struct irq_domain *domain; -+ unsigned int p; -+ -+ outputs = kcalloc(num_parents, sizeof(*outputs), GFP_KERNEL); -+ if (!outputs) -+ return -ENOMEM; -+ -+ for (p = 0; p < num_parents; p++) { -+ output = outputs + p; -+ -+ domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, output); -+ if (!domain) -+ goto domain_err; -+ -+ output->fwnode = of_node_to_fwnode(node); -+ output->output_index = p; -+ output->domain = domain; -+ -+ irq_set_chained_handler_and_data(parents[p], realtek_irq_dispatch, output); -+ } -+ -+ return 0; -+ -+domain_err: -+ while (p--) { -+ irq_set_chained_handler_and_data(parents[p], NULL, NULL); -+ irq_domain_remove(outputs[p].domain); -+ } -+ -+ kfree(outputs); -+ -+ return -ENOMEM; -+} -+ - static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent) - { -+ int parent_irqs[RTL_ICTL_NUM_OUTPUTS]; - struct of_phandle_args oirq; -- struct irq_domain *domain; -+ unsigned int num_parents; - unsigned int soc_irq; -- int parent_irq; -+ unsigned int p; -+ int cpu; -+ -+ cpumask_clear(&realtek_ictl_cpu_configurable); - -- realtek_ictl_base = of_iomap(node, 0); -- if (!realtek_ictl_base) -+ for (cpu = 0; cpu < NR_CPUS; cpu++) { -+ realtek_ictl_base[cpu] = of_iomap(node, cpu); -+ if (realtek_ictl_base[cpu]) { -+ cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable); -+ -+ /* Disable all cascaded interrupts and clear routing */ -+ for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) { -+ write_irr(REG(RTL_ICTL_IRR0, cpu), soc_irq, 0); -+ realtek_ictl_unmask[cpu] &= ~BIT(soc_irq); -+ disable_gimr(soc_irq, cpu); -+ } -+ } -+ } -+ -+ if (cpumask_empty(&realtek_ictl_cpu_configurable)) - return -ENXIO; - -- /* Disable all cascaded interrupts and clear routing */ -- writel(0, REG(RTL_ICTL_GIMR)); -- for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) -- write_irr(REG(RTL_ICTL_IRR0), soc_irq, 0); -+ num_parents = of_irq_count(node); -+ if (num_parents > RTL_ICTL_NUM_OUTPUTS) { -+ pr_err("too many parent interrupts\n"); -+ return -EINVAL; -+ } - -- if (WARN_ON(!of_irq_count(node))) { -+ for (p = 0; p < num_parents; p++) -+ parent_irqs[p] = of_irq_get(node, p); -+ -+ if (WARN_ON(!num_parents)) { - /* - * If DT contains no parent interrupts, assume MIPS CPU IRQ 2 - * (HW0) is connected to the first output. This is the case for - * all known hardware anyway. "interrupt-map" is deprecated, so - * don't bother trying to parse that. -+ * Since this is to account for old devicetrees with one-cell -+ * interrupt specifiers, only one output domain is needed. - */ - oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller"); -- oirq.args_count = 1; -- oirq.args[0] = 2; -- -- parent_irq = irq_create_of_mapping(&oirq); -+ if (oirq.np) { -+ oirq.args_count = 1; -+ oirq.args[0] = 2; -+ -+ parent_irqs[0] = irq_create_of_mapping(&oirq); -+ num_parents = 1; -+ } - - of_node_put(oirq.np); -- } else { -- parent_irq = of_irq_get(node, 0); - } - -- if (parent_irq < 0) -- return parent_irq; -- else if (!parent_irq) -- return -ENODEV; -- -- domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL); -- if (!domain) -- return -ENOMEM; -- -- irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, domain); -+ /* Ensure we haven't collected any errors before proceeding */ -+ for (p = 0; p < num_parents; p++) { -+ if (parent_irqs[p] < 0) -+ return parent_irqs[p]; -+ if (!parent_irqs[p]) -+ return -ENODEV; -+ } - -- return 0; -+ return setup_parent_interrupts(node, &parent_irqs[0], num_parents); - } - - IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init); diff --git a/target/linux/realtek/patches-6.6/318-add-rtl83xx-clk-support.patch b/target/linux/realtek/patches-6.6/318-add-rtl83xx-clk-support.patch deleted file mode 100644 index e75260a638..0000000000 --- a/target/linux/realtek/patches-6.6/318-add-rtl83xx-clk-support.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 800d5fb3c6a16661932c932bacd660e38d06b727 Mon Sep 17 00:00:00 2001 -From: Markus Stockhausen -Date: Thu, 25 Aug 2022 08:22:36 +0200 -Subject: [PATCH] realtek: add patch to enable new clock driver in kernel - -Allow building the clock driver with kernel config options. - -Submitted-by: Markus Stockhausen ---- - drivers/clk/Kconfig | 1 + - drivers/clk/Makefile | 1 + - 2 files changed, 2 insertions(+) - ---- a/drivers/clk/Kconfig -+++ b/drivers/clk/Kconfig -@@ -484,6 +484,7 @@ source "drivers/clk/mvebu/Kconfig" - source "drivers/clk/nuvoton/Kconfig" - source "drivers/clk/pistachio/Kconfig" - source "drivers/clk/qcom/Kconfig" -+source "drivers/clk/realtek/Kconfig" - source "drivers/clk/ralink/Kconfig" - source "drivers/clk/renesas/Kconfig" - source "drivers/clk/rockchip/Kconfig" ---- a/drivers/clk/Makefile -+++ b/drivers/clk/Makefile -@@ -112,6 +112,7 @@ obj-$(CONFIG_COMMON_CLK_PISTACHIO) += pi - obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ - obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ - obj-y += ralink/ -+obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/ - obj-y += renesas/ - obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ - obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ diff --git a/target/linux/realtek/patches-6.6/330-add-realtek-thernal-driver.patch b/target/linux/realtek/patches-6.6/330-add-realtek-thernal-driver.patch deleted file mode 100644 index af550a8c20..0000000000 --- a/target/linux/realtek/patches-6.6/330-add-realtek-thernal-driver.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- a/drivers/thermal/Kconfig -+++ b/drivers/thermal/Kconfig -@@ -522,4 +522,11 @@ config LOONGSON2_THERMAL - is higher than the high temperature threshold or lower than the low - temperature threshold, the interrupt will occur. - -+config REALTEK_THERMAL -+ tristate "Realtek RTL838x and RTL930x thermal sensor support" -+ depends on RTL838X || RTL839X || RTL930X || COMPILE_TEST -+ depends on THERMAL_OF -+ help -+ Support thermal sensor in Realtek RTL838x, RTL839x and RTL930x SoCs -+ - endif ---- a/drivers/thermal/Makefile -+++ b/drivers/thermal/Makefile -@@ -64,3 +64,4 @@ obj-$(CONFIG_AMLOGIC_THERMAL) += aml - obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o - obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o - obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o -+obj-$(CONFIG_REALTEK_THERMAL) += realtek-thermal.o diff --git a/target/linux/realtek/patches-6.6/700-dsa-mdio-increase-max-ports-for-rtl839x-rtl931x.patch b/target/linux/realtek/patches-6.6/700-dsa-mdio-increase-max-ports-for-rtl839x-rtl931x.patch deleted file mode 100644 index 147e2c587b..0000000000 --- a/target/linux/realtek/patches-6.6/700-dsa-mdio-increase-max-ports-for-rtl839x-rtl931x.patch +++ /dev/null @@ -1,132 +0,0 @@ -From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 26 Nov 2020 12:02:21 +0100 -Subject: realtek dsa/phy: Increase max ports for RTL839X/RTL931X - -Linux standard can only support up to 32 devices per mdio bus and up to -12 ports per DSA switch. This is not enough for the large RTL839X and -RTL931X devices. Increase the max values accordingly. Additionally take -care about the functions that work on bit masks. - -Submitted-by: Bert Vermeulen -Submitted-by: Birger Koblitz -Submitted-by: Sander Vanheule -Submitted-by: Bjørn Mork -Submitted-by: John Crispin -Signed-off-by: Markus Stockhausen ---- - drivers/net/mdio/fwnode_mdio.c | 2 +- - include/linux/phy.h | 6 +++--- - include/linux/platform_data/dsa.h | 2 +- - include/net/dsa.h | 14 +++++++------- - net/dsa/slave.c | 4 ++-- - 5 files changed, 14 insertions(+), 14 deletions(-) - ---- a/drivers/net/mdio/fwnode_mdio.c -+++ b/drivers/net/mdio/fwnode_mdio.c -@@ -87,7 +87,7 @@ int fwnode_mdiobus_phy_device_register(s - } - - if (fwnode_property_read_bool(child, "broken-turn-around")) -- mdio->phy_ignore_ta_mask |= 1 << addr; -+ mdio->phy_ignore_ta_mask |= BIT_ULL(addr); - - fwnode_property_read_u32(child, "reset-assert-us", - &phy->mdio.reset_assert_delay); ---- a/include/linux/phy.h -+++ b/include/linux/phy.h -@@ -294,7 +294,7 @@ static inline const char *phy_modes(phy_ - #define PHY_INIT_TIMEOUT 100000 - #define PHY_FORCE_TIMEOUT 10 - --#define PHY_MAX_ADDR 32 -+#define PHY_MAX_ADDR 64 - - /* Used when trying to connect to a specific phy (mii bus id:phy device id) */ - #define PHY_ID_FMT "%s:%02x" -@@ -414,10 +414,10 @@ struct mii_bus { - struct mdio_device *mdio_map[PHY_MAX_ADDR]; - - /** @phy_mask: PHY addresses to be ignored when probing */ -- u32 phy_mask; -+ u64 phy_mask; - - /** @phy_ignore_ta_mask: PHY addresses to ignore the TA/read failure */ -- u32 phy_ignore_ta_mask; -+ u64 phy_ignore_ta_mask; - - /** - * @irq: An array of interrupts, each PHY's interrupt at the index ---- a/include/linux/platform_data/dsa.h -+++ b/include/linux/platform_data/dsa.h -@@ -6,7 +6,7 @@ struct device; - struct net_device; - - #define DSA_MAX_SWITCHES 4 --#define DSA_MAX_PORTS 12 -+#define DSA_MAX_PORTS 54 - #define DSA_RTABLE_NONE -1 - - struct dsa_chip_data { ---- a/include/net/dsa.h -+++ b/include/net/dsa.h -@@ -465,7 +465,7 @@ struct dsa_switch { - /* - * Slave mii_bus and devices for the individual ports. - */ -- u32 phys_mii_mask; -+ u64 phys_mii_mask; - struct mii_bus *slave_mii_bus; - - /* Ageing Time limits in msecs */ -@@ -597,24 +597,24 @@ static inline bool dsa_is_user_port(stru - dsa_switch_for_each_port_continue_reverse((_dp), (_ds)) \ - if (dsa_port_is_cpu((_dp))) - --static inline u32 dsa_user_ports(struct dsa_switch *ds) -+static inline u64 dsa_user_ports(struct dsa_switch *ds) - { - struct dsa_port *dp; -- u32 mask = 0; -+ u64 mask = 0; - - dsa_switch_for_each_user_port(dp, ds) -- mask |= BIT(dp->index); -+ mask |= BIT_ULL(dp->index); - - return mask; - } - --static inline u32 dsa_cpu_ports(struct dsa_switch *ds) -+static inline u64 dsa_cpu_ports(struct dsa_switch *ds) - { - struct dsa_port *cpu_dp; -- u32 mask = 0; -+ u64 mask = 0; - - dsa_switch_for_each_cpu_port(cpu_dp, ds) -- mask |= BIT(cpu_dp->index); -+ mask |= BIT_ULL(cpu_dp->index); - - return mask; - } ---- a/net/dsa/slave.c -+++ b/net/dsa/slave.c -@@ -320,7 +320,7 @@ static int dsa_slave_phy_read(struct mii - { - struct dsa_switch *ds = bus->priv; - -- if (ds->phys_mii_mask & (1 << addr)) -+ if (ds->phys_mii_mask & BIT_ULL(addr)) - return ds->ops->phy_read(ds, addr, reg); - - return 0xffff; -@@ -330,7 +330,7 @@ static int dsa_slave_phy_write(struct mi - { - struct dsa_switch *ds = bus->priv; - -- if (ds->phys_mii_mask & (1 << addr)) -+ if (ds->phys_mii_mask & BIT_ULL(addr)) - return ds->ops->phy_write(ds, addr, reg, val); - - return 0; diff --git a/target/linux/realtek/patches-6.6/702-include-linux-add-phy-hsgmii-mode.patch b/target/linux/realtek/patches-6.6/702-include-linux-add-phy-hsgmii-mode.patch deleted file mode 100644 index a43b49c1ff..0000000000 --- a/target/linux/realtek/patches-6.6/702-include-linux-add-phy-hsgmii-mode.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 9d9bf16aa8d966834ac1280f96c37d22552c33d1 Mon Sep 17 00:00:00 2001 -From: Birger Koblitz -Date: Wed, 8 Sep 2021 16:13:18 +0200 -Subject: realtek phy: Add PHY hsgmii mode - -This adds RTL93xx-specific MAC configuration routines that allow also configuration -of 10GBit links for phylink. There is support for the Realtek-specific HSGMII -protocol. - -Submitted-by: Birger Koblitz ---- - drivers/net/phy/phy-core.c | 1 + - drivers/net/phy/phylink.c | 4 ++++ - include/linux/phy.h | 3 +++ - 3 files changed, 8 insertions(+) - ---- a/drivers/net/phy/phy-core.c -+++ b/drivers/net/phy/phy-core.c -@@ -126,6 +126,7 @@ int phy_interface_num_ports(phy_interfac - case PHY_INTERFACE_MODE_MOCA: - case PHY_INTERFACE_MODE_TRGMII: - case PHY_INTERFACE_MODE_USXGMII: -+ case PHY_INTERFACE_MODE_HSGMII: - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_SMII: - case PHY_INTERFACE_MODE_1000BASEX: ---- a/drivers/net/phy/phylink.c -+++ b/drivers/net/phy/phylink.c -@@ -124,6 +124,7 @@ do { \ - static const phy_interface_t phylink_sfp_interface_preference[] = { - PHY_INTERFACE_MODE_25GBASER, - PHY_INTERFACE_MODE_USXGMII, -+ PHY_INTERFACE_MODE_HSGMII, - PHY_INTERFACE_MODE_10GBASER, - PHY_INTERFACE_MODE_5GBASER, - PHY_INTERFACE_MODE_2500BASEX, -@@ -238,6 +239,7 @@ static int phylink_interface_max_speed(p - - case PHY_INTERFACE_MODE_XGMII: - case PHY_INTERFACE_MODE_RXAUI: -+ case PHY_INTERFACE_MODE_HSGMII: - case PHY_INTERFACE_MODE_XAUI: - case PHY_INTERFACE_MODE_10GBASER: - case PHY_INTERFACE_MODE_10GKR: -@@ -547,6 +549,7 @@ unsigned long phylink_get_capabilities(p - break; - - case PHY_INTERFACE_MODE_XGMII: -+ case PHY_INTERFACE_MODE_HSGMII: - case PHY_INTERFACE_MODE_RXAUI: - case PHY_INTERFACE_MODE_XAUI: - case PHY_INTERFACE_MODE_10GBASER: -@@ -957,6 +960,7 @@ static int phylink_parse_mode(struct phy - fallthrough; - case PHY_INTERFACE_MODE_USXGMII: - case PHY_INTERFACE_MODE_10GKR: -+ case PHY_INTERFACE_MODE_HSGMII: - case PHY_INTERFACE_MODE_10GBASER: - phylink_set(pl->supported, 10baseT_Half); - phylink_set(pl->supported, 10baseT_Full); ---- a/include/linux/phy.h -+++ b/include/linux/phy.h -@@ -148,6 +148,7 @@ typedef enum { - PHY_INTERFACE_MODE_XGMII, - PHY_INTERFACE_MODE_XLGMII, - PHY_INTERFACE_MODE_MOCA, -+ PHY_INTERFACE_MODE_HSGMII, - PHY_INTERFACE_MODE_PSGMII, - PHY_INTERFACE_MODE_QSGMII, - PHY_INTERFACE_MODE_TRGMII, -@@ -256,6 +257,8 @@ static inline const char *phy_modes(phy_ - return "xlgmii"; - case PHY_INTERFACE_MODE_MOCA: - return "moca"; -+ case PHY_INTERFACE_MODE_HSGMII: -+ return "hsgmii"; - case PHY_INTERFACE_MODE_PSGMII: - return "psgmii"; - case PHY_INTERFACE_MODE_QSGMII: diff --git a/target/linux/realtek/patches-6.6/706-include-linux-add-phy-ops-for-rtl838x.patch b/target/linux/realtek/patches-6.6/706-include-linux-add-phy-ops-for-rtl838x.patch deleted file mode 100644 index 3404d55ed3..0000000000 --- a/target/linux/realtek/patches-6.6/706-include-linux-add-phy-ops-for-rtl838x.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 26 Nov 2020 12:02:21 +0100 -Subject: phy: Add PHY ops for rtl838x EEE - -* rename the target to realtek -* add refactored DSA driver -* add latest gpio driver -* lots of arch cleanups -* new irq driver -* additional boards - -Submitted-by: Bert Vermeulen -Submitted-by: Birger Koblitz -Submitted-by: Sander Vanheule -Submitted-by: Bjørn Mork -Submitted-by: John Crispin ---- - include/linux/phy.h | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/include/linux/phy.h -+++ b/include/linux/phy.h -@@ -1181,6 +1181,8 @@ struct phy_driver { - */ - int (*led_polarity_set)(struct phy_device *dev, int index, - unsigned long modes); -+ int (*get_eee)(struct phy_device *dev, struct ethtool_eee *e); -+ int (*set_eee)(struct phy_device *dev, struct ethtool_eee *e); - }; - #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ - struct phy_driver, mdiodrv) diff --git a/target/linux/realtek/patches-6.6/708-drivers-net-phy-eee-support-for-rtl838x.patch b/target/linux/realtek/patches-6.6/708-drivers-net-phy-eee-support-for-rtl838x.patch deleted file mode 100644 index 44461992b1..0000000000 --- a/target/linux/realtek/patches-6.6/708-drivers-net-phy-eee-support-for-rtl838x.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 26 Nov 2020 12:02:21 +0100 -Subject: net: phy: EEE support for rtl838x - -* rename the target to realtek -* add refactored DSA driver -* add latest gpio driver -* lots of arch cleanups -* new irq driver -* additional boards - -Submitted-by: Bert Vermeulen -Submitted-by: Birger Koblitz -Submitted-by: Sander Vanheule -Submitted-by: Bjørn Mork -Submitted-by: John Crispin ---- - drivers/net/phy/phylink. | 14 +++++++++++-- - 1 file changed, 12 insertions(+), 2 deletions(-) - ---- a/drivers/net/phy/phylink.c -+++ b/drivers/net/phy/phylink.c -@@ -2805,8 +2805,11 @@ int phylink_ethtool_get_eee(struct phyli - - ASSERT_RTNL(); - -- if (pl->phydev) -+ if (pl->phydev) { -+ if (pl->phydev->drv->get_eee) -+ return pl->phydev->drv->get_eee(pl->phydev, eee); - ret = phy_ethtool_get_eee(pl->phydev, eee); -+ } - - return ret; - } -@@ -2823,8 +2826,11 @@ int phylink_ethtool_set_eee(struct phyli - - ASSERT_RTNL(); - -- if (pl->phydev) -+ if (pl->phydev) { -+ if (pl->phydev->drv->set_eee) -+ return pl->phydev->drv->set_eee(pl->phydev, eee); - ret = phy_ethtool_set_eee(pl->phydev, eee); -+ } - - return ret; - } diff --git a/target/linux/realtek/patches-6.6/712-net-phy-add-an-MDIO-SMBus-library.patch b/target/linux/realtek/patches-6.6/712-net-phy-add-an-MDIO-SMBus-library.patch deleted file mode 100644 index 229e2612ea..0000000000 --- a/target/linux/realtek/patches-6.6/712-net-phy-add-an-MDIO-SMBus-library.patch +++ /dev/null @@ -1,428 +0,0 @@ -From d585c55b9f70cf9e8c66820d7efe7130c683f19e Mon Sep 17 00:00:00 2001 -From: Antoine Tenart -Date: Fri, 21 Feb 2020 11:51:27 +0100 -Subject: [PATCH 2/3] net: phy: add an MDIO SMBus library - -Signed-off-by: Antoine Tenart ---- - drivers/net/mdio/Kconfig | 11 +++++++ - drivers/net/mdio/Makefile | 1 + - drivers/net/mdio/mdio-smbus.c | 62 +++++++++++++++++++++++++++++++++++ - drivers/net/phy/Kconfig | 1 + - include/linux/mdio/mdio-i2c.h | 16 +++++++++ - 5 files changed, 91 insertions(+) - create mode 100644 drivers/net/mdio/mdio-smbus.c - ---- a/drivers/net/mdio/Kconfig -+++ b/drivers/net/mdio/Kconfig -@@ -54,6 +54,17 @@ config MDIO_SUN4I - interface units of the Allwinner SoC that have an EMAC (A10, - A12, A10s, etc.) - -+config MDIO_SMBUS -+ tristate -+ depends on I2C_SMBUS -+ help -+ Support SMBus based PHYs. This provides a MDIO bus bridged -+ to SMBus to allow PHYs connected in SMBus mode to be accessed -+ using the existing infrastructure. -+ -+ This is library mode. -+ -+ - config MDIO_XGENE - tristate "APM X-Gene SoC MDIO bus controller" - depends on ARCH_XGENE || COMPILE_TEST ---- a/drivers/net/mdio/Makefile -+++ b/drivers/net/mdio/Makefile -@@ -20,6 +20,7 @@ obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-ms - obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o - obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o - obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o -+obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o - obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o - obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o - obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o ---- /dev/null -+++ b/drivers/net/mdio/mdio-smbus.c -@@ -0,0 +1,341 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * MDIO SMBus bridge -+ * -+ * Copyright (C) 2020 Antoine Tenart -+ * Copyright (C) 2025 Bjørn Mork -+ * -+ * Network PHYs can appear on SMBus when they are part of SFP modules. -+ */ -+#include -+#include -+#include -+#include -+ -+static int smbus_mii_read_c45(struct mii_bus *mii, int phy_id, int devad, int reg) -+{ -+ u16 bus_addr = i2c_mii_phy_addr(phy_id); -+ struct i2c_adapter *i2c = mii->priv; -+ union i2c_smbus_data data; -+ size_t addrlen; -+ u8 buf[5], *p; -+ int i, ret; -+ -+ if (!i2c_mii_valid_phy_id(phy_id)) -+ return 0xffff; -+ -+ p = buf; -+ if (devad >= 0) { -+ *p++ = 0x20 | devad; -+ *p++ = reg >> 8; -+ } -+ *p++ = reg; -+ addrlen = p - buf; -+ -+ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT); -+ if (addrlen > 1) { -+ for (i = 1; i < addrlen; i++) { -+ data.byte = buf[i]; -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, buf[0], I2C_SMBUS_BYTE_DATA, &data); -+ if (ret < 0) -+ goto unlock; -+ } -+ } -+ -+ for (i = addrlen; i < addrlen + 2; i++) { -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, buf[0], I2C_SMBUS_BYTE_DATA, &data); -+ if (ret < 0) -+ goto unlock; -+ buf[i] = data.byte; -+ } -+ -+unlock: -+ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); -+ if (ret < 0) -+ return 0xffff; -+ return buf[addrlen] << 8 | buf[addrlen + 1]; -+} -+ -+static int smbus_mii_write_c45(struct mii_bus *mii, int phy_id, int devad, int reg, u16 val) -+{ -+ u16 bus_addr = i2c_mii_phy_addr(phy_id); -+ struct i2c_adapter *i2c = mii->priv; -+ union i2c_smbus_data data; -+ size_t buflen; -+ u8 buf[5], *p; -+ int i, ret; -+ -+ if (!i2c_mii_valid_phy_id(phy_id)) -+ return 0; -+ -+ p = buf; -+ if (devad >= 0) { -+ *p++ = devad; -+ *p++ = reg >> 8; -+ } -+ *p++ = reg; -+ *p++ = val >> 8; -+ *p++ = val; -+ buflen = p - buf; -+ -+ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT); -+ for (i = 1; i < buflen; i++) { -+ data.byte = buf[i]; -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, buf[0], I2C_SMBUS_BYTE_DATA, &data); -+ if (ret < 0) -+ goto unlock; -+ } -+unlock: -+ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); -+ return ret < 0 ? ret : 0; -+} -+ -+static int smbus_mii_read_c22(struct mii_bus *bus, int phy_id, int reg) -+{ -+ return smbus_mii_read_c45(bus, phy_id, -1, reg); -+} -+ -+static int smbus_mii_write_c22(struct mii_bus *bus, int phy_id, int reg, u16 val) -+{ -+ return smbus_mii_write_c45(bus, phy_id, -1, reg, val); -+} -+ -+/* From mdio-i2c.c: -+ * -+ * RollBall SFPs do not access internal PHY via I2C address 0x56, but -+ * instead via address 0x51, when SFP page is set to 0x03 and password to -+ * 0xffffffff. -+ * -+ * address size contents description -+ * ------- ---- -------- ----------- -+ * 0x80 1 CMD 0x01/0x02/0x04 for write/read/done -+ * 0x81 1 DEV Clause 45 device -+ * 0x82 2 REG Clause 45 register -+ * 0x84 2 VAL Register value -+ */ -+#define ROLLBALL_PHY_I2C_ADDR 0x51 -+ -+#define ROLLBALL_PASSWORD (SFP_VSL + 3) -+ -+#define ROLLBALL_CMD_ADDR 0x80 -+#define ROLLBALL_DATA_ADDR 0x81 -+ -+#define ROLLBALL_CMD_WRITE 0x01 -+#define ROLLBALL_CMD_READ 0x02 -+#define ROLLBALL_CMD_DONE 0x04 -+ -+#define SFP_PAGE_ROLLBALL_MDIO 3 -+ -+static int smbus_set_sfp_page_lock(struct i2c_adapter *i2c, int bus_addr, u8 page) -+{ -+ union i2c_smbus_data data; -+ u8 oldpage; -+ int ret; -+ -+ i2c_lock_bus(i2c, I2C_LOCK_SEGMENT); -+ -+ /* read current page */ -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, SFP_PAGE, I2C_SMBUS_BYTE_DATA, &data); -+ if (ret < 0) -+ goto unlock; -+ -+ oldpage = data.byte; -+ data.byte = page; -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, SFP_PAGE, I2C_SMBUS_BYTE_DATA, &data); -+ if (ret == 0) -+ return oldpage; -+ -+unlock: -+ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); -+ -+ return ret; -+} -+ -+static int __smbus_set_sfp_page_unlock(struct i2c_adapter *i2c, int bus_addr, u8 page) -+{ -+ union i2c_smbus_data data; -+ int ret; -+ -+ data.byte = page; -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, SFP_PAGE, I2C_SMBUS_BYTE_DATA, &data); -+ i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); -+ -+ return ret; -+} -+ -+/* Wait for the ROLLBALL_CMD_ADDR register to read ROLLBALL_CMD_DONE, -+ * indicating that the previous command has completed. -+ * -+ * Quoting from the mdio-i2c.c implementation: -+ * -+ * By experiment it takes up to 70 ms to access a register for these -+ * SFPs. Sleep 20ms between iterations and try 10 times. -+ */ -+static int __smbus_rollball_mii_poll(struct i2c_adapter *i2c , int bus_addr) -+{ -+ union i2c_smbus_data data; -+ int i, ret; -+ -+ i = 10; -+ do { -+ msleep(20); -+ -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, ROLLBALL_CMD_ADDR, I2C_SMBUS_BYTE_DATA, &data); -+ if (ret < 0) -+ return ret; -+ -+ if (data.byte == ROLLBALL_CMD_DONE) -+ return 0; -+ } while (i-- > 0); -+ dev_dbg(&i2c->dev, "poll timed out\n"); -+ return -ETIMEDOUT; -+} -+ -+static int smbus_mii_read_rollball(struct mii_bus *bus, int phy_id, int devad, int reg) -+{ -+ struct i2c_adapter *i2c = bus->priv; -+ union i2c_smbus_data data; -+ int i, bus_addr, old, ret; -+ u8 buf[6]; -+ -+ bus_addr = i2c_mii_phy_addr(phy_id); -+ if (bus_addr != ROLLBALL_PHY_I2C_ADDR) -+ return 0xffff; -+ -+ old = smbus_set_sfp_page_lock(i2c, bus_addr, SFP_PAGE_ROLLBALL_MDIO); -+ if (old < 0) -+ return 0xffff; -+ -+ /* set address */ -+ buf[0] = ROLLBALL_CMD_READ; -+ buf[1] = devad; -+ buf[2] = reg >> 8; -+ buf[3] = reg & 0xff; -+ -+ /* send address */ -+ for (i = 0; i < 4; i++) { -+ data.byte = buf[i]; -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, ROLLBALL_CMD_ADDR + i, I2C_SMBUS_BYTE_DATA, &data); -+ if (ret < 0) -+ goto unlock; -+ } -+ -+ /* wait for command to complete */ -+ ret = __smbus_rollball_mii_poll(i2c, bus_addr); -+ if (ret) -+ goto unlock; -+ -+ /* read result */ -+ for (i = 4; i < 6; i++) { -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_READ, ROLLBALL_CMD_ADDR + i, I2C_SMBUS_BYTE_DATA, &data); -+ if (ret < 0) -+ goto unlock; -+ buf[i] = data.byte; -+ } -+ -+unlock: -+ __smbus_set_sfp_page_unlock(i2c, bus_addr, old); -+ if (ret < 0) -+ return 0xffff; -+ return buf[4] << 8 | buf[5]; -+} -+ -+static int smbus_mii_write_rollball(struct mii_bus *bus, int phy_id, int devad, int reg, u16 val) -+{ -+ struct i2c_adapter *i2c = bus->priv; -+ union i2c_smbus_data data; -+ int i, bus_addr, old, ret; -+ u8 buf[6]; -+ -+ bus_addr = i2c_mii_phy_addr(phy_id); -+ if (bus_addr != ROLLBALL_PHY_I2C_ADDR) -+ return 0; -+ -+ old = smbus_set_sfp_page_lock(i2c, bus_addr, SFP_PAGE_ROLLBALL_MDIO); -+ if (old < 0) -+ return old; -+ -+ /* set address */ -+ buf[0] = ROLLBALL_CMD_WRITE; -+ buf[1] = devad; -+ buf[2] = reg >> 8; -+ buf[3] = reg & 0xff; -+ buf[4] = val >> 8; -+ buf[5] = val & 0xff; -+ -+ /* send address and value */ -+ for (i = 0; i < 6; i++) { -+ data.byte = buf[i]; -+ ret = __i2c_smbus_xfer(i2c, bus_addr, 0, I2C_SMBUS_WRITE, ROLLBALL_CMD_ADDR + i, I2C_SMBUS_BYTE_DATA, &data); -+ if (ret < 0) -+ goto unlock; -+ } -+ -+ /* wait for command to complete */ -+ ret = __smbus_rollball_mii_poll(i2c, bus_addr); -+ -+unlock: -+ __smbus_set_sfp_page_unlock(i2c, bus_addr, old); -+ return ret; -+} -+ -+/* write "password" - four 0xff bytes - to the ROLLBALL_PASSWORD register */ -+static int smbus_mii_init_rollball(struct i2c_adapter *i2c) -+{ -+ union i2c_smbus_data data; -+ int i, ret; -+ -+ data.byte = 0xff; -+ for (i = 0; i < 4; i++) { -+ ret = i2c_smbus_xfer(i2c, ROLLBALL_PHY_I2C_ADDR, 0, I2C_SMBUS_WRITE, ROLLBALL_PASSWORD + i, I2C_SMBUS_BYTE_DATA, &data); -+ if (ret < 0) -+ return ret; -+ } -+ return 0; -+} -+ -+struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c, -+ enum mdio_i2c_proto protocol) -+{ -+ struct mii_bus *mii; -+ int ret; -+ -+ if (!i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) -+ return ERR_PTR(-EINVAL); -+ -+ mii = mdiobus_alloc(); -+ if (!mii) -+ return ERR_PTR(-ENOMEM); -+ -+ snprintf(mii->id, MII_BUS_ID_SIZE, "smbus:%s", dev_name(parent)); -+ mii->parent = parent; -+ mii->priv = i2c; -+ -+ switch (protocol) { -+ case MDIO_I2C_ROLLBALL: -+ ret = smbus_mii_init_rollball(i2c); -+ if (ret < 0) { -+ dev_err(parent, -+ "Cannot initialize RollBall MDIO protocol on SMBus: %d\n", -+ ret); -+ mdiobus_free(mii); -+ return ERR_PTR(ret); -+ } -+ -+ mii->read_c45 = smbus_mii_read_rollball; -+ mii->write_c45 = smbus_mii_write_rollball; -+ break; -+ default: -+ mii->read = smbus_mii_read_c22; -+ mii->write = smbus_mii_write_c22; -+ mii->read_c45 = smbus_mii_read_c45; -+ mii->write_c45 = smbus_mii_write_c45; -+ break; -+ } -+ -+ return mii; -+} -+ -+MODULE_AUTHOR("Antoine Tenart"); -+MODULE_DESCRIPTION("MDIO SMBus bridge library"); -+MODULE_LICENSE("GPL"); ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -65,6 +65,7 @@ config SFP - depends on I2C && PHYLINK - depends on HWMON || HWMON=n - select MDIO_I2C -+ select MDIO_SMBUS - - comment "Switch configuration API + drivers" - ---- a/include/linux/mdio/mdio-i2c.h -+++ b/include/linux/mdio/mdio-i2c.h -@@ -20,5 +20,9 @@ enum mdio_i2c_proto { - - struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c, - enum mdio_i2c_proto protocol); -+struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c, -+ enum mdio_i2c_proto protocol); -+bool i2c_mii_valid_phy_id(int phy_id); -+unsigned int i2c_mii_phy_addr(int phy_id); - - #endif ---- a/drivers/net/mdio/mdio-i2c.c -+++ b/drivers/net/mdio/mdio-i2c.c -@@ -20,12 +20,12 @@ - * specified to be present in SFP modules. These correspond with PHY - * addresses 16 and 17. Disallow access to these "phy" addresses. - */ --static bool i2c_mii_valid_phy_id(int phy_id) -+bool i2c_mii_valid_phy_id(int phy_id) - { - return phy_id != 0x10 && phy_id != 0x11; - } - --static unsigned int i2c_mii_phy_addr(int phy_id) -+unsigned int i2c_mii_phy_addr(int phy_id) - { - return phy_id + 0x40; - } diff --git a/target/linux/realtek/patches-6.6/714-net-phy-sfp-add-support-for-SMBus.patch b/target/linux/realtek/patches-6.6/714-net-phy-sfp-add-support-for-SMBus.patch deleted file mode 100644 index 4fb1e7f293..0000000000 --- a/target/linux/realtek/patches-6.6/714-net-phy-sfp-add-support-for-SMBus.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 3cb0bde365d913c484d20224367a54a0eac780a7 Mon Sep 17 00:00:00 2001 -From: Antoine Tenart -Date: Fri, 21 Feb 2020 11:55:29 +0100 -Subject: [PATCH 3/3] net: phy: sfp: add support for SMBus - -Signed-off-by: Antoine Tenart ---- - drivers/net/phy/sfp.c | 92 +++++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 88 insertions(+), 4 deletions(-) - ---- a/drivers/net/phy/sfp.c -+++ b/drivers/net/phy/sfp.c -@@ -675,10 +675,64 @@ static int sfp_i2c_write(struct sfp *sfp - return ret == ARRAY_SIZE(msgs) ? len : 0; - } - -+static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, -+ size_t len) -+{ -+ u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf; -+ union i2c_smbus_data data; -+ int ret; -+ -+ bus_addr -= 0x40; -+ -+ while (len > 0) { -+ ret = i2c_smbus_xfer(sfp->i2c, i2c_mii_phy_addr(bus_addr), 0, -+ I2C_SMBUS_READ, dev_addr, -+ I2C_SMBUS_BYTE_DATA, &data); -+ if (ret) -+ return ret; -+ *val++ = data.byte; -+ dev_addr++; -+ len--; -+ } -+ -+ return val - (u8 *)buf; -+} -+ -+static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, -+ size_t len) -+{ -+ u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf; -+ union i2c_smbus_data data; -+ int ret; -+ -+ bus_addr -= 0x40; -+ -+ while (len > 0) { -+ data.byte = *val++; -+ ret = i2c_smbus_xfer(sfp->i2c, i2c_mii_phy_addr(bus_addr), 0, -+ I2C_SMBUS_WRITE, dev_addr, -+ I2C_SMBUS_BYTE_DATA, &data); -+ if (ret) -+ return ret; -+ dev_addr++; -+ len--; -+ } -+ -+ return val - (u8 *)buf; -+} -+ - static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) - { -- if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) -- return -EINVAL; -+ if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) { -+ if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { -+ sfp->i2c = i2c; -+ sfp->read = sfp_smbus_read; -+ sfp->write = sfp_smbus_write; -+ -+ return 0; -+ } else -+ return -EINVAL; -+ } - - sfp->i2c = i2c; - sfp->read = sfp_i2c_read; -@@ -710,6 +764,29 @@ static int sfp_i2c_mdiobus_create(struct - return 0; - } - -+static int sfp_sm_mdiobus_create(struct sfp *sfp) -+{ -+ struct mii_bus *sm_mii; -+ int ret; -+ -+ sm_mii = mdio_smbus_alloc(sfp->dev, sfp->i2c, sfp->mdio_protocol); -+ if (IS_ERR(sm_mii)) -+ return PTR_ERR(sm_mii); -+ -+ sm_mii->name = "SFP SMBus"; -+ sm_mii->phy_mask = ~0; -+ -+ ret = mdiobus_register(sm_mii); -+ if (ret < 0) { -+ mdiobus_free(sm_mii); -+ return ret; -+ } -+ -+ sfp->i2c_mii = sm_mii; -+ -+ return 0; -+} -+ - static void sfp_i2c_mdiobus_destroy(struct sfp *sfp) - { - mdiobus_unregister(sfp->i2c_mii); -@@ -1884,9 +1961,15 @@ static void sfp_sm_fault(struct sfp *sfp - - static int sfp_sm_add_mdio_bus(struct sfp *sfp) - { -- if (sfp->mdio_protocol != MDIO_I2C_NONE) -+ if (sfp->mdio_protocol == MDIO_I2C_NONE) -+ return 0; -+ -+ if (i2c_check_functionality(sfp->i2c, I2C_FUNC_I2C)) - return sfp_i2c_mdiobus_create(sfp); - -+ if (i2c_check_functionality(sfp->i2c, I2C_FUNC_SMBUS_BYTE_DATA)) -+ return sfp_sm_mdiobus_create(sfp); -+ - return 0; - } - diff --git a/target/linux/realtek/patches-6.6/716-net-ethernet-add-support-for-rtl838x-ethernet.patch b/target/linux/realtek/patches-6.6/716-net-ethernet-add-support-for-rtl838x-ethernet.patch deleted file mode 100644 index e0c10bca63..0000000000 --- a/target/linux/realtek/patches-6.6/716-net-ethernet-add-support-for-rtl838x-ethernet.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 26 Nov 2020 12:02:21 +0100 -Subject: net: ethernet: Add support for RTL838x ethernet - -* rename the target to realtek -* add refactored DSA driver -* add latest gpio driver -* lots of arch cleanups -* new irq driver -* additional boards - -Submitted-by: Bert Vermeulen -Submitted-by: Birger Koblitz -Submitted-by: Sander Vanheule -Submitted-by: Bjørn Mork -Submitted-by: John Crispin ---- - drivers/net/ethernet/Kconfig | 7 +- - drivers/net/ethernet/Makefile | 1 + - 2 files changed, 8 insertions(+) - ---- a/drivers/net/ethernet/Kconfig -+++ b/drivers/net/ethernet/Kconfig -@@ -170,6 +170,13 @@ source "drivers/net/ethernet/rdc/Kconfig - source "drivers/net/ethernet/realtek/Kconfig" - source "drivers/net/ethernet/renesas/Kconfig" - source "drivers/net/ethernet/rocker/Kconfig" -+ -+config NET_RTL838X -+ tristate "Realtek rtl838x Ethernet MAC support" -+ depends on MACH_REALTEK_RTL -+ help -+ Say Y here if you want to use the Realtek rtl838x Gbps Ethernet MAC. -+ - source "drivers/net/ethernet/samsung/Kconfig" - source "drivers/net/ethernet/seeq/Kconfig" - source "drivers/net/ethernet/sgi/Kconfig" ---- a/drivers/net/ethernet/Makefile -+++ b/drivers/net/ethernet/Makefile -@@ -81,6 +81,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += real - obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/ - obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ - obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/ -+obj-$(CONFIG_NET_RTL838X) += rtl838x_eth.o - obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/ - obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/ - obj-$(CONFIG_NET_VENDOR_SILAN) += silan/ diff --git a/target/linux/realtek/patches-6.6/718-net-dsa-add-support-for-rtl838x-switch.patch b/target/linux/realtek/patches-6.6/718-net-dsa-add-support-for-rtl838x-switch.patch deleted file mode 100644 index 786fc37091..0000000000 --- a/target/linux/realtek/patches-6.6/718-net-dsa-add-support-for-rtl838x-switch.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 26 Nov 2020 12:02:21 +0100 -Subject: net: dsa: Add support for rtl838x switch - -* rename the target to realtek -* add refactored DSA driver -* add latest gpio driver -* lots of arch cleanups -* new irq driver -* additional boards - -Submitted-by: Bert Vermeulen -Submitted-by: Birger Koblitz -Submitted-by: Sander Vanheule -Submitted-by: Bjørn Mork -Submitted-by: John Crispin ---- - drivers/net/dsa/rtl83xx/Kconfig | 2 ++ - drivers/net/dsa/rtl83xx/Makefile | 1 + - 2 files changed, 3 insertions(+) - ---- a/drivers/net/dsa/Kconfig -+++ b/drivers/net/dsa/Kconfig -@@ -89,6 +89,8 @@ source "drivers/net/dsa/xrs700x/Kconfig" - - source "drivers/net/dsa/realtek/Kconfig" - -+source "drivers/net/dsa/rtl83xx/Kconfig" -+ - config NET_DSA_RZN1_A5PSW - tristate "Renesas RZ/N1 A5PSW Ethernet switch support" - depends on OF && ARCH_RZN1 ---- a/drivers/net/dsa/Makefile -+++ b/drivers/net/dsa/Makefile -@@ -24,5 +24,6 @@ obj-y += mv88e6xxx/ - obj-y += ocelot/ - obj-y += qca/ - obj-y += realtek/ -+obj-y += rtl83xx/ - obj-y += sja1105/ - obj-y += xrs700x/ diff --git a/target/linux/realtek/patches-6.6/720-add-rtl-phy.patch b/target/linux/realtek/patches-6.6/720-add-rtl-phy.patch deleted file mode 100644 index c7078cea4e..0000000000 --- a/target/linux/realtek/patches-6.6/720-add-rtl-phy.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 89f71ebb355c624320c2b0ace8ae9488ff53cbeb Mon Sep 17 00:00:00 2001 -From: Birger Koblitz -Date: Tue, 5 Jan 2021 20:40:52 +0100 -Subject: PHY: Add realtek PHY - -This fixes the build problems for the REALTEK target by adding a proper -configuration option for the phy module. - -Submitted-by: Birger Koblitz ---- - drivers/net/phy/Kconfig | 6 ++++++ - drivers/net/phy/Makefile | 1 + - 2 files changed, 7 insertions(+) - ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -409,6 +409,12 @@ source "drivers/net/phy/realtek/Kconfig" - - source "drivers/net/phy/rtl8261n/Kconfig" - -+config REALTEK_SOC_PHY -+ tristate "Realtek SoC PHYs" -+ depends on MACH_REALTEK_RTL -+ help -+ Supports the PHYs found in combination with Realtek Switch SoCs -+ - config RENESAS_PHY - tristate "Renesas PHYs" - help ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -101,6 +101,7 @@ obj-y += qcom/ - obj-$(CONFIG_QSEMI_PHY) += qsemi.o - obj-$(CONFIG_REALTEK_PHY) += realtek/ - obj-y += rtl8261n/ -+obj-$(CONFIG_REALTEK_SOC_PHY) += rtl83xx-phy.o - obj-$(CONFIG_RENESAS_PHY) += uPD60620.o - obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o - obj-$(CONFIG_SMSC_PHY) += smsc.o diff --git a/target/linux/realtek/patches-6.6/722-net-dsa-add-rtl838x-support-for-tag-trailer.patch b/target/linux/realtek/patches-6.6/722-net-dsa-add-rtl838x-support-for-tag-trailer.patch deleted file mode 100644 index 348c59092a..0000000000 --- a/target/linux/realtek/patches-6.6/722-net-dsa-add-rtl838x-support-for-tag-trailer.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 26 Nov 2020 12:02:21 +0100 -Subject: net: dsa: Add rtl838x support for tag trailer - -* rename the target to realtek -* add refactored DSA driver -* add latest gpio driver -* lots of arch cleanups -* new irq driver -* additional boards - -Submitted-by: Bert Vermeulen -Submitted-by: Birger Koblitz -Submitted-by: Sander Vanheule -Submitted-by: Bjørn Mork -Submitted-by: John Crispin ---- - net/dsa/tag_trailer.c | 16 +++++++++++++- - 1 file changed, 17 insertions(+), 1 deletion(-) - ---- a/net/dsa/tag_trailer.c -+++ b/net/dsa/tag_trailer.c -@@ -19,7 +19,12 @@ static struct sk_buff *trailer_xmit(stru - - trailer = skb_put(skb, 4); - trailer[0] = 0x80; -+ -+#ifdef CONFIG_NET_DSA_RTL83XX -+ trailer[1] = dp->index; -+#else - trailer[1] = 1 << dp->index; -+#endif /* CONFIG_NET_DSA_RTL838X */ - trailer[2] = 0x10; - trailer[3] = 0x00; - -@@ -35,12 +40,23 @@ static struct sk_buff *trailer_rcv(struc - return NULL; - - trailer = skb_tail_pointer(skb) - 4; -+ -+#ifdef CONFIG_NET_DSA_RTL83XX -+ if (trailer[0] != 0x80 || (trailer[1] & 0x80) != 0x00 || -+ (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) -+ return NULL; -+ -+ if (trailer[1] & 0x40) -+ skb->offload_fwd_mark = 1; -+ -+ source_port = trailer[1] & 0x3f; -+#else - if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 || - (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) - return NULL; - - source_port = trailer[1] & 7; -- -+#endif - skb->dev = dsa_master_find_slave(dev, 0, source_port); - if (!skb->dev) - return NULL; diff --git a/target/linux/realtek/patches-6.6/723-net-mdio-Add-Realtek-Otto-auxiliary-controller.patch b/target/linux/realtek/patches-6.6/723-net-mdio-Add-Realtek-Otto-auxiliary-controller.patch deleted file mode 100644 index 7315899b3f..0000000000 --- a/target/linux/realtek/patches-6.6/723-net-mdio-Add-Realtek-Otto-auxiliary-controller.patch +++ /dev/null @@ -1,227 +0,0 @@ -From ffb7da9aa25765b2115e7ff3ee4f6dafa60f5421 Mon Sep 17 00:00:00 2001 -From: Sander Vanheule -Date: Fri, 27 Dec 2024 14:55:31 +0100 -Subject: [PATCH] net: mdio: Add Realtek Otto auxiliary controller - -SoCs in Realtek's Otto platform such as the RTL8380, RTL8391, and -RTL9302 have a simple auxiliary MDIO controller that is commonly used to -manage RTL8231 GPIO expanders on switch devices. - -Add a new MDIO controller driver supporting the RTL838x (maple), RTL839x -(cypress), and RTL930x (longan) SoCs. - -Signed-off-by: Sander Vanheule ---- - drivers/net/mdio/Kconfig | 10 ++ - drivers/net/mdio/Makefile | 1 + - drivers/net/mdio/mdio-realtek-otto-aux.c | 175 +++++++++++++++++++++++ - 3 files changed, 186 insertions(+) - create mode 100644 drivers/net/mdio/mdio-realtek-otto-aux.c - ---- a/drivers/net/mdio/Kconfig -+++ b/drivers/net/mdio/Kconfig -@@ -207,6 +207,16 @@ config MDIO_REGMAP - regmap. Users willing to use this driver must explicitly select - REGMAP. - -+config MDIO_REALTEK_OTTO_AUX -+ tristate "Realtek Otto auxiliary MDIO interface support" -+ default MACH_REALTEK_RTL -+ depends on MACH_REALTEK_RTL -+ depends on MFD_SYSCON -+ select MDIO_DEVRES -+ help -+ This driver supports the auxilairy MDIO bus on RTL838x SoCs. This bus -+ is typically used to attach RTL8231 GPIO extenders. -+ - config MDIO_THUNDER - tristate "ThunderX SOCs MDIO buses" - depends on 64BIT ---- a/drivers/net/mdio/Makefile -+++ b/drivers/net/mdio/Makefile -@@ -20,6 +20,7 @@ obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-ms - obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o - obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o - obj-$(CONFIG_MDIO_REGMAP) += mdio-regmap.o -+obj-$(CONFIG_MDIO_REALTEK_OTTO_AUX) += mdio-realtek-otto-aux.o - obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o - obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o - obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o ---- /dev/null -+++ b/drivers/net/mdio/mdio-realtek-otto-aux.c -@@ -0,0 +1,175 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define RTL8380_EXT_GPIO_INDIRECT_ACCESS 0xA09C -+#define RTL8390_EXT_GPIO_INDIRECT_ACCESS 0x0224 -+#define RTL9300_EXT_GPIO_INDIRECT_ACCESS 0xC620 -+ -+#define RTL83XX_AUX_MDIO_DATA_OFFSET 16 -+#define RTL83XX_AUX_MDIO_RCMD_FAIL 0 -+ -+#define RTL93XX_AUX_MDIO_DATA_OFFSET 12 -+#define RTL93XX_AUX_MDIO_RCMD_FAIL BIT(28) -+ -+#define REALTEK_AUX_MDIO_REG GENMASK(11, 7) -+#define REALTEK_AUX_MDIO_PHY_ADDR GENMASK(6, 2) -+#define REALTEK_AUX_MDIO_WRITE BIT(1) -+#define REALTEK_AUX_MDIO_READ 0 -+#define REALTEK_AUX_MDIO_EXEC BIT(0) -+ -+struct realtek_aux_mdio_info { -+ unsigned int cmd_reg; -+ unsigned int data_offset; -+ unsigned int rcmd_fail_mask; -+ unsigned int timeout_us; -+}; -+ -+static const struct realtek_aux_mdio_info info_rtl838x = { -+ .cmd_reg = RTL8380_EXT_GPIO_INDIRECT_ACCESS, -+ .data_offset = RTL83XX_AUX_MDIO_DATA_OFFSET, -+ .rcmd_fail_mask = RTL83XX_AUX_MDIO_RCMD_FAIL, -+ .timeout_us = 1700, -+}; -+ -+static const struct realtek_aux_mdio_info info_rtl839x = { -+ .cmd_reg = RTL8390_EXT_GPIO_INDIRECT_ACCESS, -+ .data_offset = RTL83XX_AUX_MDIO_DATA_OFFSET, -+ .rcmd_fail_mask = RTL83XX_AUX_MDIO_RCMD_FAIL, -+ .timeout_us = 4120, -+}; -+ -+static const struct realtek_aux_mdio_info info_rtl930x = { -+ .cmd_reg = RTL9300_EXT_GPIO_INDIRECT_ACCESS, -+ .data_offset = RTL93XX_AUX_MDIO_DATA_OFFSET, -+ .rcmd_fail_mask = RTL93XX_AUX_MDIO_RCMD_FAIL, -+ .timeout_us = 19000, -+}; -+ -+struct realtek_aux_mdio_ctrl { -+ struct device *dev; -+ struct regmap *map; -+ const struct realtek_aux_mdio_info *info; -+}; -+ -+#define mii_bus_to_ctrl(bus) ((struct realtek_aux_mdio_ctrl *) bus->priv) -+ -+static int realtek_aux_mdio_cmd(struct realtek_aux_mdio_ctrl *ctrl, int addr, int regnum, -+ u32 rw_bit, u16 *data) -+{ -+ unsigned int cmd; -+ int err; -+ -+ cmd = rw_bit | REALTEK_AUX_MDIO_EXEC; -+ cmd |= FIELD_PREP(REALTEK_AUX_MDIO_PHY_ADDR, addr); -+ cmd |= FIELD_PREP(REALTEK_AUX_MDIO_REG, regnum); -+ -+ if (rw_bit == REALTEK_AUX_MDIO_WRITE) -+ cmd |= *data << ctrl->info->data_offset; -+ -+ err = regmap_write(ctrl->map, ctrl->info->cmd_reg, cmd); -+ if (err) -+ return err; -+ -+ err = regmap_read_poll_timeout_atomic(ctrl->map, ctrl->info->cmd_reg, cmd, -+ !(cmd & REALTEK_AUX_MDIO_EXEC), 3, ctrl->info->timeout_us); -+ if (err) -+ return err; -+ -+ if (rw_bit == REALTEK_AUX_MDIO_READ) { -+ if (cmd & ctrl->info->rcmd_fail_mask) -+ return -EIO; -+ -+ *data = (cmd >> ctrl->info->data_offset) & GENMASK(15, 0); -+ } -+ -+ return 0; -+} -+ -+static int realtek_aux_mdio_read(struct mii_bus *bus, int addr, int regnum) -+{ -+ struct realtek_aux_mdio_ctrl *ctrl = mii_bus_to_ctrl(bus); -+ u16 data; -+ int err; -+ -+ err = realtek_aux_mdio_cmd(ctrl, addr, regnum, REALTEK_AUX_MDIO_READ, &data); -+ -+ if (err) -+ return err; -+ else -+ return data; -+} -+ -+static int realtek_aux_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) -+{ -+ struct realtek_aux_mdio_ctrl *ctrl = mii_bus_to_ctrl(bus); -+ -+ return realtek_aux_mdio_cmd(ctrl, addr, regnum, REALTEK_AUX_MDIO_WRITE, &val); -+} -+ -+static int realtek_aux_mdio_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct realtek_aux_mdio_ctrl *ctrl; -+ struct mii_bus *bus; -+ -+ bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*ctrl)); -+ if (!bus) -+ return -ENOMEM; -+ -+ ctrl = bus->priv; -+ ctrl->dev = &pdev->dev; -+ ctrl->info = (const struct realtek_aux_mdio_info *) device_get_match_data(ctrl->dev); -+ ctrl->map = syscon_node_to_regmap(np->parent); -+ if (IS_ERR(ctrl->map)) -+ return PTR_ERR(ctrl->map); -+ -+ bus->name = "Realtek auxiliary MDIO bus"; -+ snprintf(bus->id, MII_BUS_ID_SIZE, "realtek-aux-mdio") ; -+ bus->parent = ctrl->dev; -+ bus->read = realtek_aux_mdio_read; -+ bus->write = realtek_aux_mdio_write; -+ /* Don't have interrupts */ -+ for (unsigned int i = 0; i < PHY_MAX_ADDR; i++) -+ bus->irq[i] = PHY_POLL; -+ -+ return devm_of_mdiobus_register(ctrl->dev, bus, np); -+} -+ -+static const struct of_device_id realtek_aux_mdio_of_match[] = { -+ { -+ .compatible = "realtek,rtl8380-aux-mdio", -+ .data = &info_rtl838x, -+ }, -+ { -+ .compatible = "realtek,rtl8390-aux-mdio", -+ .data = &info_rtl839x, -+ }, -+ { -+ .compatible = "realtek,rtl9300-aux-mdio", -+ .data = &info_rtl930x, -+ }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, realtek_aux_mdio_of_match); -+ -+static struct platform_driver realtek_aux_mdio_driver = { -+ .driver = { -+ .name = "realtek-otto-aux-mdio", -+ .of_match_table = realtek_aux_mdio_of_match -+ }, -+ .probe = realtek_aux_mdio_probe, -+}; -+module_platform_driver(realtek_aux_mdio_driver); -+ -+MODULE_AUTHOR("Sander Vanheule "); -+MODULE_DESCRIPTION("Realtek otto auxiliary MDIO bus"); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/realtek/patches-6.6/800-gpio-regmap-Bypass-cache-for-shadowed-outputs.patch b/target/linux/realtek/patches-6.6/800-gpio-regmap-Bypass-cache-for-shadowed-outputs.patch deleted file mode 100644 index b4dbf320b2..0000000000 --- a/target/linux/realtek/patches-6.6/800-gpio-regmap-Bypass-cache-for-shadowed-outputs.patch +++ /dev/null @@ -1,56 +0,0 @@ -From b3f79468c90d8770f007d628a1e32b2d5d44a5c2 Mon Sep 17 00:00:00 2001 -From: Sander Vanheule -Date: Sat, 15 May 2021 11:57:32 +0200 -Subject: [PATCH] gpio: regmap: Bypass cache for shadowed outputs - -Some chips have the read-only input and write-only output data registers -aliased to the same offset, but do not perform direction multiplexing on -writes. Upon writing the register, this then always updates the output -value, even when the pin is configured as input. As a result it is not -safe to perform read-modify-writes on output pins, when other pins are -still configured as input. - -For example, on a bit-banged I2C bus, where the lines are switched -between out-low and in (with external pull-up) - - OUT(L) IN OUT(H) -SCK ....../''''''|'''''' - -SDA '''''''''\.......... - ^ ^- SCK switches to direction to OUT, but now has a high - | value, breaking the clock. - | - \- Perform RMW to update SDA. This reads the current input - value for SCK, updates the SDA value and writes back a 1 - for SCK as well. - -If a register is used for both the data input and data output (and is -not marked as volatile) the driver should ensure the cache is not -updated on register reads. This ensures proper functioning of writing -the output register with regmap_update_bits(), which will then use and -update the cache only on register writes. - -Signed-off-by: Sander Vanheule ---- - drivers/gpio/gpio-regmap.c | 10 +++++++++- - 1 file changed, 9 insertions(+), 1 deletion(-) - ---- a/drivers/gpio/gpio-regmap.c -+++ b/drivers/gpio/gpio-regmap.c -@@ -74,7 +74,15 @@ static int gpio_regmap_get(struct gpio_c - if (ret) - return ret; - -- ret = regmap_read(gpio->regmap, reg, &val); -+ /* -+ * Ensure we don't spoil the register cache with pin input values and -+ * perform a bypassed read. This way the cache (if any) is only used and -+ * updated on register writes. -+ */ -+ if (gpio->reg_dat_base == gpio->reg_set_base) -+ ret = regmap_read_bypassed(gpio->regmap, reg, &val); -+ else -+ ret = regmap_read(gpio->regmap, reg, &val); - if (ret) - return ret; - diff --git a/target/linux/realtek/patches-6.6/802-mfd-Add-RTL8231-core-device.patch b/target/linux/realtek/patches-6.6/802-mfd-Add-RTL8231-core-device.patch deleted file mode 100644 index df4d4aa143..0000000000 --- a/target/linux/realtek/patches-6.6/802-mfd-Add-RTL8231-core-device.patch +++ /dev/null @@ -1,330 +0,0 @@ -From 4e3455e058d40eb2a7326016494e3c81dc506c33 Mon Sep 17 00:00:00 2001 -From: Sander Vanheule -Date: Mon, 10 May 2021 18:33:01 +0200 -Subject: [PATCH] mfd: Add RTL8231 core device - -The RTL8231 is implemented as an MDIO device, and provides a regmap -interface for register access by the core and child devices. - -The chip can also be a device on an SMI bus, an I2C-like bus by Realtek. -Since kernel support for SMI is limited, and no real-world SMI -implementations have been encountered for this device, this is currently -unimplemented. The use of the regmap interface should make any future -support relatively straightforward. - -After reset, all pins are muxed to GPIO inputs before the pin drivers -are enabled. This is done to prevent accidental system resets, when a -pin is connected to the parent SoC's reset line. - -To provide different read and write semantics for the GPIO data -registers, a secondary virtual register range is used to enable separate -caching properties of pin input and output values. - -Signed-off-by: Sander Vanheule ---- - drivers/mfd/Kconfig | 9 ++ - drivers/mfd/Makefile | 1 + - drivers/mfd/rtl8231.c | 193 ++++++++++++++++++++++++++++++++++++ - include/linux/mfd/rtl8231.h | 71 +++++++++++++ - 4 files changed, 274 insertions(+) - create mode 100644 drivers/mfd/rtl8231.c - create mode 100644 include/linux/mfd/rtl8231.h - ---- a/drivers/mfd/Kconfig -+++ b/drivers/mfd/Kconfig -@@ -1171,6 +1171,15 @@ config MFD_RDC321X - southbridge which provides access to GPIOs and Watchdog using the - southbridge PCI device configuration space. - -+config MFD_RTL8231 -+ tristate "Realtek RTL8231 GPIO and LED expander" -+ select MFD_CORE -+ select REGMAP_MDIO -+ help -+ Support for the Realtek RTL8231 GPIO and LED expander. -+ Provides up to 37 GPIOs, 88 LEDs, and one PWM output. -+ When built as a module, this module will be named rtl8231. -+ - config MFD_RT4831 - tristate "Richtek RT4831 four channel WLED and Display Bias Voltage" - depends on I2C ---- a/drivers/mfd/Makefile -+++ b/drivers/mfd/Makefile -@@ -240,6 +240,7 @@ obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421- - obj-$(CONFIG_MFD_HI6421_SPMI) += hi6421-spmi-pmic.o - obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o - obj-$(CONFIG_MFD_DLN2) += dln2.o -+obj-$(CONFIG_MFD_RTL8231) += rtl8231.o - obj-$(CONFIG_MFD_RT4831) += rt4831.o - obj-$(CONFIG_MFD_RT5033) += rt5033.o - obj-$(CONFIG_MFD_RT5120) += rt5120.o ---- /dev/null -+++ b/drivers/mfd/rtl8231.c -@@ -0,0 +1,193 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+static bool rtl8231_volatile_reg(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ /* -+ * Registers with self-clearing bits, strapping pin values. -+ * Don't mark the data registers as volatile, since we need -+ * caching for the output values. -+ */ -+ case RTL8231_REG_FUNC0: -+ case RTL8231_REG_FUNC1: -+ case RTL8231_REG_PIN_HI_CFG: -+ case RTL8231_REG_LED_END: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+static const struct reg_field RTL8231_FIELD_LED_START = REG_FIELD(RTL8231_REG_FUNC0, 1, 1); -+ -+static const struct mfd_cell rtl8231_cells[] = { -+ { -+ .name = "rtl8231-pinctrl", -+ }, -+ { -+ .name = "rtl8231-leds", -+ .of_compatible = "realtek,rtl8231-leds", -+ }, -+}; -+ -+static int rtl8231_soft_reset(struct regmap *map) -+{ -+ const unsigned int all_pins_mask = GENMASK(RTL8231_BITS_VAL - 1, 0); -+ unsigned int val; -+ int err; -+ -+ /* SOFT_RESET bit self-clears when done */ -+ regmap_write_bits(map, RTL8231_REG_PIN_HI_CFG, -+ RTL8231_PIN_HI_CFG_SOFT_RESET, RTL8231_PIN_HI_CFG_SOFT_RESET); -+ err = regmap_read_poll_timeout(map, RTL8231_REG_PIN_HI_CFG, val, -+ !(val & RTL8231_PIN_HI_CFG_SOFT_RESET), 50, 1000); -+ if (err) -+ return err; -+ -+ regcache_mark_dirty(map); -+ -+ /* -+ * Chip reset results in a pin configuration that is a mix of LED and GPIO outputs. -+ * Select GPI functionality for all pins before enabling pin outputs. -+ */ -+ regmap_write(map, RTL8231_REG_PIN_MODE0, all_pins_mask); -+ regmap_write(map, RTL8231_REG_GPIO_DIR0, all_pins_mask); -+ regmap_write(map, RTL8231_REG_PIN_MODE1, all_pins_mask); -+ regmap_write(map, RTL8231_REG_GPIO_DIR1, all_pins_mask); -+ regmap_write(map, RTL8231_REG_PIN_HI_CFG, -+ RTL8231_PIN_HI_CFG_MODE_MASK | RTL8231_PIN_HI_CFG_DIR_MASK); -+ -+ return 0; -+} -+ -+static int rtl8231_init(struct device *dev, struct regmap *map) -+{ -+ struct regmap_field *led_start; -+ unsigned int started; -+ unsigned int val; -+ int err; -+ -+ err = regmap_read(map, RTL8231_REG_FUNC1, &val); -+ if (err) { -+ dev_err(dev, "failed to read READY_CODE\n"); -+ return err; -+ } -+ -+ val = FIELD_GET(RTL8231_FUNC1_READY_CODE_MASK, val); -+ if (val != RTL8231_FUNC1_READY_CODE_VALUE) { -+ dev_err(dev, "RTL8231 not present or ready 0x%x != 0x%x\n", -+ val, RTL8231_FUNC1_READY_CODE_VALUE); -+ return -ENODEV; -+ } -+ -+ led_start = dev_get_drvdata(dev); -+ err = regmap_field_read(led_start, &started); -+ if (err) -+ return err; -+ -+ if (!started) { -+ err = rtl8231_soft_reset(map); -+ if (err) -+ return err; -+ /* LED_START enables power to output pins, and starts the LED engine */ -+ err = regmap_field_force_write(led_start, 1); -+ } -+ -+ return err; -+} -+ -+static const struct regmap_config rtl8231_mdio_regmap_config = { -+ .val_bits = RTL8231_BITS_VAL, -+ .reg_bits = RTL8231_BITS_REG, -+ .volatile_reg = rtl8231_volatile_reg, -+ .max_register = RTL8231_REG_COUNT - 1, -+ .use_single_read = true, -+ .use_single_write = true, -+ .reg_format_endian = REGMAP_ENDIAN_BIG, -+ .val_format_endian = REGMAP_ENDIAN_BIG, -+ /* Cannot use REGCACHE_FLAT because it's not smart enough about cache invalidation */ -+ .cache_type = REGCACHE_RBTREE, -+}; -+ -+static int rtl8231_mdio_probe(struct mdio_device *mdiodev) -+{ -+ struct device *dev = &mdiodev->dev; -+ struct regmap_field *led_start; -+ struct regmap *map; -+ int err; -+ -+ map = devm_regmap_init_mdio(mdiodev, &rtl8231_mdio_regmap_config); -+ if (IS_ERR(map)) { -+ dev_err(dev, "failed to init regmap\n"); -+ return PTR_ERR(map); -+ } -+ -+ led_start = devm_regmap_field_alloc(dev, map, RTL8231_FIELD_LED_START); -+ if (IS_ERR(led_start)) -+ return PTR_ERR(led_start); -+ -+ dev_set_drvdata(dev, led_start); -+ -+ mdiodev->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); -+ if (IS_ERR(mdiodev->reset_gpio)) -+ return PTR_ERR(mdiodev->reset_gpio); -+ -+ device_property_read_u32(dev, "reset-assert-delay", &mdiodev->reset_assert_delay); -+ device_property_read_u32(dev, "reset-deassert-delay", &mdiodev->reset_deassert_delay); -+ -+ err = rtl8231_init(dev, map); -+ if (err) -+ return err; -+ -+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, rtl8231_cells, -+ ARRAY_SIZE(rtl8231_cells), NULL, 0, NULL); -+} -+ -+__maybe_unused static int rtl8231_suspend(struct device *dev) -+{ -+ struct regmap_field *led_start = dev_get_drvdata(dev); -+ -+ return regmap_field_force_write(led_start, 0); -+} -+ -+__maybe_unused static int rtl8231_resume(struct device *dev) -+{ -+ struct regmap_field *led_start = dev_get_drvdata(dev); -+ -+ return regmap_field_force_write(led_start, 1); -+} -+ -+static SIMPLE_DEV_PM_OPS(rtl8231_pm_ops, rtl8231_suspend, rtl8231_resume); -+ -+static const struct of_device_id rtl8231_of_match[] = { -+ { .compatible = "realtek,rtl8231" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, rtl8231_of_match); -+ -+static struct mdio_driver rtl8231_mdio_driver = { -+ .mdiodrv.driver = { -+ .name = "rtl8231-expander", -+ .of_match_table = rtl8231_of_match, -+ .pm = pm_ptr(&rtl8231_pm_ops), -+ }, -+ .probe = rtl8231_mdio_probe, -+}; -+mdio_module_driver(rtl8231_mdio_driver); -+ -+MODULE_AUTHOR("Sander Vanheule "); -+MODULE_DESCRIPTION("Realtek RTL8231 GPIO and LED expander"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/include/linux/mfd/rtl8231.h -@@ -0,0 +1,71 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Register definitions the RTL8231 GPIO and LED expander chip -+ */ -+ -+#ifndef __LINUX_MFD_RTL8231_H -+#define __LINUX_MFD_RTL8231_H -+ -+#include -+ -+/* -+ * Registers addresses are 5 bit, values are 16 bit -+ * Also define a duplicated range of virtual addresses, to enable -+ * different read/write behaviour on the GPIO data registers -+ */ -+#define RTL8231_BITS_VAL 16 -+#define RTL8231_BITS_REG 5 -+ -+/* Chip control */ -+#define RTL8231_REG_FUNC0 0x00 -+#define RTL8231_FUNC0_SCAN_MODE BIT(0) -+#define RTL8231_FUNC0_SCAN_SINGLE 0 -+#define RTL8231_FUNC0_SCAN_BICOLOR BIT(0) -+ -+#define RTL8231_REG_FUNC1 0x01 -+#define RTL8231_FUNC1_READY_CODE_VALUE 0x37 -+#define RTL8231_FUNC1_READY_CODE_MASK GENMASK(9, 4) -+#define RTL8231_FUNC1_DEBOUNCE_MASK GENMASK(15, 10) -+ -+/* Pin control */ -+#define RTL8231_REG_PIN_MODE0 0x02 -+#define RTL8231_REG_PIN_MODE1 0x03 -+ -+#define RTL8231_PIN_MODE_LED 0 -+#define RTL8231_PIN_MODE_GPIO 1 -+ -+/* Pin high config: pin and GPIO control for pins 32-26 */ -+#define RTL8231_REG_PIN_HI_CFG 0x04 -+#define RTL8231_PIN_HI_CFG_MODE_MASK GENMASK(4, 0) -+#define RTL8231_PIN_HI_CFG_DIR_MASK GENMASK(9, 5) -+#define RTL8231_PIN_HI_CFG_INV_MASK GENMASK(14, 10) -+#define RTL8231_PIN_HI_CFG_SOFT_RESET BIT(15) -+ -+/* GPIO control registers */ -+#define RTL8231_REG_GPIO_DIR0 0x05 -+#define RTL8231_REG_GPIO_DIR1 0x06 -+#define RTL8231_REG_GPIO_INVERT0 0x07 -+#define RTL8231_REG_GPIO_INVERT1 0x08 -+ -+#define RTL8231_GPIO_DIR_IN 1 -+#define RTL8231_GPIO_DIR_OUT 0 -+ -+/* -+ * GPIO data registers -+ * Only the output data can be written to these registers, and only the input -+ * data can be read. -+ */ -+#define RTL8231_REG_GPIO_DATA0 0x1c -+#define RTL8231_REG_GPIO_DATA1 0x1d -+#define RTL8231_REG_GPIO_DATA2 0x1e -+#define RTL8231_PIN_HI_DATA_MASK GENMASK(4, 0) -+ -+/* LED control base registers */ -+#define RTL8231_REG_LED0_BASE 0x09 -+#define RTL8231_REG_LED1_BASE 0x10 -+#define RTL8231_REG_LED2_BASE 0x17 -+#define RTL8231_REG_LED_END 0x1b -+ -+#define RTL8231_REG_COUNT 0x1f -+ -+#endif /* __LINUX_MFD_RTL8231_H */ diff --git a/target/linux/realtek/patches-6.6/803-pinctrl-Add-RTL8231-pin-control-and-GPIO-support.patch b/target/linux/realtek/patches-6.6/803-pinctrl-Add-RTL8231-pin-control-and-GPIO-support.patch deleted file mode 100644 index de0f5ee868..0000000000 --- a/target/linux/realtek/patches-6.6/803-pinctrl-Add-RTL8231-pin-control-and-GPIO-support.patch +++ /dev/null @@ -1,581 +0,0 @@ -From 098324288a63a6dcc44e96cc381aef3d5c48d89e Mon Sep 17 00:00:00 2001 -From: Sander Vanheule -Date: Mon, 10 May 2021 22:15:31 +0200 -Subject: [PATCH] pinctrl: Add RTL8231 pin control and GPIO support - -This driver implements the GPIO and pin muxing features provided by the -RTL8231. The device should be instantiated as an MFD child, where the -parent device has already configured the regmap used for register -access. - -Debouncing is only available for the six highest GPIOs, and must be -emulated when other pins are used for (button) inputs. Although -described in the bindings, drive strength selection is currently not -implemented. - -Signed-off-by: Sander Vanheule ---- - drivers/pinctrl/Kconfig | 11 + - drivers/pinctrl/Makefile | 1 + - drivers/pinctrl/pinctrl-rtl8231.c | 521 ++++++++++++++++++++++++++++++ - 3 files changed, 533 insertions(+) - create mode 100644 drivers/pinctrl/pinctrl-rtl8231.c - ---- a/drivers/pinctrl/Kconfig -+++ b/drivers/pinctrl/Kconfig -@@ -417,6 +417,17 @@ config PINCTRL_ROCKCHIP - help - This support pinctrl and GPIO driver for Rockchip SoCs. - -+config PINCTRL_RTL8231 -+ tristate "Realtek RTL8231 GPIO expander's pin controller" -+ depends on MFD_RTL8231 -+ default MFD_RTL8231 -+ select GPIO_REGMAP -+ select GENERIC_PINCONF -+ select GENERIC_PINMUX_FUNCTIONS -+ help -+ Support for RTL8231 expander's GPIOs and pin controller. -+ When built as a module, the module will be called pinctrl-rtl8231. -+ - config PINCTRL_SINGLE - tristate "One-register-per-pin type device tree based pinctrl driver" - depends on OF ---- a/drivers/pinctrl/Makefile -+++ b/drivers/pinctrl/Makefile -@@ -43,6 +43,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-p - obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o - obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o - obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o -+obj-$(CONFIG_PINCTRL_RTL8231) += pinctrl-rtl8231.o - obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o - obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o - obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o ---- /dev/null -+++ b/drivers/pinctrl/pinctrl-rtl8231.c -@@ -0,0 +1,525 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "core.h" -+#include "pinmux.h" -+#include -+ -+#define RTL8231_NUM_GPIOS 37 -+#define RTL8231_DEBOUNCE_USEC 100000 -+#define RTL8231_DEBOUNCE_MIN_OFFSET 31 -+ -+struct rtl8231_pin_ctrl { -+ struct pinctrl_desc pctl_desc; -+ struct regmap *map; -+}; -+ -+/* -+ * Pin controller functionality -+ */ -+static const char * const rtl8231_pin_function_names[] = { -+ "gpio", -+ "led", -+ "pwm", -+}; -+ -+enum rtl8231_pin_function { -+ RTL8231_PIN_FUNCTION_GPIO = BIT(0), -+ RTL8231_PIN_FUNCTION_LED = BIT(1), -+ RTL8231_PIN_FUNCTION_PWM = BIT(2), -+}; -+ -+struct rtl8231_pin_desc { -+ const enum rtl8231_pin_function functions; -+ const u8 reg; -+ const u8 offset; -+ const u8 gpio_function_value; -+}; -+ -+#define RTL8231_PIN_DESC(_num, _func, _reg, _fld, _val) \ -+ [_num] = { \ -+ .functions = RTL8231_PIN_FUNCTION_GPIO | _func, \ -+ .reg = _reg, \ -+ .offset = _fld, \ -+ .gpio_function_value = _val, \ -+ } -+#define RTL8231_GPIO_PIN_DESC(_num, _reg, _fld) \ -+ RTL8231_PIN_DESC(_num, 0, _reg, _fld, RTL8231_PIN_MODE_GPIO) -+#define RTL8231_LED_PIN_DESC(_num, _reg, _fld) \ -+ RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_LED, _reg, _fld, RTL8231_PIN_MODE_GPIO) -+#define RTL8231_PWM_PIN_DESC(_num, _reg, _fld) \ -+ RTL8231_PIN_DESC(_num, RTL8231_PIN_FUNCTION_PWM, _reg, _fld, 0) -+ -+/* -+ * All pins have a GPIO/LED mux bit, but the bits for pins 35/36 are read-only. Use this bit -+ * for the GPIO-only pin instead of a placeholder, so the rest of the logic can stay generic. -+ */ -+static struct rtl8231_pin_desc rtl8231_pin_data[RTL8231_NUM_GPIOS] = { -+ RTL8231_LED_PIN_DESC(0, RTL8231_REG_PIN_MODE0, 0), -+ RTL8231_LED_PIN_DESC(1, RTL8231_REG_PIN_MODE0, 1), -+ RTL8231_LED_PIN_DESC(2, RTL8231_REG_PIN_MODE0, 2), -+ RTL8231_LED_PIN_DESC(3, RTL8231_REG_PIN_MODE0, 3), -+ RTL8231_LED_PIN_DESC(4, RTL8231_REG_PIN_MODE0, 4), -+ RTL8231_LED_PIN_DESC(5, RTL8231_REG_PIN_MODE0, 5), -+ RTL8231_LED_PIN_DESC(6, RTL8231_REG_PIN_MODE0, 6), -+ RTL8231_LED_PIN_DESC(7, RTL8231_REG_PIN_MODE0, 7), -+ RTL8231_LED_PIN_DESC(8, RTL8231_REG_PIN_MODE0, 8), -+ RTL8231_LED_PIN_DESC(9, RTL8231_REG_PIN_MODE0, 9), -+ RTL8231_LED_PIN_DESC(10, RTL8231_REG_PIN_MODE0, 10), -+ RTL8231_LED_PIN_DESC(11, RTL8231_REG_PIN_MODE0, 11), -+ RTL8231_LED_PIN_DESC(12, RTL8231_REG_PIN_MODE0, 12), -+ RTL8231_LED_PIN_DESC(13, RTL8231_REG_PIN_MODE0, 13), -+ RTL8231_LED_PIN_DESC(14, RTL8231_REG_PIN_MODE0, 14), -+ RTL8231_LED_PIN_DESC(15, RTL8231_REG_PIN_MODE0, 15), -+ RTL8231_LED_PIN_DESC(16, RTL8231_REG_PIN_MODE1, 0), -+ RTL8231_LED_PIN_DESC(17, RTL8231_REG_PIN_MODE1, 1), -+ RTL8231_LED_PIN_DESC(18, RTL8231_REG_PIN_MODE1, 2), -+ RTL8231_LED_PIN_DESC(19, RTL8231_REG_PIN_MODE1, 3), -+ RTL8231_LED_PIN_DESC(20, RTL8231_REG_PIN_MODE1, 4), -+ RTL8231_LED_PIN_DESC(21, RTL8231_REG_PIN_MODE1, 5), -+ RTL8231_LED_PIN_DESC(22, RTL8231_REG_PIN_MODE1, 6), -+ RTL8231_LED_PIN_DESC(23, RTL8231_REG_PIN_MODE1, 7), -+ RTL8231_LED_PIN_DESC(24, RTL8231_REG_PIN_MODE1, 8), -+ RTL8231_LED_PIN_DESC(25, RTL8231_REG_PIN_MODE1, 9), -+ RTL8231_LED_PIN_DESC(26, RTL8231_REG_PIN_MODE1, 10), -+ RTL8231_LED_PIN_DESC(27, RTL8231_REG_PIN_MODE1, 11), -+ RTL8231_LED_PIN_DESC(28, RTL8231_REG_PIN_MODE1, 12), -+ RTL8231_LED_PIN_DESC(29, RTL8231_REG_PIN_MODE1, 13), -+ RTL8231_LED_PIN_DESC(30, RTL8231_REG_PIN_MODE1, 14), -+ RTL8231_LED_PIN_DESC(31, RTL8231_REG_PIN_MODE1, 15), -+ RTL8231_LED_PIN_DESC(32, RTL8231_REG_PIN_HI_CFG, 0), -+ RTL8231_LED_PIN_DESC(33, RTL8231_REG_PIN_HI_CFG, 1), -+ RTL8231_LED_PIN_DESC(34, RTL8231_REG_PIN_HI_CFG, 2), -+ RTL8231_PWM_PIN_DESC(35, RTL8231_REG_FUNC1, 3), -+ RTL8231_GPIO_PIN_DESC(36, RTL8231_REG_PIN_HI_CFG, 4), -+}; -+ -+#define RTL8231_PIN(_num) \ -+ { \ -+ .number = _num, \ -+ .name = "gpio" #_num, \ -+ .drv_data = &rtl8231_pin_data[_num] \ -+ } -+ -+static const struct pinctrl_pin_desc rtl8231_pins[RTL8231_NUM_GPIOS] = { -+ RTL8231_PIN(0), -+ RTL8231_PIN(1), -+ RTL8231_PIN(2), -+ RTL8231_PIN(3), -+ RTL8231_PIN(4), -+ RTL8231_PIN(5), -+ RTL8231_PIN(6), -+ RTL8231_PIN(7), -+ RTL8231_PIN(8), -+ RTL8231_PIN(9), -+ RTL8231_PIN(10), -+ RTL8231_PIN(11), -+ RTL8231_PIN(12), -+ RTL8231_PIN(13), -+ RTL8231_PIN(14), -+ RTL8231_PIN(15), -+ RTL8231_PIN(16), -+ RTL8231_PIN(17), -+ RTL8231_PIN(18), -+ RTL8231_PIN(19), -+ RTL8231_PIN(20), -+ RTL8231_PIN(21), -+ RTL8231_PIN(22), -+ RTL8231_PIN(23), -+ RTL8231_PIN(24), -+ RTL8231_PIN(25), -+ RTL8231_PIN(26), -+ RTL8231_PIN(27), -+ RTL8231_PIN(28), -+ RTL8231_PIN(29), -+ RTL8231_PIN(30), -+ RTL8231_PIN(31), -+ RTL8231_PIN(32), -+ RTL8231_PIN(33), -+ RTL8231_PIN(34), -+ RTL8231_PIN(35), -+ RTL8231_PIN(36), -+}; -+ -+static int rtl8231_get_groups_count(struct pinctrl_dev *pctldev) -+{ -+ return ARRAY_SIZE(rtl8231_pins); -+} -+ -+static const char *rtl8231_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector) -+{ -+ return rtl8231_pins[selector].name; -+} -+ -+static int rtl8231_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, -+ const unsigned int **pins, unsigned int *num_pins) -+{ -+ if (selector >= ARRAY_SIZE(rtl8231_pins)) -+ return -EINVAL; -+ -+ *pins = &rtl8231_pins[selector].number; -+ *num_pins = 1; -+ -+ return 0; -+} -+ -+static const struct pinctrl_ops rtl8231_pinctrl_ops = { -+ .get_groups_count = rtl8231_get_groups_count, -+ .get_group_name = rtl8231_get_group_name, -+ .get_group_pins = rtl8231_get_group_pins, -+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all, -+ .dt_free_map = pinconf_generic_dt_free_map, -+}; -+ -+static int rtl8231_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, -+ unsigned int group_selector) -+{ -+ const struct function_desc *func = pinmux_generic_get_function(pctldev, func_selector); -+ const struct rtl8231_pin_desc *desc = rtl8231_pins[group_selector].drv_data; -+ const struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); -+ unsigned int func_flag = (uintptr_t) func->data; -+ unsigned int function_mask; -+ unsigned int gpio_function; -+ -+ if (!(desc->functions & func_flag)) -+ return -EINVAL; -+ -+ function_mask = BIT(desc->offset); -+ gpio_function = desc->gpio_function_value << desc->offset; -+ -+ if (func_flag == RTL8231_PIN_FUNCTION_GPIO) -+ return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function); -+ else -+ return regmap_update_bits(ctrl->map, desc->reg, function_mask, ~gpio_function); -+} -+ -+static int rtl8231_gpio_request_enable(struct pinctrl_dev *pctldev, -+ struct pinctrl_gpio_range *range, unsigned int offset) -+{ -+ const struct rtl8231_pin_desc *desc = rtl8231_pins[offset].drv_data; -+ struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); -+ unsigned int function_mask; -+ unsigned int gpio_function; -+ -+ function_mask = BIT(desc->offset); -+ gpio_function = desc->gpio_function_value << desc->offset; -+ -+ return regmap_update_bits(ctrl->map, desc->reg, function_mask, gpio_function); -+} -+ -+static const struct pinmux_ops rtl8231_pinmux_ops = { -+ .get_functions_count = pinmux_generic_get_function_count, -+ .get_function_name = pinmux_generic_get_function_name, -+ .get_function_groups = pinmux_generic_get_function_groups, -+ .set_mux = rtl8231_set_mux, -+ .gpio_request_enable = rtl8231_gpio_request_enable, -+ .strict = true, -+}; -+ -+static int rtl8231_pin_config_get(struct pinctrl_dev *pctldev, unsigned int offset, -+ unsigned long *config) -+{ -+ struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); -+ unsigned int param = pinconf_to_config_param(*config); -+ unsigned int arg; -+ int err; -+ int v; -+ -+ switch (param) { -+ case PIN_CONFIG_INPUT_DEBOUNCE: -+ if (offset < RTL8231_DEBOUNCE_MIN_OFFSET) -+ return -EINVAL; -+ -+ err = regmap_read(ctrl->map, RTL8231_REG_FUNC1, &v); -+ if (err) -+ return err; -+ -+ v = FIELD_GET(RTL8231_FUNC1_DEBOUNCE_MASK, v); -+ if (v & BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET)) -+ arg = RTL8231_DEBOUNCE_USEC; -+ else -+ arg = 0; -+ break; -+ default: -+ return -ENOTSUPP; -+ } -+ -+ *config = pinconf_to_config_packed(param, arg); -+ -+ return 0; -+} -+ -+static int rtl8231_pin_config_set(struct pinctrl_dev *pctldev, unsigned int offset, -+ unsigned long *configs, unsigned int num_configs) -+{ -+ struct rtl8231_pin_ctrl *ctrl = pinctrl_dev_get_drvdata(pctldev); -+ unsigned int param, arg; -+ unsigned int pin_mask; -+ int err; -+ int i; -+ -+ for (i = 0; i < num_configs; i++) { -+ param = pinconf_to_config_param(configs[i]); -+ arg = pinconf_to_config_argument(configs[i]); -+ -+ switch (param) { -+ case PIN_CONFIG_INPUT_DEBOUNCE: -+ if (offset < RTL8231_DEBOUNCE_MIN_OFFSET) -+ return -EINVAL; -+ -+ pin_mask = FIELD_PREP(RTL8231_FUNC1_DEBOUNCE_MASK, -+ BIT(offset - RTL8231_DEBOUNCE_MIN_OFFSET)); -+ -+ switch (arg) { -+ case 0: -+ err = regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1, -+ pin_mask, 0); -+ break; -+ case RTL8231_DEBOUNCE_USEC: -+ err = regmap_update_bits(ctrl->map, RTL8231_REG_FUNC1, -+ pin_mask, pin_mask); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ break; -+ default: -+ return -ENOTSUPP; -+ } -+ } -+ -+ return err; -+} -+ -+static const struct pinconf_ops rtl8231_pinconf_ops = { -+ .is_generic = true, -+ .pin_config_get = rtl8231_pin_config_get, -+ .pin_config_set = rtl8231_pin_config_set, -+}; -+ -+static int rtl8231_pinctrl_init_functions(struct pinctrl_dev *pctl, struct rtl8231_pin_ctrl *ctrl) -+{ -+ const char *function_name; -+ const char **groups; -+ unsigned int f_idx; -+ unsigned int pin; -+ int num_groups; -+ int err; -+ -+ for (f_idx = 0; f_idx < ARRAY_SIZE(rtl8231_pin_function_names); f_idx++) { -+ function_name = rtl8231_pin_function_names[f_idx]; -+ -+ for (pin = 0, num_groups = 0; pin < ctrl->pctl_desc.npins; pin++) -+ if (rtl8231_pin_data[pin].functions & BIT(f_idx)) -+ num_groups++; -+ -+ groups = devm_kcalloc(pctl->dev, num_groups, sizeof(*groups), GFP_KERNEL); -+ if (!groups) -+ return -ENOMEM; -+ -+ for (pin = 0, num_groups = 0; pin < ctrl->pctl_desc.npins; pin++) -+ if (rtl8231_pin_data[pin].functions & BIT(f_idx)) -+ groups[num_groups++] = rtl8231_pins[pin].name; -+ -+ err = pinmux_generic_add_function(pctl, function_name, groups, num_groups, -+ (void *) BIT(f_idx)); -+ if (err < 0) -+ return err; -+ } -+ -+ return 0; -+} -+ -+struct pin_field_info { -+ const struct reg_field gpio_data; -+ const struct reg_field gpio_dir; -+ const struct reg_field mode; -+}; -+ -+static const struct pin_field_info pin_fields[] = { -+ { -+ .gpio_data = REG_FIELD(RTL8231_REG_GPIO_DATA0, 0, 15), -+ .gpio_dir = REG_FIELD(RTL8231_REG_GPIO_DIR0, 0, 15), -+ .mode = REG_FIELD(RTL8231_REG_PIN_MODE0, 0, 15), -+ }, -+ { -+ .gpio_data = REG_FIELD(RTL8231_REG_GPIO_DATA1, 0, 15), -+ .gpio_dir = REG_FIELD(RTL8231_REG_GPIO_DIR1, 0, 15), -+ .mode = REG_FIELD(RTL8231_REG_PIN_MODE1, 0, 15), -+ }, -+ { -+ .gpio_data = REG_FIELD(RTL8231_REG_GPIO_DATA2, 0, 4), -+ .gpio_dir = REG_FIELD(RTL8231_REG_PIN_HI_CFG, 5, 9), -+ .mode = REG_FIELD(RTL8231_REG_PIN_HI_CFG, 0, 4), -+ }, -+}; -+ -+static int rtl8231_configure_safe(struct device *dev, struct regmap *map) -+{ -+ struct regmap_field *field_data; -+ struct regmap_field *field_mode; -+ struct regmap_field *field_dir; -+ unsigned int is_output; -+ unsigned int is_gpio; -+ unsigned int data; -+ unsigned int mode; -+ unsigned int dir; -+ int err; -+ -+ for (unsigned int i = 0; i < ARRAY_SIZE(pin_fields); i++) { -+ field_data = devm_regmap_field_alloc(dev, map, pin_fields[i].gpio_data); -+ if (IS_ERR(field_data)) -+ return PTR_ERR(field_data); -+ -+ field_dir = devm_regmap_field_alloc(dev, map, pin_fields[i].gpio_dir); -+ if (IS_ERR(field_dir)) -+ return PTR_ERR(field_dir); -+ -+ field_mode = devm_regmap_field_alloc(dev, map, pin_fields[i].mode); -+ if (IS_ERR(field_mode)) -+ return PTR_ERR(field_mode); -+ -+ /* The register cache is invalid at start-up, so this should read from HW */ -+ err = regmap_field_read(field_data, &data); -+ if (err) -+ return err; -+ -+ err = regmap_field_read(field_dir, &dir); -+ if (err) -+ return err; -+ -+ err = regmap_field_read(field_mode, &mode); -+ if (err) -+ return err; -+ -+ /* Write back only the GPIO-out values to fix the cache */ -+ data &= ~dir; -+ regmap_field_write(field_data, data); -+ -+ /* -+ * Set every pin that is configured as gpio-output but muxed for the alternative -+ * (LED) function to gpio-in. That way the pin will be high impedance when it is -+ * muxed to GPIO, preventing unwanted glitches. -+ * The pin muxes are left as-is, so there are no signal changes. -+ */ -+ is_gpio = mode; -+ is_output = ~dir; -+ regmap_field_write(field_dir, dir | (~is_gpio & is_output)); -+ -+ devm_regmap_field_free(dev, field_data); -+ devm_regmap_field_free(dev, field_dir); -+ devm_regmap_field_free(dev, field_mode); -+ } -+ -+ return 0; -+} -+ -+static int rtl8231_pinctrl_init(struct device *dev, struct rtl8231_pin_ctrl *ctrl) -+{ -+ struct pinctrl_dev *pctldev; -+ int err; -+ -+ ctrl->pctl_desc.name = "rtl8231-pinctrl"; -+ ctrl->pctl_desc.owner = THIS_MODULE; -+ ctrl->pctl_desc.confops = &rtl8231_pinconf_ops; -+ ctrl->pctl_desc.pctlops = &rtl8231_pinctrl_ops; -+ ctrl->pctl_desc.pmxops = &rtl8231_pinmux_ops; -+ ctrl->pctl_desc.npins = ARRAY_SIZE(rtl8231_pins); -+ ctrl->pctl_desc.pins = rtl8231_pins; -+ -+ err = devm_pinctrl_register_and_init(dev->parent, &ctrl->pctl_desc, ctrl, &pctldev); -+ if (err) { -+ dev_err(dev, "failed to register pin controller\n"); -+ return err; -+ } -+ -+ err = rtl8231_pinctrl_init_functions(pctldev, ctrl); -+ if (err) -+ return err; -+ -+ err = pinctrl_enable(pctldev); -+ if (err) -+ dev_err(dev, "failed to enable pin controller\n"); -+ -+ return err; -+} -+ -+/* -+ * GPIO controller functionality -+ */ -+static int rtl8231_gpio_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base, -+ unsigned int offset, unsigned int *reg, unsigned int *mask) -+{ -+ unsigned int pin_mask = BIT(offset % RTL8231_BITS_VAL); -+ -+ if (base == RTL8231_REG_GPIO_DATA0 || offset < 32) { -+ *reg = base + offset / RTL8231_BITS_VAL; -+ *mask = pin_mask; -+ } else if (base == RTL8231_REG_GPIO_DIR0) { -+ *reg = RTL8231_REG_PIN_HI_CFG; -+ *mask = FIELD_PREP(RTL8231_PIN_HI_CFG_DIR_MASK, pin_mask); -+ } else { -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int rtl8231_pinctrl_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct rtl8231_pin_ctrl *ctrl; -+ struct gpio_regmap_config gpio_cfg = {}; -+ int err; -+ -+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); -+ if (!ctrl) -+ return -ENOMEM; -+ -+ ctrl->map = dev_get_regmap(dev->parent, NULL); -+ if (!ctrl->map) -+ return -ENODEV; -+ -+ err = rtl8231_configure_safe(dev, ctrl->map); -+ if (err) -+ return err; -+ -+ err = rtl8231_pinctrl_init(dev, ctrl); -+ if (err) -+ return err; -+ -+ gpio_cfg.regmap = ctrl->map; -+ gpio_cfg.parent = dev->parent; -+ gpio_cfg.ngpio = RTL8231_NUM_GPIOS; -+ gpio_cfg.ngpio_per_reg = RTL8231_BITS_VAL; -+ -+ gpio_cfg.reg_dat_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0); -+ gpio_cfg.reg_set_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DATA0); -+ gpio_cfg.reg_dir_in_base = GPIO_REGMAP_ADDR(RTL8231_REG_GPIO_DIR0); -+ -+ gpio_cfg.reg_mask_xlate = rtl8231_gpio_reg_mask_xlate; -+ -+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_cfg)); -+} -+ -+static struct platform_driver rtl8231_pinctrl_driver = { -+ .driver = { -+ .name = "rtl8231-pinctrl", -+ }, -+ .probe = rtl8231_pinctrl_probe, -+}; -+module_platform_driver(rtl8231_pinctrl_driver); -+ -+MODULE_AUTHOR("Sander Vanheule "); -+MODULE_DESCRIPTION("Realtek RTL8231 pin control and GPIO support"); -+MODULE_LICENSE("GPL"); diff --git a/target/linux/realtek/patches-6.6/804-leds-Add-support-for-RTL8231-LED-scan-matrix.patch b/target/linux/realtek/patches-6.6/804-leds-Add-support-for-RTL8231-LED-scan-matrix.patch deleted file mode 100644 index 37542b8230..0000000000 --- a/target/linux/realtek/patches-6.6/804-leds-Add-support-for-RTL8231-LED-scan-matrix.patch +++ /dev/null @@ -1,338 +0,0 @@ -From 6b797a97c007e46d6081fc6f4b41ce8407078605 Mon Sep 17 00:00:00 2001 -From: Sander Vanheule -Date: Mon, 10 May 2021 22:16:11 +0200 -Subject: [PATCH] leds: Add support for RTL8231 LED scan matrix - -Both single and bi-color scanning modes are supported. The driver will -verify that the addresses are valid for the current mode, before -registering the LEDs. LEDs can be turned on, off, or toggled at one of -six predefined rates from 40ms to 1280ms. - -Implements a platform device for use as a child device with RTL8231 MFD, -and uses the parent regmap to access the required registers. - -Signed-off-by: Sander Vanheule ---- - drivers/leds/Kconfig | 10 ++ - drivers/leds/Makefile | 1 + - drivers/leds/leds-rtl8231.c | 291 ++++++++++++++++++++++++++++++++++++ - 3 files changed, 302 insertions(+) - create mode 100644 drivers/leds/leds-rtl8231.c - ---- a/drivers/leds/Kconfig -+++ b/drivers/leds/Kconfig -@@ -586,6 +586,16 @@ config LEDS_REGULATOR - help - This option enables support for regulator driven LEDs. - -+config LEDS_RTL8231 -+ tristate "RTL8231 LED matrix support" -+ depends on LEDS_CLASS -+ depends on MFD_RTL8231 -+ default MFD_RTL8231 -+ help -+ This option enables support for using the LED scanning matrix output -+ of the RTL8231 GPIO and LED expander chip. -+ When built as a module, this module will be named leds-rtl8231. -+ - config LEDS_BD2606MVV - tristate "LED driver for BD2606MVV" - depends on LEDS_CLASS ---- a/drivers/leds/Makefile -+++ b/drivers/leds/Makefile -@@ -77,6 +77,7 @@ obj-$(CONFIG_LEDS_PM8058) += leds-pm805 - obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o - obj-$(CONFIG_LEDS_PWM) += leds-pwm.o - obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o -+obj-$(CONFIG_LEDS_RTL8231) += leds-rtl8231.o - obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o - obj-$(CONFIG_LEDS_ST1202) += leds-st1202.o - obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o ---- /dev/null -+++ b/drivers/leds/leds-rtl8231.c -@@ -0,0 +1,285 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+/** -+ * struct led_toggle_rate - description of an LED blinking mode -+ * @interval_ms: LED toggle rate in milliseconds -+ * @mode: Register field value used to activate this mode -+ * -+ * For LED hardware accelerated blinking, with equal on and off delay. -+ * Both delays are given by @interval, so the interval at which the LED blinks -+ * (i.e. turn on and off once) is double this value. -+ */ -+struct led_toggle_rate { -+ u16 interval_ms; -+ u8 mode; -+}; -+ -+/** -+ * struct led_modes - description of all LED modes -+ * @toggle_rates: Array of led_toggle_rate values, sorted by ascending interval -+ * @num_toggle_rates: Number of elements in @led_toggle_rate -+ * @off: Register field value to turn LED off -+ * @on: Register field value to turn LED on -+ */ -+struct led_modes { -+ const struct led_toggle_rate *toggle_rates; -+ unsigned int num_toggle_rates; -+ u8 off; -+ u8 on; -+}; -+ -+struct rtl8231_led { -+ struct led_classdev led; -+ const struct led_modes *modes; -+ struct regmap_field *reg_field; -+}; -+#define to_rtl8231_led(_cdev) container_of(_cdev, struct rtl8231_led, led) -+ -+#define RTL8231_NUM_LEDS 3 -+#define RTL8231_LED_PER_REG 5 -+#define RTL8231_BITS_PER_LED 3 -+ -+static const unsigned int rtl8231_led_port_counts_single[RTL8231_NUM_LEDS] = {32, 32, 24}; -+static const unsigned int rtl8231_led_port_counts_bicolor[RTL8231_NUM_LEDS] = {24, 24, 24}; -+ -+static const unsigned int rtl8231_led_base[RTL8231_NUM_LEDS] = { -+ RTL8231_REG_LED0_BASE, -+ RTL8231_REG_LED1_BASE, -+ RTL8231_REG_LED2_BASE, -+}; -+ -+#define RTL8231_DEFAULT_TOGGLE_INTERVAL_MS 500 -+ -+static const struct led_toggle_rate rtl8231_toggle_rates[] = { -+ { 40, 1}, -+ { 80, 2}, -+ { 160, 3}, -+ { 320, 4}, -+ { 640, 5}, -+ {1280, 6}, -+}; -+ -+static const struct led_modes rtl8231_led_modes = { -+ .off = 0, -+ .on = 7, -+ .num_toggle_rates = ARRAY_SIZE(rtl8231_toggle_rates), -+ .toggle_rates = rtl8231_toggle_rates, -+}; -+ -+static void rtl8231_led_brightness_set(struct led_classdev *led_cdev, -+ enum led_brightness brightness) -+{ -+ struct rtl8231_led *pled = to_rtl8231_led(led_cdev); -+ -+ if (brightness) -+ regmap_field_write(pled->reg_field, pled->modes->on); -+ else -+ regmap_field_write(pled->reg_field, pled->modes->off); -+} -+ -+static enum led_brightness rtl8231_led_brightness_get(struct led_classdev *led_cdev) -+{ -+ struct rtl8231_led *pled = to_rtl8231_led(led_cdev); -+ u32 current_mode = pled->modes->off; -+ -+ regmap_field_read(pled->reg_field, ¤t_mode); -+ -+ if (current_mode == pled->modes->off) -+ return LED_OFF; -+ else -+ return LED_ON; -+} -+ -+static unsigned int rtl8231_led_current_interval(struct rtl8231_led *pled) -+{ -+ unsigned int mode; -+ unsigned int i; -+ -+ if (regmap_field_read(pled->reg_field, &mode)) -+ return 0; -+ -+ for (i = 0; i < pled->modes->num_toggle_rates; i++) -+ if (mode == pled->modes->toggle_rates[i].mode) -+ return pled->modes->toggle_rates[i].interval_ms; -+ -+ return 0; -+} -+ -+static int rtl8231_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, -+ unsigned long *delay_off) -+{ -+ struct rtl8231_led *pled = to_rtl8231_led(led_cdev); -+ const struct led_toggle_rate *rates = pled->modes->toggle_rates; -+ unsigned int num_rates = pled->modes->num_toggle_rates; -+ unsigned int interval_ms; -+ unsigned int i; -+ int err; -+ -+ if (*delay_on == 0 && *delay_off == 0) { -+ interval_ms = RTL8231_DEFAULT_TOGGLE_INTERVAL_MS; -+ } else { -+ /* -+ * If the current mode is blinking, choose the delay that (likely) changed. -+ * Otherwise, choose the interval that would have the same total delay. -+ */ -+ interval_ms = rtl8231_led_current_interval(pled); -+ if (interval_ms > 0 && interval_ms == *delay_off) -+ interval_ms = *delay_on; -+ else if (interval_ms > 0 && interval_ms == *delay_on) -+ interval_ms = *delay_off; -+ else -+ interval_ms = (*delay_on + *delay_off) / 2; -+ } -+ -+ /* Find clamped toggle interval */ -+ for (i = 0; i < (num_rates - 1); i++) -+ if (interval_ms > rates[i].interval_ms) -+ break; -+ -+ interval_ms = rates[i].interval_ms; -+ -+ err = regmap_field_write(pled->reg_field, rates[i].mode); -+ if (err) -+ return err; -+ -+ *delay_on = interval_ms; -+ *delay_off = interval_ms; -+ -+ return 0; -+} -+ -+static int rtl8231_led_read_address(struct fwnode_handle *fwnode, unsigned int *addr_port, -+ unsigned int *addr_led) -+{ -+ u32 addr[2]; -+ int err; -+ -+ err = fwnode_property_count_u32(fwnode, "reg"); -+ if (err < 0) -+ return err; -+ if (err != ARRAY_SIZE(addr)) -+ return -EINVAL; -+ -+ err = fwnode_property_read_u32_array(fwnode, "reg", addr, ARRAY_SIZE(addr)); -+ if (err) -+ return err; -+ -+ *addr_port = addr[0]; -+ *addr_led = addr[1]; -+ -+ return 0; -+} -+ -+static const struct regmap_field *rtl8231_led_get_field(struct device *dev, struct regmap *map, -+ unsigned int port_index, unsigned int led_index) -+{ -+ unsigned int offset = port_index / RTL8231_LED_PER_REG; -+ unsigned int shift = (port_index % RTL8231_LED_PER_REG) * RTL8231_BITS_PER_LED; -+ const struct reg_field field = REG_FIELD(rtl8231_led_base[led_index] + offset, shift, -+ shift + RTL8231_BITS_PER_LED - 1); -+ -+ return devm_regmap_field_alloc(dev, map, field); -+} -+ -+static int rtl8231_led_probe_single(struct device *dev, struct regmap *map, -+ const unsigned int *port_counts, struct fwnode_handle *fwnode) -+{ -+ struct led_init_data init_data = {}; -+ struct rtl8231_led *pled; -+ unsigned int port_index; -+ unsigned int led_index; -+ int err; -+ -+ pled = devm_kzalloc(dev, sizeof(*pled), GFP_KERNEL); -+ if (!pled) -+ return -ENOMEM; -+ -+ err = rtl8231_led_read_address(fwnode, &port_index, &led_index); -+ if (err) { -+ dev_err(dev, "LED address invalid"); -+ return err; -+ } -+ -+ if (led_index >= RTL8231_NUM_LEDS || port_index >= port_counts[led_index]) { -+ dev_err(dev, "LED address (%d.%d) invalid", port_index, led_index); -+ return -EINVAL; -+ } -+ -+ pled->reg_field = rtl8231_led_get_field(dev, map, port_index, led_index); -+ if (IS_ERR(pled->reg_field)) -+ return PTR_ERR(pled->reg_field); -+ -+ pled->modes = &rtl8231_led_modes; -+ -+ pled->led.max_brightness = 1; -+ pled->led.brightness_get = rtl8231_led_brightness_get; -+ pled->led.brightness_set = rtl8231_led_brightness_set; -+ pled->led.blink_set = rtl8231_led_blink_set; -+ -+ init_data.fwnode = fwnode; -+ -+ return devm_led_classdev_register_ext(dev, &pled->led, &init_data); -+} -+ -+static int rtl8231_led_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ const unsigned int *port_counts; -+ struct fwnode_handle *child; -+ struct regmap *map; -+ int err; -+ -+ map = dev_get_regmap(dev->parent, NULL); -+ if (!map) -+ return -ENODEV; -+ -+ if (device_property_match_string(dev, "realtek,led-scan-mode", "single-color") >= 0) { -+ port_counts = rtl8231_led_port_counts_single; -+ regmap_update_bits(map, RTL8231_REG_FUNC0, -+ RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_SINGLE); -+ } else if (device_property_match_string(dev, "realtek,led-scan-mode", "bi-color") >= 0) { -+ port_counts = rtl8231_led_port_counts_bicolor; -+ regmap_update_bits(map, RTL8231_REG_FUNC0, -+ RTL8231_FUNC0_SCAN_MODE, RTL8231_FUNC0_SCAN_BICOLOR); -+ } else { -+ dev_err(dev, "scan mode missing or invalid"); -+ return -EINVAL; -+ } -+ -+ fwnode_for_each_available_child_node(dev->fwnode, child) { -+ err = rtl8231_led_probe_single(dev, map, port_counts, child); -+ if (err) -+ dev_warn(dev, "failed to register LED %pfwP", child); -+ } -+ -+ return 0; -+} -+ -+static const struct of_device_id of_rtl8231_led_match[] = { -+ { .compatible = "realtek,rtl8231-leds" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, of_rtl8231_led_match); -+ -+static struct platform_driver rtl8231_led_driver = { -+ .driver = { -+ .name = "rtl8231-leds", -+ .of_match_table = of_rtl8231_led_match, -+ }, -+ .probe = rtl8231_led_probe, -+}; -+module_platform_driver(rtl8231_led_driver); -+ -+MODULE_AUTHOR("Sander Vanheule "); -+MODULE_DESCRIPTION("Realtek RTL8231 LED support"); -+MODULE_LICENSE("GPL");