diff options
67 files changed, 8783 insertions, 0 deletions
diff --git a/target/linux/ramips/patches-5.10/0001-MIPS-cmdline-Clean-up-boot_command_line-initializati.patch b/target/linux/ramips/patches-5.10/0001-MIPS-cmdline-Clean-up-boot_command_line-initializati.patch new file mode 100644 index 0000000000..eedc7498be --- /dev/null +++ b/target/linux/ramips/patches-5.10/0001-MIPS-cmdline-Clean-up-boot_command_line-initializati.patch @@ -0,0 +1,192 @@ +From: Paul Burton <paul.burton@mips.com> +Date: Wed, 9 Oct 2019 23:09:45 +0000 +Subject: MIPS: cmdline: Clean up boot_command_line initialization + +Our current code to initialize boot_command_line is a mess. Some of this +is due to the addition of too many options over the years, and some of +this is due to workarounds for early_init_dt_scan_chosen() performing +actions specific to options from other architectures that probably +shouldn't be in generic code. + +Clean this up by introducing a new bootcmdline_init() function that +simplifies the initialization somewhat. The major changes are: + +- Because bootcmdline_init() is a function it can return early in the + CONFIG_CMDLINE_OVERRIDE case. + +- We clear boot_command_line rather than inheriting whatever + early_init_dt_scan_chosen() may have left us. This means we no longer + need to set boot_command_line to a space character in an attempt to + prevent early_init_dt_scan_chosen() from copying CONFIG_CMDLINE into + boot_command_line without us knowing about it. + +- Indirection via USE_PROM_CMDLINE, USE_DTB_CMDLINE, EXTEND_WITH_PROM & + BUILTIN_EXTEND_WITH_PROM macros is removed; they seemingly served only + to obfuscate the code. + +- The logic is cleaner, clearer & commented. + +Two minor drawbacks of this approach are: + +1) We call of_scan_flat_dt(), which means we scan through the DT again. + The overhead is fairly minimal & shouldn't be noticeable. + +2) cmdline_scan_chosen() duplicates a small amount of the logic from + early_init_dt_scan_chosen(). Alternatives might be to allow the + generic FDT code to keep & expose a copy of the arguments taken from + the /chosen node's bootargs property, or to introduce a function like + early_init_dt_scan_chosen() that retrieves them without modification + to handle CONFIG_CMDLINE. Neither of these sounds particularly + cleaner though, and this way we at least keep the extra work in + arch/mips. + +Origin: upstream, https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7784cac697351f0cc0a4bb619594c0c99348c5aa +Signed-off-by: Paul Burton <paul.burton@mips.com> +Cc: linux-mips@vger.kernel.org + +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -538,11 +538,88 @@ static void __init check_kernel_sections + } + } + +-#define USE_PROM_CMDLINE IS_ENABLED(CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER) +-#define USE_DTB_CMDLINE IS_ENABLED(CONFIG_MIPS_CMDLINE_FROM_DTB) +-#define EXTEND_WITH_PROM IS_ENABLED(CONFIG_MIPS_CMDLINE_DTB_EXTEND) +-#define BUILTIN_EXTEND_WITH_PROM \ +- IS_ENABLED(CONFIG_MIPS_CMDLINE_BUILTIN_EXTEND) ++static void __init bootcmdline_append(const char *s, size_t max) ++{ ++ if (!s[0] || !max) ++ return; ++ ++ if (boot_command_line[0]) ++ strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); ++ ++ strlcat(boot_command_line, s, max); ++} ++ ++static int __init bootcmdline_scan_chosen(unsigned long node, const char *uname, ++ int depth, void *data) ++{ ++ bool *dt_bootargs = data; ++ const char *p; ++ int l; ++ ++ if (depth != 1 || !data || ++ (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) ++ return 0; ++ ++ p = of_get_flat_dt_prop(node, "bootargs", &l); ++ if (p != NULL && l > 0) { ++ bootcmdline_append(p, min(l, COMMAND_LINE_SIZE)); ++ *dt_bootargs = true; ++ } ++ ++ return 1; ++} ++ ++static void __init bootcmdline_init(char **cmdline_p) ++{ ++ bool dt_bootargs = false; ++ ++ /* ++ * If CMDLINE_OVERRIDE is enabled then initializing the command line is ++ * trivial - we simply use the built-in command line unconditionally & ++ * unmodified. ++ */ ++ if (IS_ENABLED(CONFIG_CMDLINE_OVERRIDE)) { ++ strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); ++ return; ++ } ++ ++ /* ++ * If the user specified a built-in command line & ++ * MIPS_CMDLINE_BUILTIN_EXTEND, then the built-in command line is ++ * prepended to arguments from the bootloader or DT so we'll copy them ++ * to the start of boot_command_line here. Otherwise, empty ++ * boot_command_line to undo anything early_init_dt_scan_chosen() did. ++ */ ++ if (IS_ENABLED(CONFIG_MIPS_CMDLINE_BUILTIN_EXTEND)) ++ strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); ++ else ++ boot_command_line[0] = 0; ++ ++ /* ++ * If we're configured to take boot arguments from DT, look for those ++ * now. ++ */ ++ if (IS_ENABLED(CONFIG_MIPS_CMDLINE_FROM_DTB)) ++ of_scan_flat_dt(bootcmdline_scan_chosen, &dt_bootargs); ++ ++ /* ++ * If we didn't get any arguments from DT (regardless of whether that's ++ * because we weren't configured to look for them, or because we looked ++ * & found none) then we'll take arguments from the bootloader. ++ * plat_mem_setup() should have filled arcs_cmdline with arguments from ++ * the bootloader. ++ */ ++ if (IS_ENABLED(CONFIG_MIPS_CMDLINE_DTB_EXTEND) || !dt_bootargs) ++ bootcmdline_append(arcs_cmdline, COMMAND_LINE_SIZE); ++ ++ /* ++ * If the user specified a built-in command line & we didn't already ++ * prepend it, we append it to boot_command_line here. ++ */ ++ if (IS_ENABLED(CONFIG_CMDLINE_BOOL) && ++ !IS_ENABLED(CONFIG_MIPS_CMDLINE_BUILTIN_EXTEND)) ++ bootcmdline_append(builtin_cmdline, COMMAND_LINE_SIZE); ++} + + /* + * arch_mem_init - initialize memory management subsystem +@@ -570,48 +647,12 @@ static void __init arch_mem_init(char ** + { + extern void plat_mem_setup(void); + +- /* +- * Initialize boot_command_line to an innocuous but non-empty string in +- * order to prevent early_init_dt_scan_chosen() from copying +- * CONFIG_CMDLINE into it without our knowledge. We handle +- * CONFIG_CMDLINE ourselves below & don't want to duplicate its +- * content because repeating arguments can be problematic. +- */ +- strlcpy(boot_command_line, " ", COMMAND_LINE_SIZE); +- + /* call board setup routine */ + plat_mem_setup(); + memblock_set_bottom_up(true); + +-#if defined(CONFIG_CMDLINE_BOOL) && defined(CONFIG_CMDLINE_OVERRIDE) +- strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); +-#else +- if ((USE_PROM_CMDLINE && arcs_cmdline[0]) || +- (USE_DTB_CMDLINE && !boot_command_line[0])) +- strlcpy(boot_command_line, arcs_cmdline, COMMAND_LINE_SIZE); +- +- if (EXTEND_WITH_PROM && arcs_cmdline[0]) { +- if (boot_command_line[0]) +- strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); +- strlcat(boot_command_line, arcs_cmdline, COMMAND_LINE_SIZE); +- } +- +-#if defined(CONFIG_CMDLINE_BOOL) +- if (builtin_cmdline[0]) { +- if (boot_command_line[0]) +- strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); +- strlcat(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); +- } +- +- if (BUILTIN_EXTEND_WITH_PROM && arcs_cmdline[0]) { +- if (boot_command_line[0]) +- strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); +- strlcat(boot_command_line, arcs_cmdline, COMMAND_LINE_SIZE); +- } +-#endif +-#endif ++ bootcmdline_init(cmdline_p); + strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); +- + *cmdline_p = command_line; + + parse_early_param(); diff --git a/target/linux/ramips/patches-5.10/0002-MIPS-Always-define-builtin_cmdline.patch b/target/linux/ramips/patches-5.10/0002-MIPS-Always-define-builtin_cmdline.patch new file mode 100644 index 0000000000..03124d07d1 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0002-MIPS-Always-define-builtin_cmdline.patch @@ -0,0 +1,44 @@ +From b7340422cc16c5deff100812f38114bb5ec81203 Mon Sep 17 00:00:00 2001 +From: Paul Burton <paul.burton@mips.com> +Date: Sat, 12 Oct 2019 20:43:36 +0000 +Subject: [PATCH] MIPS: Always define builtin_cmdline + +Commit 7784cac69735 ("MIPS: cmdline: Clean up boot_command_line +initialization") made use of builtin_cmdline conditional upon plain C if +statements rather than preprocessor #ifdef's. This caused build failures +for configurations with CONFIG_CMDLINE_BOOL=n where builtin_cmdline +wasn't defined, for example: + + arch/mips/kernel/setup.c: In function 'bootcmdline_init': +>> arch/mips/kernel/setup.c:582:30: error: 'builtin_cmdline' undeclared + (first use in this function); did you mean 'builtin_driver'? + strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); + ^~~~~~~~~~~~~~~ + builtin_driver + arch/mips/kernel/setup.c:582:30: note: each undeclared identifier is + reported only once for each function it appears in + +Fix this by defining builtin_cmdline as an empty string in the affected +configurations. All of the paths that use it should be optimized out +anyway so the data itself gets optimized away too. + +Signed-off-by: Paul Burton <paul.burton@mips.com> +Fixes: 7784cac69735 ("MIPS: cmdline: Clean up boot_command_line initialization") +Reported-by: kbuild test robot <lkp@intel.com> +Reported-by: Nathan Chancellor <natechancellor@gmail.com> +Cc: linux-mips@vger.kernel.org +--- + arch/mips/kernel/setup.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -68,6 +68,8 @@ char __initdata arcs_cmdline[COMMAND_LIN + + #ifdef CONFIG_CMDLINE_BOOL + static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE; ++#else ++static const char builtin_cmdline[] __initconst = ""; + #endif + + /* diff --git a/target/linux/ramips/patches-5.10/0003-MIPS-Fix-memory-reservation-in-bootmem_init-for-cert.patch b/target/linux/ramips/patches-5.10/0003-MIPS-Fix-memory-reservation-in-bootmem_init-for-cert.patch new file mode 100644 index 0000000000..63429f49b0 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0003-MIPS-Fix-memory-reservation-in-bootmem_init-for-cert.patch @@ -0,0 +1,45 @@ +From: Tobias Wolf <dev-NTEO@vplace.de> +Subject: [v2] MIPS: Fix memory reservation in bootmem_init for certain non-usermem setups + +Commit 67a3ba25aa95 ("MIPS: Fix incorrect mem=X@Y handling") introduced a new +issue for rt288x where "PHYS_OFFSET" is 0x0 but the calculated "ramstart" is +not. As the prerequisite of custom memory map has been removed, this results +in the full memory range of 0x0 - 0x8000000 to be marked as reserved for this +platform. + +v2: Correctly compare that usermem is not null. + +This patch adds the originally intended prerequisite again. + +Signed-off-by: Tobias Wolf <dev-NTEO@vplace.de> +--- + +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -287,6 +287,8 @@ static unsigned long __init init_initrd( + * Initialize the bootmem allocator. It also setup initrd related data + * if needed. + */ ++static int usermem __initdata; ++ + #if defined(CONFIG_SGI_IP27) || (defined(CONFIG_CPU_LOONGSON3) && defined(CONFIG_NUMA)) + + static void __init bootmem_init(void) +@@ -325,7 +327,7 @@ static void __init bootmem_init(void) + /* + * Reserve any memory between the start of RAM and PHYS_OFFSET + */ +- if (ramstart > PHYS_OFFSET) ++ if (usermem && ramstart > PHYS_OFFSET) + memblock_reserve(PHYS_OFFSET, ramstart - PHYS_OFFSET); + + if (PFN_UP(ramstart) > ARCH_PFN_OFFSET) { +@@ -386,8 +388,6 @@ static void __init bootmem_init(void) + + #endif /* CONFIG_SGI_IP27 */ + +-static int usermem __initdata; +- + static int __init early_parse_mem(char *p) + { + phys_addr_t start, size; diff --git a/target/linux/ramips/patches-5.10/0005-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch b/target/linux/ramips/patches-5.10/0005-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch new file mode 100644 index 0000000000..773c74f4e9 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0005-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch @@ -0,0 +1,82 @@ +From ce3d4a4111a5f7e6b4e74bceae5faa6ce388e8ec Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Sun, 14 Jul 2013 23:08:11 +0200 +Subject: [PATCH 05/53] MIPS: use set_mode() to enable/disable the cevt-r4k + irq + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/ralink/Kconfig | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -1,12 +1,17 @@ + # SPDX-License-Identifier: GPL-2.0 + if RALINK + ++config CEVT_SYSTICK_QUIRK ++ bool ++ default n ++ + config CLKEVT_RT3352 + bool + depends on SOC_RT305X || SOC_MT7620 + default y + select TIMER_OF + select CLKSRC_MMIO ++ select CEVT_SYSTICK_QUIRK + + config RALINK_ILL_ACC + bool +--- a/arch/mips/kernel/cevt-r4k.c ++++ b/arch/mips/kernel/cevt-r4k.c +@@ -15,6 +15,26 @@ + #include <asm/time.h> + #include <asm/cevt-r4k.h> + ++static int mips_state_oneshot(struct clock_event_device *evt) ++{ ++ if (!cp0_timer_irq_installed) { ++ cp0_timer_irq_installed = 1; ++ setup_irq(evt->irq, &c0_compare_irqaction); ++ } ++ ++ return 0; ++} ++ ++static int mips_state_shutdown(struct clock_event_device *evt) ++{ ++ if (cp0_timer_irq_installed) { ++ cp0_timer_irq_installed = 0; ++ remove_irq(evt->irq, &c0_compare_irqaction); ++ } ++ ++ return 0; ++} ++ + static int mips_next_event(unsigned long delta, + struct clock_event_device *evt) + { +@@ -281,17 +301,21 @@ int r4k_clockevent_init(void) + cd->rating = 300; + cd->irq = irq; + cd->cpumask = cpumask_of(cpu); ++ cd->set_state_shutdown = mips_state_shutdown; ++ cd->set_state_oneshot = mips_state_oneshot; + cd->set_next_event = mips_next_event; + cd->event_handler = mips_event_handler; + + clockevents_config_and_register(cd, mips_hpt_frequency, min_delta, 0x7fffffff); + ++#ifndef CONFIG_CEVT_SYSTICK_QUIRK + if (cp0_timer_irq_installed) + return 0; + + cp0_timer_irq_installed = 1; + + setup_irq(irq, &c0_compare_irqaction); ++#endif + + return 0; + } diff --git a/target/linux/ramips/patches-5.10/0006-MIPS-ralink-add-cpu-frequency-scaling.patch b/target/linux/ramips/patches-5.10/0006-MIPS-ralink-add-cpu-frequency-scaling.patch new file mode 100644 index 0000000000..a3b8e4d250 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0006-MIPS-ralink-add-cpu-frequency-scaling.patch @@ -0,0 +1,200 @@ +From bd30f19a006fb52bac80c6463c49dd2f4159f4ac Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Sun, 28 Jul 2013 16:26:41 +0200 +Subject: [PATCH 06/53] MIPS: ralink: add cpu frequency scaling + +This feature will break udelay() and cause the delay loop to have longer delays +when the frequency is scaled causing a performance hit. + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/ralink/cevt-rt3352.c | 38 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +--- a/arch/mips/ralink/cevt-rt3352.c ++++ b/arch/mips/ralink/cevt-rt3352.c +@@ -29,6 +29,10 @@ + /* enable the counter */ + #define CFG_CNT_EN 0x1 + ++/* mt7620 frequency scaling defines */ ++#define CLK_LUT_CFG 0x40 ++#define SLEEP_EN BIT(31) ++ + struct systick_device { + void __iomem *membase; + struct clock_event_device dev; +@@ -36,21 +40,53 @@ struct systick_device { + int freq_scale; + }; + ++static void (*systick_freq_scaling)(struct systick_device *sdev, int status); ++ + static int systick_set_oneshot(struct clock_event_device *evt); + static int systick_shutdown(struct clock_event_device *evt); + ++static inline void mt7620_freq_scaling(struct systick_device *sdev, int status) ++{ ++ if (sdev->freq_scale == status) ++ return; ++ ++ sdev->freq_scale = status; ++ ++ pr_info("%s: %s autosleep mode\n", sdev->dev.name, ++ (status) ? ("enable") : ("disable")); ++ if (status) ++ rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) | SLEEP_EN, CLK_LUT_CFG); ++ else ++ rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) & ~SLEEP_EN, CLK_LUT_CFG); ++} ++ ++static inline unsigned int read_count(struct systick_device *sdev) ++{ ++ return ioread32(sdev->membase + SYSTICK_COUNT); ++} ++ ++static inline unsigned int read_compare(struct systick_device *sdev) ++{ ++ return ioread32(sdev->membase + SYSTICK_COMPARE); ++} ++ ++static inline void write_compare(struct systick_device *sdev, unsigned int val) ++{ ++ iowrite32(val, sdev->membase + SYSTICK_COMPARE); ++} ++ + static int systick_next_event(unsigned long delta, + struct clock_event_device *evt) + { + struct systick_device *sdev; +- u32 count; ++ int res; + + sdev = container_of(evt, struct systick_device, dev); +- count = ioread32(sdev->membase + SYSTICK_COUNT); +- count = (count + delta) % SYSTICK_FREQ; +- iowrite32(count, sdev->membase + SYSTICK_COMPARE); ++ delta += read_count(sdev); ++ write_compare(sdev, delta); ++ res = ((int)(read_count(sdev) - delta) >= 0) ? -ETIME : 0; + +- return 0; ++ return res; + } + + static void systick_event_handler(struct clock_event_device *dev) +@@ -60,20 +96,25 @@ static void systick_event_handler(struct + + static irqreturn_t systick_interrupt(int irq, void *dev_id) + { +- struct clock_event_device *dev = (struct clock_event_device *) dev_id; ++ int ret = 0; ++ struct clock_event_device *cdev; ++ struct systick_device *sdev; + +- dev->event_handler(dev); ++ if (read_c0_cause() & STATUSF_IP7) { ++ cdev = (struct clock_event_device *) dev_id; ++ sdev = container_of(cdev, struct systick_device, dev); ++ ++ /* Clear Count/Compare Interrupt */ ++ write_compare(sdev, read_compare(sdev)); ++ cdev->event_handler(cdev); ++ ret = 1; ++ } + +- return IRQ_HANDLED; ++ return IRQ_RETVAL(ret); + } + + static struct systick_device systick = { + .dev = { +- /* +- * cevt-r4k uses 300, make sure systick +- * gets used if available +- */ +- .rating = 310, + .features = CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = systick_next_event, + .set_state_shutdown = systick_shutdown, +@@ -95,9 +136,15 @@ static int systick_shutdown(struct clock + sdev = container_of(evt, struct systick_device, dev); + + if (sdev->irq_requested) +- free_irq(systick.dev.irq, &systick_irqaction); ++ remove_irq(systick.dev.irq, &systick_irqaction); + sdev->irq_requested = 0; +- iowrite32(0, systick.membase + SYSTICK_CONFIG); ++ iowrite32(CFG_CNT_EN, systick.membase + SYSTICK_CONFIG); ++ ++ if (systick_freq_scaling) ++ systick_freq_scaling(sdev, 0); ++ ++ if (systick_freq_scaling) ++ systick_freq_scaling(sdev, 1); + + return 0; + } +@@ -117,34 +164,48 @@ static int systick_set_oneshot(struct cl + return 0; + } + ++static const struct of_device_id systick_match[] = { ++ { .compatible = "ralink,mt7620a-systick", .data = mt7620_freq_scaling}, ++ {}, ++}; ++ + static int __init ralink_systick_init(struct device_node *np) + { ++ const struct of_device_id *match; ++ int rating = 200; + int ret; + + systick.membase = of_iomap(np, 0); + if (!systick.membase) + return -ENXIO; + +- systick_irqaction.name = np->name; +- systick.dev.name = np->name; +- clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60); +- systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev); +- systick.dev.max_delta_ticks = 0x7fff; +- systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev); +- systick.dev.min_delta_ticks = 0x3; ++ match = of_match_node(systick_match, np); ++ if (match) { ++ systick_freq_scaling = match->data; ++ /* ++ * cevt-r4k uses 300, make sure systick ++ * gets used if available ++ */ ++ rating = 310; ++ } ++ ++ /* enable counter than register clock source */ ++ iowrite32(CFG_CNT_EN, systick.membase + SYSTICK_CONFIG); ++ clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name, ++ SYSTICK_FREQ, rating, 16, clocksource_mmio_readl_up); ++ ++ /* register clock event */ + systick.dev.irq = irq_of_parse_and_map(np, 0); + if (!systick.dev.irq) { + pr_err("%pOFn: request_irq failed", np); + return -EINVAL; + } + +- ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name, +- SYSTICK_FREQ, 301, 16, +- clocksource_mmio_readl_up); +- if (ret) +- return ret; +- +- clockevents_register_device(&systick.dev); ++ systick_irqaction.name = np->name; ++ systick.dev.name = np->name; ++ systick.dev.rating = rating; ++ systick.dev.cpumask = cpumask_of(0); ++ clockevents_config_and_register(&systick.dev, SYSTICK_FREQ, 0x3, 0x7fff); + + pr_info("%pOFn: running - mult: %d, shift: %d\n", + np, systick.dev.mult, systick.dev.shift); diff --git a/target/linux/ramips/patches-5.10/0007-MIPS-ralink-copy-the-commandline-from-the-devicetree.patch b/target/linux/ramips/patches-5.10/0007-MIPS-ralink-copy-the-commandline-from-the-devicetree.patch new file mode 100644 index 0000000000..54af571d85 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0007-MIPS-ralink-copy-the-commandline-from-the-devicetree.patch @@ -0,0 +1,21 @@ +From 67b7bff0fd364c194e653f69baa623ba2141bd4c Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Mon, 4 Aug 2014 18:46:02 +0200 +Subject: [PATCH 07/53] MIPS: ralink: copy the commandline from the devicetree + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/ralink/of.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -80,6 +80,8 @@ void __init plat_mem_setup(void) + + __dt_setup_arch(dtb); + ++ strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE); ++ + of_scan_flat_dt(early_init_dt_find_memory, NULL); + if (memory_dtb) + of_scan_flat_dt(early_init_dt_scan_memory, NULL); diff --git a/target/linux/ramips/patches-5.10/0010-MIPS-add-bootargs-override-property.patch b/target/linux/ramips/patches-5.10/0010-MIPS-add-bootargs-override-property.patch new file mode 100644 index 0000000000..c19a0fb480 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0010-MIPS-add-bootargs-override-property.patch @@ -0,0 +1,63 @@ +From f15d27f9c90ede4b16eb37f9ae573ef81c2b6996 Mon Sep 17 00:00:00 2001 +From: David Bauer <mail@david-bauer.net> +Date: Thu, 31 Dec 2020 18:49:12 +0100 +Subject: [PATCH] MIPS: add bootargs-override property + +Add support for the bootargs-override property to the chosen node +similar to the one used on ipq806x or mpc85xx. + +This is necessary, as the U-Boot used on some boards, notably the +Ubiquiti UniFi 6 Lite, overwrite the bootargs property of the chosen +node leading to a kernel panic when loading OpenWrt. + +Signed-off-by: David Bauer <mail@david-bauer.net> +--- + arch/mips/kernel/setup.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -571,8 +571,28 @@ static int __init bootcmdline_scan_chose + return 1; + } + ++static int __init bootcmdline_scan_chosen_override(unsigned long node, const char *uname, ++ int depth, void *data) ++{ ++ bool *dt_bootargs = data; ++ const char *p; ++ int l; ++ ++ if (depth != 1 || !data || strcmp(uname, "chosen") != 0) ++ return 0; ++ ++ p = of_get_flat_dt_prop(node, "bootargs-override", &l); ++ if (p != NULL && l > 0) { ++ strlcpy(boot_command_line, p, COMMAND_LINE_SIZE); ++ *dt_bootargs = true; ++ } ++ ++ return 1; ++} ++ + static void __init bootcmdline_init(char **cmdline_p) + { ++ bool dt_bootargs_override = false; + bool dt_bootargs = false; + + /* +@@ -586,6 +606,14 @@ static void __init bootcmdline_init(char + } + + /* ++ * If bootargs-override in the chosen node is set, use this as the ++ * command line ++ */ ++ of_scan_flat_dt(bootcmdline_scan_chosen_override, &dt_bootargs_override); ++ if (dt_bootargs_override) ++ return; ++ ++ /* + * If the user specified a built-in command line & + * MIPS_CMDLINE_BUILTIN_EXTEND, then the built-in command line is + * prepended to arguments from the bootloader or DT so we'll copy them diff --git a/target/linux/ramips/patches-5.10/0013-owrt-hack-fix-mt7688-cache-issue.patch b/target/linux/ramips/patches-5.10/0013-owrt-hack-fix-mt7688-cache-issue.patch new file mode 100644 index 0000000000..bedea14506 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0013-owrt-hack-fix-mt7688-cache-issue.patch @@ -0,0 +1,29 @@ +From 5ede027f6c4a57ed25da872420508b7f1168b36b Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Mon, 7 Dec 2015 17:15:32 +0100 +Subject: [PATCH 13/53] owrt: hack: fix mt7688 cache issue + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/kernel/setup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -723,8 +723,6 @@ static void __init arch_mem_init(char ** + memblock_reserve(crashk_res.start, + crashk_res.end - crashk_res.start + 1); + #endif +- device_tree_init(); +- + /* + * In order to reduce the possibility of kernel panic when failed to + * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate +@@ -841,6 +839,7 @@ void __init setup_arch(char **cmdline_p) + + cpu_cache_init(); + paging_init(); ++ device_tree_init(); + } + + unsigned long kernelsp[NR_CPUS]; diff --git a/target/linux/ramips/patches-5.10/0015-arch-mips-do-not-select-illegal-access-driver-by-def.patch b/target/linux/ramips/patches-5.10/0015-arch-mips-do-not-select-illegal-access-driver-by-def.patch new file mode 100644 index 0000000000..1dc54ccf23 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0015-arch-mips-do-not-select-illegal-access-driver-by-def.patch @@ -0,0 +1,25 @@ +From 9e6ce539092a1dd605a20bf73c655a9de58d8641 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Mon, 7 Dec 2015 17:18:05 +0100 +Subject: [PATCH 15/53] arch: mips: do not select illegal access driver by + default + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/ralink/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -14,9 +14,9 @@ config CLKEVT_RT3352 + select CEVT_SYSTICK_QUIRK + + config RALINK_ILL_ACC +- bool ++ bool "illegal access irq" + depends on SOC_RT305X +- default y ++ default n + + config IRQ_INTC + bool diff --git a/target/linux/ramips/patches-5.10/0024-GPIO-add-named-gpio-exports.patch b/target/linux/ramips/patches-5.10/0024-GPIO-add-named-gpio-exports.patch new file mode 100644 index 0000000000..d4ea379159 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0024-GPIO-add-named-gpio-exports.patch @@ -0,0 +1,165 @@ +From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Tue, 12 Aug 2014 20:49:27 +0200 +Subject: [PATCH 24/53] GPIO: add named gpio exports + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++ + drivers/gpio/gpiolib-sysfs.c | 10 +++++- + include/asm-generic/gpio.h | 6 ++++ + include/linux/gpio/consumer.h | 8 +++++ + 4 files changed, 91 insertions(+), 1 deletion(-) + +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -19,6 +19,8 @@ + #include <linux/pinctrl/pinctrl.h> + #include <linux/slab.h> + #include <linux/gpio/machine.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> + + #include "gpiolib.h" + #include "gpiolib-of.h" +@@ -915,3 +917,68 @@ void of_gpiochip_remove(struct gpio_chip + { + of_node_put(chip->of_node); + } ++ ++static struct of_device_id gpio_export_ids[] = { ++ { .compatible = "gpio-export" }, ++ { /* sentinel */ } ++}; ++ ++static int of_gpio_export_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *cnp; ++ u32 val; ++ int nb = 0; ++ ++ for_each_child_of_node(np, cnp) { ++ const char *name = NULL; ++ int gpio; ++ bool dmc; ++ int max_gpio = 1; ++ int i; ++ ++ of_property_read_string(cnp, "gpio-export,name", &name); ++ ++ if (!name) ++ max_gpio = of_gpio_count(cnp); ++ ++ for (i = 0; i < max_gpio; i++) { ++ unsigned flags = 0; ++ enum of_gpio_flags of_flags; ++ ++ gpio = of_get_gpio_flags(cnp, i, &of_flags); ++ if (!gpio_is_valid(gpio)) ++ return gpio; ++ ++ if (of_flags == OF_GPIO_ACTIVE_LOW) ++ flags |= GPIOF_ACTIVE_LOW; ++ ++ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ else ++ flags |= GPIOF_IN; ++ ++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) ++ continue; ++ ++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); ++ gpio_export_with_name(gpio, dmc, name); ++ nb++; ++ } ++ } ++ ++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_export_driver = { ++ .driver = { ++ .name = "gpio-export", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(gpio_export_ids), ++ }, ++ .probe = of_gpio_export_probe, ++}; ++ ++module_platform_driver(gpio_export_driver); +--- a/drivers/gpio/gpiolib-sysfs.c ++++ b/drivers/gpio/gpiolib-sysfs.c +@@ -563,7 +563,7 @@ static struct class gpio_class = { + * + * Returns zero on success, else an error. + */ +-int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) + { + struct gpio_chip *chip; + struct gpio_device *gdev; +@@ -625,6 +625,8 @@ int gpiod_export(struct gpio_desc *desc, + offset = gpio_chip_hwgpio(desc); + if (chip->names && chip->names[offset]) + ioname = chip->names[offset]; ++ if (name) ++ ioname = name; + + dev = device_create_with_groups(&gpio_class, &gdev->dev, + MKDEV(0, 0), data, gpio_groups, +@@ -646,6 +648,12 @@ err_unlock: + gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return status; + } ++EXPORT_SYMBOL_GPL(__gpiod_export); ++ ++int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++{ ++ return __gpiod_export(desc, direction_may_change, NULL); ++} + EXPORT_SYMBOL_GPL(gpiod_export); + + static int match_export(struct device *dev, const void *desc) +--- a/include/asm-generic/gpio.h ++++ b/include/asm-generic/gpio.h +@@ -127,6 +127,12 @@ static inline int gpio_export(unsigned g + return gpiod_export(gpio_to_desc(gpio), direction_may_change); + } + ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); ++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) ++{ ++ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); ++} ++ + static inline int gpio_export_link(struct device *dev, const char *name, + unsigned gpio) + { +--- a/include/linux/gpio/consumer.h ++++ b/include/linux/gpio/consumer.h +@@ -668,6 +668,7 @@ static inline void devm_acpi_dev_remove_ + + #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) + ++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); + int gpiod_export(struct gpio_desc *desc, bool direction_may_change); + int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); +@@ -675,6 +676,13 @@ void gpiod_unexport(struct gpio_desc *de + + #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ + ++static inline int _gpiod_export(struct gpio_desc *desc, ++ bool direction_may_change, ++ const char *name) ++{ ++ return -ENOSYS; ++} ++ + static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) + { diff --git a/target/linux/ramips/patches-5.10/0026-DT-Add-documentation-for-gpio-ralink.patch b/target/linux/ramips/patches-5.10/0026-DT-Add-documentation-for-gpio-ralink.patch new file mode 100644 index 0000000000..7d5f98f647 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0026-DT-Add-documentation-for-gpio-ralink.patch @@ -0,0 +1,59 @@ +From d410e5478c622c01fcf31427533df5f433df9146 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Sun, 28 Jul 2013 19:45:30 +0200 +Subject: [PATCH 26/53] DT: Add documentation for gpio-ralink + +Describe gpio-ralink binding. + +Signed-off-by: John Crispin <blogic@openwrt.org> +Cc: linux-mips@linux-mips.org +Cc: devicetree@vger.kernel.org +Cc: linux-gpio@vger.kernel.org +--- + .../devicetree/bindings/gpio/gpio-ralink.txt | 40 ++++++++++++++++++++ + 1 file changed, 40 insertions(+) + create mode 100644 Documentation/devicetree/bindings/gpio/gpio-ralink.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/gpio/gpio-ralink.txt +@@ -0,0 +1,40 @@ ++Ralink SoC GPIO controller bindings ++ ++Required properties: ++- compatible: ++ - "ralink,rt2880-gpio" for Ralink controllers ++- #gpio-cells : Should be two. ++ - first cell is the pin number ++ - second cell is used to specify optional parameters (unused) ++- gpio-controller : Marks the device node as a GPIO controller ++- reg : Physical base address and length of the controller's registers ++- interrupt-parent: phandle to the INTC device node ++- interrupts : Specify the INTC interrupt number ++- ralink,num-gpios : Specify the number of GPIOs ++- ralink,register-map : The register layout depends on the GPIO bank and actual ++ SoC type. Register offsets need to be in this order. ++ [ INT, EDGE, RENA, FENA, DATA, DIR, POL, SET, RESET, TOGGLE ] ++ ++Optional properties: ++- ralink,gpio-base : Specify the GPIO chips base number ++ ++Example: ++ ++ gpio0: gpio@600 { ++ compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio"; ++ ++ #gpio-cells = <2>; ++ gpio-controller; ++ ++ reg = <0x600 0x34>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ ralink,gpio-base = <0>; ++ ralink,num-gpios = <24>; ++ ralink,register-map = [ 00 04 08 0c ++ 20 24 28 2c ++ 30 34 ]; ++ ++ }; diff --git a/target/linux/ramips/patches-5.10/0027-GPIO-MIPS-ralink-add-gpio-driver-for-ralink-SoC.patch b/target/linux/ramips/patches-5.10/0027-GPIO-MIPS-ralink-add-gpio-driver-for-ralink-SoC.patch new file mode 100644 index 0000000000..eae507bcd7 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0027-GPIO-MIPS-ralink-add-gpio-driver-for-ralink-SoC.patch @@ -0,0 +1,416 @@ +From 69fdd2c4f937796b934e89c33acde9d082e27bfd Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Mon, 4 Aug 2014 20:36:29 +0200 +Subject: [PATCH 27/53] GPIO: MIPS: ralink: add gpio driver for ralink SoC + +Add gpio driver for Ralink SoC. This driver makes the gpio core on +RT2880, RT305x, rt3352, rt3662, rt3883, rt5350 and mt7620 work. + +Signed-off-by: John Crispin <blogic@openwrt.org> +Cc: linux-mips@linux-mips.org +Cc: linux-gpio@vger.kernel.org +--- + arch/mips/include/asm/mach-ralink/gpio.h | 24 ++ + drivers/gpio/Kconfig | 6 + + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-ralink.c | 355 ++++++++++++++++++++++++++++++ + 4 files changed, 386 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/gpio.h + create mode 100644 drivers/gpio/gpio-ralink.c + +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/gpio.h +@@ -0,0 +1,24 @@ ++/* ++ * Ralink SoC GPIO API support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __ASM_MACH_RALINK_GPIO_H ++#define __ASM_MACH_RALINK_GPIO_H ++ ++#define ARCH_NR_GPIOS 128 ++#include <asm-generic/gpio.h> ++ ++#define gpio_get_value __gpio_get_value ++#define gpio_set_value __gpio_set_value ++#define gpio_cansleep __gpio_cansleep ++#define gpio_to_irq __gpio_to_irq ++ ++#endif /* __ASM_MACH_RALINK_GPIO_H */ +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -471,6 +471,12 @@ config GPIO_SNPS_CREG + where only several fields in register belong to GPIO lines and + each GPIO line owns a field with different length and on/off value. + ++config GPIO_RALINK ++ bool "Ralink GPIO Support" ++ depends on RALINK ++ help ++ Say yes here to support the Ralink SoC GPIO device ++ + config GPIO_SPEAR_SPICS + bool "ST SPEAr13xx SPI Chip Select as GPIO support" + depends on PLAT_SPEAR +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -112,6 +112,7 @@ obj-$(CONFIG_GPIO_PISOSR) += gpio-pisos + obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o + obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o + obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o ++obj-$(CONFIG_GPIO_RALINK) += gpio-ralink.o + obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o + obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o + obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o +--- /dev/null ++++ b/drivers/gpio/gpio-ralink.c +@@ -0,0 +1,341 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org> ++ * Copyright (C) 2013 John Crispin <blogic@openwrt.org> ++ */ ++ ++#include <linux/module.h> ++#include <linux/io.h> ++#include <linux/gpio.h> ++#include <linux/spinlock.h> ++#include <linux/platform_device.h> ++#include <linux/of_irq.h> ++#include <linux/irqdomain.h> ++#include <linux/interrupt.h> ++ ++enum ralink_gpio_reg { ++ GPIO_REG_INT = 0, ++ GPIO_REG_EDGE, ++ GPIO_REG_RENA, ++ GPIO_REG_FENA, ++ GPIO_REG_DATA, ++ GPIO_REG_DIR, ++ GPIO_REG_POL, ++ GPIO_REG_SET, ++ GPIO_REG_RESET, ++ GPIO_REG_TOGGLE, ++ GPIO_REG_MAX ++}; ++ ++struct ralink_gpio_chip { ++ struct gpio_chip chip; ++ u8 regs[GPIO_REG_MAX]; ++ ++ spinlock_t lock; ++ void __iomem *membase; ++ struct irq_domain *domain; ++ int irq; ++ ++ u32 rising; ++ u32 falling; ++}; ++ ++#define MAP_MAX 4 ++static struct irq_domain *irq_map[MAP_MAX]; ++static int irq_map_count; ++static atomic_t irq_refcount = ATOMIC_INIT(0); ++ ++static inline struct ralink_gpio_chip *to_ralink_gpio(struct gpio_chip *chip) ++{ ++ struct ralink_gpio_chip *rg; ++ ++ rg = container_of(chip, struct ralink_gpio_chip, chip); ++ ++ return rg; ++} ++ ++static inline void rt_gpio_w32(struct ralink_gpio_chip *rg, u8 reg, u32 val) ++{ ++ iowrite32(val, rg->membase + rg->regs[reg]); ++} ++ ++static inline u32 rt_gpio_r32(struct ralink_gpio_chip *rg, u8 reg) ++{ ++ return ioread32(rg->membase + rg->regs[reg]); ++} ++ ++static void ralink_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ ++ rt_gpio_w32(rg, (value) ? GPIO_REG_SET : GPIO_REG_RESET, BIT(offset)); ++} ++ ++static int ralink_gpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ ++ return !!(rt_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset)); ++} ++ ++static int ralink_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ unsigned long flags; ++ u32 t; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ t = rt_gpio_r32(rg, GPIO_REG_DIR); ++ t &= ~BIT(offset); ++ rt_gpio_w32(rg, GPIO_REG_DIR, t); ++ spin_unlock_irqrestore(&rg->lock, flags); ++ ++ return 0; ++} ++ ++static int ralink_gpio_direction_output(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ unsigned long flags; ++ u32 t; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ ralink_gpio_set(chip, offset, value); ++ t = rt_gpio_r32(rg, GPIO_REG_DIR); ++ t |= BIT(offset); ++ rt_gpio_w32(rg, GPIO_REG_DIR, t); ++ spin_unlock_irqrestore(&rg->lock, flags); ++ ++ return 0; ++} ++ ++static int ralink_gpio_to_irq(struct gpio_chip *chip, unsigned pin) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ ++ if (rg->irq < 1) ++ return -1; ++ ++ return irq_create_mapping(rg->domain, pin); ++} ++ ++static void ralink_gpio_irq_handler(struct irq_desc *desc) ++{ ++ int i; ++ ++ for (i = 0; i < irq_map_count; i++) { ++ struct irq_domain *domain = irq_map[i]; ++ struct ralink_gpio_chip *rg; ++ unsigned long pending; ++ int bit; ++ ++ rg = (struct ralink_gpio_chip *) domain->host_data; ++ pending = rt_gpio_r32(rg, GPIO_REG_INT); ++ ++ for_each_set_bit(bit, &pending, rg->chip.ngpio) { ++ u32 map = irq_find_mapping(domain, bit); ++ generic_handle_irq(map); ++ rt_gpio_w32(rg, GPIO_REG_INT, BIT(bit)); ++ } ++ } ++} ++ ++static void ralink_gpio_irq_unmask(struct irq_data *d) ++{ ++ struct ralink_gpio_chip *rg; ++ unsigned long flags; ++ u32 rise, fall; ++ ++ rg = (struct ralink_gpio_chip *) d->domain->host_data; ++ rise = rt_gpio_r32(rg, GPIO_REG_RENA); ++ fall = rt_gpio_r32(rg, GPIO_REG_FENA); ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ rt_gpio_w32(rg, GPIO_REG_RENA, rise | (BIT(d->hwirq) & rg->rising)); ++ rt_gpio_w32(rg, GPIO_REG_FENA, fall | (BIT(d->hwirq) & rg->falling)); ++ spin_unlock_irqrestore(&rg->lock, flags); ++} ++ ++static void ralink_gpio_irq_mask(struct irq_data *d) ++{ ++ struct ralink_gpio_chip *rg; ++ unsigned long flags; ++ u32 rise, fall; ++ ++ rg = (struct ralink_gpio_chip *) d->domain->host_data; ++ rise = rt_gpio_r32(rg, GPIO_REG_RENA); ++ fall = rt_gpio_r32(rg, GPIO_REG_FENA); ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ rt_gpio_w32(rg, GPIO_REG_FENA, fall & ~BIT(d->hwirq)); ++ rt_gpio_w32(rg, GPIO_REG_RENA, rise & ~BIT(d->hwirq)); ++ spin_unlock_irqrestore(&rg->lock, flags); ++} ++ ++static int ralink_gpio_irq_type(struct irq_data *d, unsigned int type) ++{ ++ struct ralink_gpio_chip *rg; ++ u32 mask = BIT(d->hwirq); ++ ++ rg = (struct ralink_gpio_chip *) d->domain->host_data; ++ ++ if (type == IRQ_TYPE_PROBE) { ++ if ((rg->rising | rg->falling) & mask) ++ return 0; ++ ++ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; ++ } ++ ++ if (type & IRQ_TYPE_EDGE_RISING) ++ rg->rising |= mask; ++ else ++ rg->rising &= ~mask; ++ ++ if (type & IRQ_TYPE_EDGE_FALLING) ++ rg->falling |= mask; ++ else ++ rg->falling &= ~mask; ++ ++ return 0; ++} ++ ++static struct irq_chip ralink_gpio_irq_chip = { ++ .name = "GPIO", ++ .irq_unmask = ralink_gpio_irq_unmask, ++ .irq_mask = ralink_gpio_irq_mask, ++ .irq_mask_ack = ralink_gpio_irq_mask, ++ .irq_set_type = ralink_gpio_irq_type, ++}; ++ ++static int gpio_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) ++{ ++ irq_set_chip_and_handler(irq, &ralink_gpio_irq_chip, handle_level_irq); ++ irq_set_handler_data(irq, d); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops irq_domain_ops = { ++ .xlate = irq_domain_xlate_onecell, ++ .map = gpio_map, ++}; ++ ++static void ralink_gpio_irq_init(struct device_node *np, ++ struct ralink_gpio_chip *rg) ++{ ++ if (irq_map_count >= MAP_MAX) ++ return; ++ ++ rg->irq = irq_of_parse_and_map(np, 0); ++ if (!rg->irq) ++ return; ++ ++ rg->domain = irq_domain_add_linear(np, rg->chip.ngpio, ++ &irq_domain_ops, rg); ++ if (!rg->domain) { ++ dev_err(rg->chip.parent, "irq_domain_add_linear failed\n"); ++ return; ++ } ++ ++ irq_map[irq_map_count++] = rg->domain; ++ ++ rt_gpio_w32(rg, GPIO_REG_RENA, 0x0); ++ rt_gpio_w32(rg, GPIO_REG_FENA, 0x0); ++ ++ if (!atomic_read(&irq_refcount)) ++ irq_set_chained_handler(rg->irq, ralink_gpio_irq_handler); ++ atomic_inc(&irq_refcount); ++ ++ dev_info(rg->chip.parent, "registering %d irq handlers\n", rg->chip.ngpio); ++} ++ ++static int ralink_gpio_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ struct ralink_gpio_chip *rg; ++ const __be32 *ngpio, *gpiobase; ++ ++ if (!res) { ++ dev_err(&pdev->dev, "failed to find resource\n"); ++ return -ENOMEM; ++ } ++ ++ rg = devm_kzalloc(&pdev->dev, ++ sizeof(struct ralink_gpio_chip), GFP_KERNEL); ++ if (!rg) ++ return -ENOMEM; ++ ++ rg->membase = devm_ioremap_resource(&pdev->dev, res); ++ if (!rg->membase) { ++ dev_err(&pdev->dev, "cannot remap I/O memory region\n"); ++ return -ENOMEM; ++ } ++ ++ if (of_property_read_u8_array(np, "ralink,register-map", ++ rg->regs, GPIO_REG_MAX)) { ++ dev_err(&pdev->dev, "failed to read register definition\n"); ++ return -EINVAL; ++ } ++ ++ ngpio = of_get_property(np, "ralink,num-gpios", NULL); ++ if (!ngpio) { ++ dev_err(&pdev->dev, "failed to read number of pins\n"); ++ return -EINVAL; ++ } ++ ++ gpiobase = of_get_property(np, "ralink,gpio-base", NULL); ++ if (gpiobase) ++ rg->chip.base = be32_to_cpu(*gpiobase); ++ else ++ rg->chip.base = -1; ++ ++ spin_lock_init(&rg->lock); ++ ++ rg->chip.parent = &pdev->dev; ++ rg->chip.label = dev_name(&pdev->dev); ++ rg->chip.of_node = np; ++ rg->chip.ngpio = be32_to_cpu(*ngpio); ++ rg->chip.direction_input = ralink_gpio_direction_input; ++ rg->chip.direction_output = ralink_gpio_direction_output; ++ rg->chip.get = ralink_gpio_get; ++ rg->chip.set = ralink_gpio_set; ++ rg->chip.request = gpiochip_generic_request; ++ rg->chip.to_irq = ralink_gpio_to_irq; ++ rg->chip.free = gpiochip_generic_free; ++ ++ /* set polarity to low for all lines */ ++ rt_gpio_w32(rg, GPIO_REG_POL, 0); ++ ++ dev_info(&pdev->dev, "registering %d gpios\n", rg->chip.ngpio); ++ ++ ralink_gpio_irq_init(np, rg); ++ ++ return gpiochip_add(&rg->chip); ++} ++ ++static const struct of_device_id ralink_gpio_match[] = { ++ { .compatible = "ralink,rt2880-gpio" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_gpio_match); ++ ++static struct platform_driver ralink_gpio_driver = { ++ .probe = ralink_gpio_probe, ++ .driver = { ++ .name = "rt2880_gpio", ++ .owner = THIS_MODULE, ++ .of_match_table = ralink_gpio_match, ++ }, ++}; ++ ++static int __init ralink_gpio_init(void) ++{ ++ return platform_driver_register(&ralink_gpio_driver); ++} ++ ++subsys_initcall(ralink_gpio_init); diff --git a/target/linux/ramips/patches-5.10/0029-gpio-ralink-Add-support-for-GPIO-as-interrupt-contro.patch b/target/linux/ramips/patches-5.10/0029-gpio-ralink-Add-support-for-GPIO-as-interrupt-contro.patch new file mode 100644 index 0000000000..8520ce32ff --- /dev/null +++ b/target/linux/ramips/patches-5.10/0029-gpio-ralink-Add-support-for-GPIO-as-interrupt-contro.patch @@ -0,0 +1,44 @@ +From 57fa7f2f4ef6f78ce1d30509c0d111aa3791b524 Mon Sep 17 00:00:00 2001 +From: Daniel Santos <daniel.santos@pobox.com> +Date: Sun, 4 Nov 2018 20:24:32 -0600 +Subject: gpio-ralink: Add support for GPIO as interrupt-controller + +Signed-off-by: Daniel Santos <daniel.santos@pobox.com> +--- + Documentation/devicetree/bindings/gpio/gpio-ralink.txt | 6 ++++++ + drivers/gpio/gpio-ralink.c | 2 +- + 2 files changed, 7 insertions(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/gpio/gpio-ralink.txt ++++ b/Documentation/devicetree/bindings/gpio/gpio-ralink.txt +@@ -17,6 +17,9 @@ Required properties: + + Optional properties: + - ralink,gpio-base : Specify the GPIO chips base number ++- interrupt-controller : marks this as an interrupt controller ++- #interrupt-cells : a standard two-cell interrupt flag, see ++ interrupt-controller/interrupts.txt + + Example: + +@@ -28,6 +31,9 @@ Example: + + reg = <0x600 0x34>; + ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ + interrupt-parent = <&intc>; + interrupts = <6>; + +--- a/drivers/gpio/gpio-ralink.c ++++ b/drivers/gpio/gpio-ralink.c +@@ -220,7 +220,7 @@ static int gpio_map(struct irq_domain *d + } + + static const struct irq_domain_ops irq_domain_ops = { +- .xlate = irq_domain_xlate_onecell, ++ .xlate = irq_domain_xlate_twocell, + .map = gpio_map, + }; + diff --git a/target/linux/ramips/patches-5.10/0031-uvc-add-iPassion-iP2970-support.patch b/target/linux/ramips/patches-5.10/0031-uvc-add-iPassion-iP2970-support.patch new file mode 100644 index 0000000000..084b655483 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0031-uvc-add-iPassion-iP2970-support.patch @@ -0,0 +1,246 @@ +From 975e76214cd2516eb6cfff4c3eec581872645e88 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Thu, 19 Sep 2013 01:50:59 +0200 +Subject: [PATCH 31/53] uvc: add iPassion iP2970 support + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/media/usb/uvc/uvc_driver.c | 12 +++ + drivers/media/usb/uvc/uvc_status.c | 2 + + drivers/media/usb/uvc/uvc_video.c | 147 ++++++++++++++++++++++++++++++++++++ + drivers/media/usb/uvc/uvcvideo.h | 5 +- + 4 files changed, 165 insertions(+), 1 deletion(-) + +--- a/drivers/media/usb/uvc/uvc_driver.c ++++ b/drivers/media/usb/uvc/uvc_driver.c +@@ -2908,6 +2908,18 @@ static const struct usb_device_id uvc_id + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, ++ /* iPassion iP2970 */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x1B3B, ++ .idProduct = 0x2970, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 0, ++ .driver_info = UVC_QUIRK_PROBE_MINMAX ++ | UVC_QUIRK_STREAM_NO_FID ++ | UVC_QUIRK_MOTION ++ | UVC_QUIRK_SINGLE_ISO }, + /* Generic USB Video Class */ + { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) }, + { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) }, +--- a/drivers/media/usb/uvc/uvc_status.c ++++ b/drivers/media/usb/uvc/uvc_status.c +@@ -223,6 +223,7 @@ static void uvc_status_complete(struct u + if (uvc_event_control(urb, status, len)) + /* The URB will be resubmitted in work context. */ + return; ++ dev->motion = 1; + break; + } + +@@ -271,6 +272,7 @@ int uvc_status_init(struct uvc_device *d + } + + pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); ++ dev->motion = 0; + + /* For high-speed interrupt endpoints, the bInterval value is used as + * an exponent of two. Some developers forgot about it. +--- a/drivers/media/usb/uvc/uvc_video.c ++++ b/drivers/media/usb/uvc/uvc_video.c +@@ -16,6 +16,11 @@ + #include <linux/wait.h> + #include <linux/atomic.h> + #include <asm/unaligned.h> ++#include <linux/skbuff.h> ++#include <linux/kobject.h> ++#include <linux/netlink.h> ++#include <linux/kobject.h> ++#include <linux/workqueue.h> + + #include <media/v4l2-common.h> + +@@ -1156,9 +1161,149 @@ static void uvc_video_decode_data(struct + uvc_urb->async_operations++; + } + ++struct bh_priv { ++ unsigned long seen; ++}; ++ ++struct bh_event { ++ const char *name; ++ struct sk_buff *skb; ++ struct work_struct work; ++}; ++ ++#define BH_ERR(fmt, args...) printk(KERN_ERR "%s: " fmt, "webcam", ##args ) ++#define BH_DBG(fmt, args...) do {} while (0) ++#define BH_SKB_SIZE 2048 ++ ++extern u64 uevent_next_seqnum(void); ++static int seen = 0; ++ ++static int bh_event_add_var(struct bh_event *event, int argv, ++ const char *format, ...) ++{ ++ static char buf[128]; ++ char *s; ++ va_list args; ++ int len; ++ ++ if (argv) ++ return 0; ++ ++ va_start(args, format); ++ len = vsnprintf(buf, sizeof(buf), format, args); ++ va_end(args); ++ ++ if (len >= sizeof(buf)) { ++ BH_ERR("buffer size too small\n"); ++ WARN_ON(1); ++ return -ENOMEM; ++ } ++ ++ s = skb_put(event->skb, len + 1); ++ strcpy(s, buf); ++ ++ BH_DBG("added variable '%s'\n", s); ++ ++ return 0; ++} ++ ++static int motion_hotplug_fill_event(struct bh_event *event) ++{ ++ int s = jiffies; ++ int ret; ++ ++ if (!seen) ++ seen = jiffies; ++ ++ ret = bh_event_add_var(event, 0, "HOME=%s", "/"); ++ if (ret) ++ return ret; ++ ++ ret = bh_event_add_var(event, 0, "PATH=%s", ++ "/sbin:/bin:/usr/sbin:/usr/bin"); ++ if (ret) ++ return ret; ++ ++ ret = bh_event_add_var(event, 0, "SUBSYSTEM=usb"); ++ if (ret) ++ return ret; ++ ++ ret = bh_event_add_var(event, 0, "ACTION=motion"); ++ if (ret) ++ return ret; ++ ++ ret = bh_event_add_var(event, 0, "SEEN=%d", s - seen); ++ if (ret) ++ return ret; ++ seen = s; ++ ++ ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum()); ++ ++ return ret; ++} ++ ++static void motion_hotplug_work(struct work_struct *work) ++{ ++ struct bh_event *event = container_of(work, struct bh_event, work); ++ int ret = 0; ++ ++ event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL); ++ if (!event->skb) ++ goto out_free_event; ++ ++ ret = bh_event_add_var(event, 0, "%s@", "add"); ++ if (ret) ++ goto out_free_skb; ++ ++ ret = motion_hotplug_fill_event(event); ++ if (ret) ++ goto out_free_skb; ++ ++ NETLINK_CB(event->skb).dst_group = 1; ++ broadcast_uevent(event->skb, 0, 1, GFP_KERNEL); ++ ++out_free_skb: ++ if (ret) { ++ BH_ERR("work error %d\n", ret); ++ kfree_skb(event->skb); ++ } ++out_free_event: ++ kfree(event); ++} ++ ++static int motion_hotplug_create_event(void) ++{ ++ struct bh_event *event; ++ ++ event = kzalloc(sizeof(*event), GFP_KERNEL); ++ if (!event) ++ return -ENOMEM; ++ ++ event->name = "motion"; ++ ++ INIT_WORK(&event->work, (void *)(void *)motion_hotplug_work); ++ schedule_work(&event->work); ++ ++ return 0; ++} ++ ++#define MOTION_FLAG_OFFSET 4 + static void uvc_video_decode_end(struct uvc_streaming *stream, + struct uvc_buffer *buf, const u8 *data, int len) + { ++ if ((stream->dev->quirks & UVC_QUIRK_MOTION) && ++ (data[len - 2] == 0xff) && (data[len - 1] == 0xd9)) { ++ u8 *mem; ++ buf->state = UVC_BUF_STATE_READY; ++ mem = (u8 *) (buf->mem + MOTION_FLAG_OFFSET); ++ if ( stream->dev->motion ) { ++ stream->dev->motion = 0; ++ motion_hotplug_create_event(); ++ } else { ++ *mem &= 0x7f; ++ } ++ } ++ + /* Mark the buffer as done if the EOF marker is set. */ + if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) { + uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n"); +@@ -1715,6 +1860,8 @@ static int uvc_init_video_isoc(struct uv + if (npackets == 0) + return -ENOMEM; + ++ if (stream->dev->quirks & UVC_QUIRK_SINGLE_ISO) ++ npackets = 1; + size = npackets * psize; + + for_each_uvc_urb(uvc_urb, stream) { +--- a/drivers/media/usb/uvc/uvcvideo.h ++++ b/drivers/media/usb/uvc/uvcvideo.h +@@ -199,7 +199,9 @@ + #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400 + #define UVC_QUIRK_FORCE_Y8 0x00000800 + #define UVC_QUIRK_FORCE_BPP 0x00001000 +- ++#define UVC_QUIRK_MOTION 0x00001000 ++#define UVC_QUIRK_SINGLE_ISO 0x00002000 ++ + /* Format flags */ + #define UVC_FMT_FLAG_COMPRESSED 0x00000001 + #define UVC_FMT_FLAG_STREAM 0x00000002 +@@ -666,6 +668,7 @@ struct uvc_device { + u8 *status; + struct input_dev *input; + char input_phys[64]; ++ int motion; + + struct uvc_ctrl_work { + struct work_struct work; diff --git a/target/linux/ramips/patches-5.10/0037-mtd-cfi-cmdset-0002-force-word-write.patch b/target/linux/ramips/patches-5.10/0037-mtd-cfi-cmdset-0002-force-word-write.patch new file mode 100644 index 0000000000..7011bbe50b --- /dev/null +++ b/target/linux/ramips/patches-5.10/0037-mtd-cfi-cmdset-0002-force-word-write.patch @@ -0,0 +1,20 @@ +From ee9081b2726a5ca8cde5497afdc5425e21ff8f8b Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Mon, 15 Jul 2013 00:39:21 +0200 +Subject: [PATCH 37/53] mtd: cfi cmdset 0002 force word write + +--- + drivers/mtd/chips/cfi_cmdset_0002.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -40,7 +40,7 @@ + #include <linux/mtd/xip.h> + + #define AMD_BOOTLOC_BUG +-#define FORCE_WORD_WRITE 0 ++#define FORCE_WORD_WRITE 1 + + #define MAX_RETRIES 3 + diff --git a/target/linux/ramips/patches-5.10/0041-DT-Add-documentation-for-spi-rt2880.patch b/target/linux/ramips/patches-5.10/0041-DT-Add-documentation-for-spi-rt2880.patch new file mode 100644 index 0000000000..e2643e3f25 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0041-DT-Add-documentation-for-spi-rt2880.patch @@ -0,0 +1,44 @@ +From da6015e7f19d749f135f7ac55c4ec47b06faa868 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Fri, 9 Aug 2013 20:12:59 +0200 +Subject: [PATCH 41/53] DT: Add documentation for spi-rt2880 + +Describe the SPI master found on the MIPS based Ralink RT2880 SoC. + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + .../devicetree/bindings/spi/spi-rt2880.txt | 28 ++++++++++++++++++++ + 1 file changed, 28 insertions(+) + create mode 100644 Documentation/devicetree/bindings/spi/spi-rt2880.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/spi/spi-rt2880.txt +@@ -0,0 +1,28 @@ ++Ralink SoC RT2880 SPI master controller. ++ ++This SPI controller is found on most wireless SoCs made by ralink. ++ ++Required properties: ++- compatible : "ralink,rt2880-spi" ++- reg : The register base for the controller. ++- #address-cells : <1>, as required by generic SPI binding. ++- #size-cells : <0>, also as required by generic SPI binding. ++ ++Child nodes as per the generic SPI binding. ++ ++Example: ++ ++ spi@b00 { ++ compatible = "ralink,rt2880-spi"; ++ reg = <0xb00 0x100>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ m25p80@0 { ++ compatible = "m25p80"; ++ reg = <0>; ++ spi-max-frequency = <10000000>; ++ }; ++ }; ++ diff --git a/target/linux/ramips/patches-5.10/0042-SPI-ralink-add-Ralink-SoC-spi-driver.patch b/target/linux/ramips/patches-5.10/0042-SPI-ralink-add-Ralink-SoC-spi-driver.patch new file mode 100644 index 0000000000..652371617a --- /dev/null +++ b/target/linux/ramips/patches-5.10/0042-SPI-ralink-add-Ralink-SoC-spi-driver.patch @@ -0,0 +1,574 @@ +From 683af4ebb91a1600df1946ac4769d916b8a1be65 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Sun, 27 Jul 2014 11:15:12 +0100 +Subject: [PATCH 42/53] SPI: ralink: add Ralink SoC spi driver + +Add the driver needed to make SPI work on Ralink SoC. + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +Acked-by: John Crispin <blogic@openwrt.org> +--- + drivers/spi/Kconfig | 6 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-rt2880.c | 530 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 537 insertions(+) + create mode 100644 drivers/spi/spi-rt2880.c + +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -605,6 +605,12 @@ config SPI_QCOM_GENI + This driver can also be built as a module. If so, the module + will be called spi-geni-qcom. + ++config SPI_RT2880 ++ tristate "Ralink RT288x SPI Controller" ++ depends on RALINK ++ help ++ This selects a driver for the Ralink RT288x/RT305x SPI Controller. ++ + config SPI_S3C24XX + tristate "Samsung S3C24XX series SPI" + depends on ARCH_S3C24XX +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -87,6 +87,7 @@ obj-$(CONFIG_SPI_QUP) += spi-qup.o + obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o + obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o + obj-$(CONFIG_SPI_RSPI) += spi-rspi.o ++obj-$(CONFIG_SPI_RT2880) += spi-rt2880.o + obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o + spi-s3c24xx-hw-y := spi-s3c24xx.o + spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o +--- /dev/null ++++ b/drivers/spi/spi-rt2880.c +@@ -0,0 +1,530 @@ ++/* ++ * spi-rt2880.c -- Ralink RT288x/RT305x SPI controller driver ++ * ++ * Copyright (C) 2011 Sergiy <piratfm@gmail.com> ++ * Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * Some parts are based on spi-orion.c: ++ * Author: Shadi Ammouri <shadi@marvell.com> ++ * Copyright (C) 2007-2008 Marvell Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/clk.h> ++#include <linux/err.h> ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/reset.h> ++#include <linux/spi/spi.h> ++#include <linux/platform_device.h> ++#include <linux/gpio.h> ++ ++#define DRIVER_NAME "spi-rt2880" ++ ++#define RAMIPS_SPI_STAT 0x00 ++#define RAMIPS_SPI_CFG 0x10 ++#define RAMIPS_SPI_CTL 0x14 ++#define RAMIPS_SPI_DATA 0x20 ++#define RAMIPS_SPI_ADDR 0x24 ++#define RAMIPS_SPI_BS 0x28 ++#define RAMIPS_SPI_USER 0x2C ++#define RAMIPS_SPI_TXFIFO 0x30 ++#define RAMIPS_SPI_RXFIFO 0x34 ++#define RAMIPS_SPI_FIFO_STAT 0x38 ++#define RAMIPS_SPI_MODE 0x3C ++#define RAMIPS_SPI_DEV_OFFSET 0x40 ++#define RAMIPS_SPI_DMA 0x80 ++#define RAMIPS_SPI_DMASTAT 0x84 ++#define RAMIPS_SPI_ARBITER 0xF0 ++ ++/* SPISTAT register bit field */ ++#define SPISTAT_BUSY BIT(0) ++ ++/* SPICFG register bit field */ ++#define SPICFG_ADDRMODE BIT(12) ++#define SPICFG_RXENVDIS BIT(11) ++#define SPICFG_RXCAP BIT(10) ++#define SPICFG_SPIENMODE BIT(9) ++#define SPICFG_MSBFIRST BIT(8) ++#define SPICFG_SPICLKPOL BIT(6) ++#define SPICFG_RXCLKEDGE_FALLING BIT(5) ++#define SPICFG_TXCLKEDGE_FALLING BIT(4) ++#define SPICFG_HIZSPI BIT(3) ++#define SPICFG_SPICLK_PRESCALE_MASK 0x7 ++#define SPICFG_SPICLK_DIV2 0 ++#define SPICFG_SPICLK_DIV4 1 ++#define SPICFG_SPICLK_DIV8 2 ++#define SPICFG_SPICLK_DIV16 3 ++#define SPICFG_SPICLK_DIV32 4 ++#define SPICFG_SPICLK_DIV64 5 ++#define SPICFG_SPICLK_DIV128 6 ++#define SPICFG_SPICLK_DISABLE 7 ++ ++/* SPICTL register bit field */ ++#define SPICTL_START BIT(4) ++#define SPICTL_HIZSDO BIT(3) ++#define SPICTL_STARTWR BIT(2) ++#define SPICTL_STARTRD BIT(1) ++#define SPICTL_SPIENA BIT(0) ++ ++/* SPIUSER register bit field */ ++#define SPIUSER_USERMODE BIT(21) ++#define SPIUSER_INSTR_PHASE BIT(20) ++#define SPIUSER_ADDR_PHASE_MASK 0x7 ++#define SPIUSER_ADDR_PHASE_OFFSET 17 ++#define SPIUSER_MODE_PHASE BIT(16) ++#define SPIUSER_DUMMY_PHASE_MASK 0x3 ++#define SPIUSER_DUMMY_PHASE_OFFSET 14 ++#define SPIUSER_DATA_PHASE_MASK 0x3 ++#define SPIUSER_DATA_PHASE_OFFSET 12 ++#define SPIUSER_DATA_READ (BIT(0) << SPIUSER_DATA_PHASE_OFFSET) ++#define SPIUSER_DATA_WRITE (BIT(1) << SPIUSER_DATA_PHASE_OFFSET) ++#define SPIUSER_ADDR_TYPE_OFFSET 9 ++#define SPIUSER_MODE_TYPE_OFFSET 6 ++#define SPIUSER_DUMMY_TYPE_OFFSET 3 ++#define SPIUSER_DATA_TYPE_OFFSET 0 ++#define SPIUSER_TRANSFER_MASK 0x7 ++#define SPIUSER_TRANSFER_SINGLE BIT(0) ++#define SPIUSER_TRANSFER_DUAL BIT(1) ++#define SPIUSER_TRANSFER_QUAD BIT(2) ++ ++#define SPIUSER_TRANSFER_TYPE(type) ( \ ++ (type << SPIUSER_ADDR_TYPE_OFFSET) | \ ++ (type << SPIUSER_MODE_TYPE_OFFSET) | \ ++ (type << SPIUSER_DUMMY_TYPE_OFFSET) | \ ++ (type << SPIUSER_DATA_TYPE_OFFSET) \ ++) ++ ++/* SPIFIFOSTAT register bit field */ ++#define SPIFIFOSTAT_TXEMPTY BIT(19) ++#define SPIFIFOSTAT_RXEMPTY BIT(18) ++#define SPIFIFOSTAT_TXFULL BIT(17) ++#define SPIFIFOSTAT_RXFULL BIT(16) ++#define SPIFIFOSTAT_FIFO_MASK 0xff ++#define SPIFIFOSTAT_TX_OFFSET 8 ++#define SPIFIFOSTAT_RX_OFFSET 0 ++ ++#define SPI_FIFO_DEPTH 16 ++ ++/* SPIMODE register bit field */ ++#define SPIMODE_MODE_OFFSET 24 ++#define SPIMODE_DUMMY_OFFSET 0 ++ ++/* SPIARB register bit field */ ++#define SPICTL_ARB_EN BIT(31) ++#define SPICTL_CSCTL1 BIT(16) ++#define SPI1_POR BIT(1) ++#define SPI0_POR BIT(0) ++ ++#define RT2880_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | \ ++ SPI_CS_HIGH) ++ ++static atomic_t hw_reset_count = ATOMIC_INIT(0); ++ ++struct rt2880_spi { ++ struct spi_master *master; ++ void __iomem *base; ++ u32 speed; ++ u16 wait_loops; ++ u16 mode; ++ struct clk *clk; ++}; ++ ++static inline struct rt2880_spi *spidev_to_rt2880_spi(struct spi_device *spi) ++{ ++ return spi_master_get_devdata(spi->master); ++} ++ ++static inline u32 rt2880_spi_read(struct rt2880_spi *rs, u32 reg) ++{ ++ return ioread32(rs->base + reg); ++} ++ ++static inline void rt2880_spi_write(struct rt2880_spi *rs, u32 reg, ++ const u32 val) ++{ ++ iowrite32(val, rs->base + reg); ++} ++ ++static inline void rt2880_spi_setbits(struct rt2880_spi *rs, u32 reg, u32 mask) ++{ ++ void __iomem *addr = rs->base + reg; ++ ++ iowrite32((ioread32(addr) | mask), addr); ++} ++ ++static inline void rt2880_spi_clrbits(struct rt2880_spi *rs, u32 reg, u32 mask) ++{ ++ void __iomem *addr = rs->base + reg; ++ ++ iowrite32((ioread32(addr) & ~mask), addr); ++} ++ ++static u32 rt2880_spi_baudrate_get(struct spi_device *spi, unsigned int speed) ++{ ++ struct rt2880_spi *rs = spidev_to_rt2880_spi(spi); ++ u32 rate; ++ u32 prescale; ++ ++ /* ++ * the supported rates are: 2, 4, 8, ... 128 ++ * round up as we look for equal or less speed ++ */ ++ rate = DIV_ROUND_UP(clk_get_rate(rs->clk), speed); ++ rate = roundup_pow_of_two(rate); ++ ++ /* Convert the rate to SPI clock divisor value. */ ++ prescale = ilog2(rate / 2); ++ ++ /* some tolerance. double and add 100 */ ++ rs->wait_loops = (8 * HZ * loops_per_jiffy) / ++ (clk_get_rate(rs->clk) / rate); ++ rs->wait_loops = (rs->wait_loops << 1) + 100; ++ rs->speed = speed; ++ ++ dev_dbg(&spi->dev, "speed: %lu/%u, rate: %u, prescal: %u, loops: %hu\n", ++ clk_get_rate(rs->clk) / rate, speed, rate, prescale, ++ rs->wait_loops); ++ ++ return prescale; ++} ++ ++static u32 get_arbiter_offset(struct spi_master *master) ++{ ++ u32 offset; ++ ++ offset = RAMIPS_SPI_ARBITER; ++ if (master->bus_num == 1) ++ offset -= RAMIPS_SPI_DEV_OFFSET; ++ ++ return offset; ++} ++ ++static void rt2880_spi_set_cs(struct spi_device *spi, bool enable) ++{ ++ struct rt2880_spi *rs = spidev_to_rt2880_spi(spi); ++ ++ if (enable) ++ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA); ++ else ++ rt2880_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA); ++} ++ ++static int rt2880_spi_wait_ready(struct rt2880_spi *rs, int len) ++{ ++ int loop = rs->wait_loops * len; ++ ++ while ((rt2880_spi_read(rs, RAMIPS_SPI_STAT) & SPISTAT_BUSY) && --loop) ++ cpu_relax(); ++ ++ if (loop) ++ return 0; ++ ++ return -ETIMEDOUT; ++} ++ ++static void rt2880_dump_reg(struct spi_master *master) ++{ ++ struct rt2880_spi *rs = spi_master_get_devdata(master); ++ ++ dev_dbg(&master->dev, "stat: %08x, cfg: %08x, ctl: %08x, " \ ++ "data: %08x, arb: %08x\n", ++ rt2880_spi_read(rs, RAMIPS_SPI_STAT), ++ rt2880_spi_read(rs, RAMIPS_SPI_CFG), ++ rt2880_spi_read(rs, RAMIPS_SPI_CTL), ++ rt2880_spi_read(rs, RAMIPS_SPI_DATA), ++ rt2880_spi_read(rs, get_arbiter_offset(master))); ++} ++ ++static int rt2880_spi_transfer_one(struct spi_master *master, ++ struct spi_device *spi, struct spi_transfer *xfer) ++{ ++ struct rt2880_spi *rs = spi_master_get_devdata(master); ++ unsigned len; ++ const u8 *tx = xfer->tx_buf; ++ u8 *rx = xfer->rx_buf; ++ int err = 0; ++ ++ /* change clock speed */ ++ if (unlikely(rs->speed != xfer->speed_hz)) { ++ u32 reg; ++ reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG); ++ reg &= ~SPICFG_SPICLK_PRESCALE_MASK; ++ reg |= rt2880_spi_baudrate_get(spi, xfer->speed_hz); ++ rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg); ++ } ++ ++ if (tx) { ++ len = xfer->len; ++ while (len-- > 0) { ++ rt2880_spi_write(rs, RAMIPS_SPI_DATA, *tx++); ++ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR); ++ err = rt2880_spi_wait_ready(rs, 1); ++ if (err) { ++ dev_err(&spi->dev, "TX failed, err=%d\n", err); ++ goto out; ++ } ++ } ++ } ++ ++ if (rx) { ++ len = xfer->len; ++ while (len-- > 0) { ++ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD); ++ err = rt2880_spi_wait_ready(rs, 1); ++ if (err) { ++ dev_err(&spi->dev, "RX failed, err=%d\n", err); ++ goto out; ++ } ++ *rx++ = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA); ++ } ++ } ++ ++out: ++ return err; ++} ++ ++/* copy from spi.c */ ++static void spi_set_cs(struct spi_device *spi, bool enable) ++{ ++ if (spi->mode & SPI_CS_HIGH) ++ enable = !enable; ++ ++ if (spi->cs_gpio >= 0) ++ gpio_set_value(spi->cs_gpio, !enable); ++ else if (spi->master->set_cs) ++ spi->master->set_cs(spi, !enable); ++} ++ ++static int rt2880_spi_setup(struct spi_device *spi) ++{ ++ struct spi_master *master = spi->master; ++ struct rt2880_spi *rs = spi_master_get_devdata(master); ++ u32 reg, old_reg, arbit_off; ++ ++ if ((spi->max_speed_hz > master->max_speed_hz) || ++ (spi->max_speed_hz < master->min_speed_hz)) { ++ dev_err(&spi->dev, "invalide requested speed %d Hz\n", ++ spi->max_speed_hz); ++ return -EINVAL; ++ } ++ ++ if (!(master->bits_per_word_mask & ++ BIT(spi->bits_per_word - 1))) { ++ dev_err(&spi->dev, "invalide bits_per_word %d\n", ++ spi->bits_per_word); ++ return -EINVAL; ++ } ++ ++ /* the hardware seems can't work on mode0 force it to mode3 */ ++ if ((spi->mode & (SPI_CPOL | SPI_CPHA)) == SPI_MODE_0) { ++ dev_warn(&spi->dev, "force spi mode3\n"); ++ spi->mode |= SPI_MODE_3; ++ } ++ ++ /* chip polarity */ ++ arbit_off = get_arbiter_offset(master); ++ reg = old_reg = rt2880_spi_read(rs, arbit_off); ++ if (spi->mode & SPI_CS_HIGH) { ++ switch (master->bus_num) { ++ case 1: ++ reg |= SPI1_POR; ++ break; ++ default: ++ reg |= SPI0_POR; ++ break; ++ } ++ } else { ++ switch (master->bus_num) { ++ case 1: ++ reg &= ~SPI1_POR; ++ break; ++ default: ++ reg &= ~SPI0_POR; ++ break; ++ } ++ } ++ ++ /* enable spi1 */ ++ if (master->bus_num == 1) ++ reg |= SPICTL_ARB_EN; ++ ++ if (reg != old_reg) ++ rt2880_spi_write(rs, arbit_off, reg); ++ ++ /* deselected the spi device */ ++ spi_set_cs(spi, false); ++ ++ rt2880_dump_reg(master); ++ ++ return 0; ++} ++ ++static int rt2880_spi_prepare_message(struct spi_master *master, ++ struct spi_message *msg) ++{ ++ struct rt2880_spi *rs = spi_master_get_devdata(master); ++ struct spi_device *spi = msg->spi; ++ u32 reg; ++ ++ if ((rs->mode == spi->mode) && (rs->speed == spi->max_speed_hz)) ++ return 0; ++ ++#if 0 ++ /* set spido to tri-state */ ++ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO); ++#endif ++ ++ reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG); ++ ++ reg &= ~(SPICFG_MSBFIRST | SPICFG_SPICLKPOL | ++ SPICFG_RXCLKEDGE_FALLING | ++ SPICFG_TXCLKEDGE_FALLING | ++ SPICFG_SPICLK_PRESCALE_MASK); ++ ++ /* MSB */ ++ if (!(spi->mode & SPI_LSB_FIRST)) ++ reg |= SPICFG_MSBFIRST; ++ ++ /* spi mode */ ++ switch (spi->mode & (SPI_CPOL | SPI_CPHA)) { ++ case SPI_MODE_0: ++ reg |= SPICFG_TXCLKEDGE_FALLING; ++ break; ++ case SPI_MODE_1: ++ reg |= SPICFG_RXCLKEDGE_FALLING; ++ break; ++ case SPI_MODE_2: ++ reg |= SPICFG_SPICLKPOL | SPICFG_RXCLKEDGE_FALLING; ++ break; ++ case SPI_MODE_3: ++ reg |= SPICFG_SPICLKPOL | SPICFG_TXCLKEDGE_FALLING; ++ break; ++ } ++ rs->mode = spi->mode; ++ ++#if 0 ++ /* set spiclk and spiena to tri-state */ ++ reg |= SPICFG_HIZSPI; ++#endif ++ ++ /* clock divide */ ++ reg |= rt2880_spi_baudrate_get(spi, spi->max_speed_hz); ++ ++ rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg); ++ ++ return 0; ++} ++ ++static int rt2880_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct rt2880_spi *rs; ++ void __iomem *base; ++ struct resource *r; ++ struct clk *clk; ++ int ret; ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, r); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "unable to get SYS clock\n"); ++ return PTR_ERR(clk); ++ } ++ ++ ret = clk_prepare_enable(clk); ++ if (ret) ++ goto err_clk; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*rs)); ++ if (master == NULL) { ++ dev_dbg(&pdev->dev, "master allocation failed\n"); ++ ret = -ENOMEM; ++ goto err_clk; ++ } ++ ++ master->dev.of_node = pdev->dev.of_node; ++ master->mode_bits = RT2880_SPI_MODE_BITS; ++ master->bits_per_word_mask = SPI_BPW_MASK(8); ++ master->min_speed_hz = clk_get_rate(clk) / 128; ++ master->max_speed_hz = clk_get_rate(clk) / 2; ++ master->flags = SPI_MASTER_HALF_DUPLEX; ++ master->setup = rt2880_spi_setup; ++ master->prepare_message = rt2880_spi_prepare_message; ++ master->set_cs = rt2880_spi_set_cs; ++ master->transfer_one = rt2880_spi_transfer_one, ++ ++ dev_set_drvdata(&pdev->dev, master); ++ ++ rs = spi_master_get_devdata(master); ++ rs->master = master; ++ rs->base = base; ++ rs->clk = clk; ++ ++ if (atomic_inc_return(&hw_reset_count) == 1) ++ device_reset(&pdev->dev); ++ ++ ret = devm_spi_register_master(&pdev->dev, master); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "devm_spi_register_master error.\n"); ++ goto err_master; ++ } ++ ++ return ret; ++ ++err_master: ++ spi_master_put(master); ++ kfree(master); ++err_clk: ++ clk_disable_unprepare(clk); ++ ++ return ret; ++} ++ ++static int rt2880_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct rt2880_spi *rs; ++ ++ master = dev_get_drvdata(&pdev->dev); ++ rs = spi_master_get_devdata(master); ++ ++ clk_disable_unprepare(rs->clk); ++ atomic_dec(&hw_reset_count); ++ ++ return 0; ++} ++ ++MODULE_ALIAS("platform:" DRIVER_NAME); ++ ++static const struct of_device_id rt2880_spi_match[] = { ++ { .compatible = "ralink,rt2880-spi" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt2880_spi_match); ++ ++static struct platform_driver rt2880_spi_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = rt2880_spi_match, ++ }, ++ .probe = rt2880_spi_probe, ++ .remove = rt2880_spi_remove, ++}; ++ ++module_platform_driver(rt2880_spi_driver); ++ ++MODULE_DESCRIPTION("Ralink SPI driver"); ++MODULE_AUTHOR("Sergiy <piratfm@gmail.com>"); ++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/ramips/patches-5.10/0044-i2c-MIPS-adds-ralink-I2C-driver.patch b/target/linux/ramips/patches-5.10/0044-i2c-MIPS-adds-ralink-I2C-driver.patch new file mode 100644 index 0000000000..e3bb7f2488 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0044-i2c-MIPS-adds-ralink-I2C-driver.patch @@ -0,0 +1,507 @@ +From 723b8beaabf3c3c4b1ce69480141f1e926f3f3b2 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Sun, 27 Jul 2014 09:52:56 +0100 +Subject: [PATCH 44/53] i2c: MIPS: adds ralink I2C driver + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + .../devicetree/bindings/i2c/i2c-ralink.txt | 27 ++ + drivers/i2c/busses/Kconfig | 4 + + drivers/i2c/busses/Makefile | 1 + + drivers/i2c/busses/i2c-ralink.c | 327 ++++++++++++++++++++ + 4 files changed, 359 insertions(+) + create mode 100644 Documentation/devicetree/bindings/i2c/i2c-ralink.txt + create mode 100644 drivers/i2c/busses/i2c-ralink.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/i2c/i2c-ralink.txt +@@ -0,0 +1,27 @@ ++I2C for Ralink platforms ++ ++Required properties : ++- compatible : Must be "link,rt3052-i2c" ++- reg: physical base address of the controller and length of memory mapped ++ region. ++- #address-cells = <1>; ++- #size-cells = <0>; ++ ++Optional properties: ++- Child nodes conforming to i2c bus binding ++ ++Example : ++ ++palmbus@10000000 { ++ i2c@900 { ++ compatible = "link,rt3052-i2c"; ++ reg = <0x900 0x100>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hwmon@4b { ++ compatible = "national,lm92"; ++ reg = <0x4b>; ++ }; ++ }; ++}; +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -922,6 +922,11 @@ config I2C_RK3X + This driver can also be built as a module. If so, the module will + be called i2c-rk3x. + ++config I2C_RALINK ++ tristate "Ralink I2C Controller" ++ depends on RALINK && !SOC_MT7621 ++ select OF_I2C ++ + config HAVE_S3C2410_I2C + bool + help +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -91,6 +91,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o + obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o + obj-$(CONFIG_I2C_PXA) += i2c-pxa.o + obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o ++obj-$(CONFIG_I2C_RALINK) += i2c-ralink.o + obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-geni.o + obj-$(CONFIG_I2C_QUP) += i2c-qup.o + obj-$(CONFIG_I2C_RIIC) += i2c-riic.o +--- /dev/null ++++ b/drivers/i2c/busses/i2c-ralink.c +@@ -0,0 +1,435 @@ ++/* ++ * drivers/i2c/busses/i2c-ralink.c ++ * ++ * Copyright (C) 2013 Steven Liu <steven_liu@mediatek.com> ++ * Copyright (C) 2016 Michael Lee <igvtee@gmail.com> ++ * ++ * Improve driver for i2cdetect from i2c-tools to detect i2c devices on the bus. ++ * (C) 2014 Sittisak <sittisaks@hotmail.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/interrupt.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/reset.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/platform_device.h> ++#include <linux/of_platform.h> ++#include <linux/i2c.h> ++#include <linux/io.h> ++#include <linux/err.h> ++#include <linux/clk.h> ++ ++#define REG_CONFIG_REG 0x00 ++#define REG_CLKDIV_REG 0x04 ++#define REG_DEVADDR_REG 0x08 ++#define REG_ADDR_REG 0x0C ++#define REG_DATAOUT_REG 0x10 ++#define REG_DATAIN_REG 0x14 ++#define REG_STATUS_REG 0x18 ++#define REG_STARTXFR_REG 0x1C ++#define REG_BYTECNT_REG 0x20 ++ ++/* REG_CONFIG_REG */ ++#define I2C_ADDRLEN_OFFSET 5 ++#define I2C_DEVADLEN_OFFSET 2 ++#define I2C_ADDRLEN_MASK 0x3 ++#define I2C_ADDR_DIS BIT(1) ++#define I2C_DEVADDR_DIS BIT(0) ++#define I2C_ADDRLEN_8 (7 << I2C_ADDRLEN_OFFSET) ++#define I2C_DEVADLEN_7 (6 << I2C_DEVADLEN_OFFSET) ++#define I2C_CONF_DEFAULT (I2C_ADDRLEN_8 | I2C_DEVADLEN_7) ++ ++/* REG_CLKDIV_REG */ ++#define I2C_CLKDIV_MASK 0xffff ++ ++/* REG_DEVADDR_REG */ ++#define I2C_DEVADDR_MASK 0x7f ++ ++/* REG_ADDR_REG */ ++#define I2C_ADDR_MASK 0xff ++ ++/* REG_STATUS_REG */ ++#define I2C_STARTERR BIT(4) ++#define I2C_ACKERR BIT(3) ++#define I2C_DATARDY BIT(2) ++#define I2C_SDOEMPTY BIT(1) ++#define I2C_BUSY BIT(0) ++ ++/* REG_STARTXFR_REG */ ++#define NOSTOP_CMD BIT(2) ++#define NODATA_CMD BIT(1) ++#define READ_CMD BIT(0) ++ ++/* REG_BYTECNT_REG */ ++#define BYTECNT_MAX 64 ++#define SET_BYTECNT(x) (x - 1) ++ ++/* timeout waiting for I2C devices to respond (clock streching) */ ++#define TIMEOUT_MS 1000 ++#define DELAY_INTERVAL_US 100 ++ ++struct rt_i2c { ++ void __iomem *base; ++ struct clk *clk; ++ struct device *dev; ++ struct i2c_adapter adap; ++ u32 cur_clk; ++ u32 clk_div; ++ u32 flags; ++}; ++ ++static void rt_i2c_w32(struct rt_i2c *i2c, u32 val, unsigned reg) ++{ ++ iowrite32(val, i2c->base + reg); ++} ++ ++static u32 rt_i2c_r32(struct rt_i2c *i2c, unsigned reg) ++{ ++ return ioread32(i2c->base + reg); ++} ++ ++static int poll_down_timeout(void __iomem *addr, u32 mask) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS); ++ ++ do { ++ if (!(readl_relaxed(addr) & mask)) ++ return 0; ++ ++ usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); ++ } while (time_before(jiffies, timeout)); ++ ++ return (readl_relaxed(addr) & mask) ? -EAGAIN : 0; ++} ++ ++static int rt_i2c_wait_idle(struct rt_i2c *i2c) ++{ ++ int ret; ++ ++ ret = poll_down_timeout(i2c->base + REG_STATUS_REG, I2C_BUSY); ++ if (ret < 0) ++ dev_dbg(i2c->dev, "idle err(%d)\n", ret); ++ ++ return ret; ++} ++ ++static int poll_up_timeout(void __iomem *addr, u32 mask) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS); ++ u32 status; ++ ++ do { ++ status = readl_relaxed(addr); ++ ++ /* check error status */ ++ if (status & I2C_STARTERR) ++ return -EAGAIN; ++ else if (status & I2C_ACKERR) ++ return -ENXIO; ++ else if (status & mask) ++ return 0; ++ ++ usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); ++ } while (time_before(jiffies, timeout)); ++ ++ return -ETIMEDOUT; ++} ++ ++static int rt_i2c_wait_rx_done(struct rt_i2c *i2c) ++{ ++ int ret; ++ ++ ret = poll_up_timeout(i2c->base + REG_STATUS_REG, I2C_DATARDY); ++ if (ret < 0) ++ dev_dbg(i2c->dev, "rx err(%d)\n", ret); ++ ++ return ret; ++} ++ ++static int rt_i2c_wait_tx_done(struct rt_i2c *i2c) ++{ ++ int ret; ++ ++ ret = poll_up_timeout(i2c->base + REG_STATUS_REG, I2C_SDOEMPTY); ++ if (ret < 0) ++ dev_dbg(i2c->dev, "tx err(%d)\n", ret); ++ ++ return ret; ++} ++ ++static void rt_i2c_reset(struct rt_i2c *i2c) ++{ ++ device_reset(i2c->adap.dev.parent); ++ barrier(); ++ rt_i2c_w32(i2c, i2c->clk_div, REG_CLKDIV_REG); ++} ++ ++static void rt_i2c_dump_reg(struct rt_i2c *i2c) ++{ ++ dev_dbg(i2c->dev, "conf %08x, clkdiv %08x, devaddr %08x, " \ ++ "addr %08x, dataout %08x, datain %08x, " \ ++ "status %08x, startxfr %08x, bytecnt %08x\n", ++ rt_i2c_r32(i2c, REG_CONFIG_REG), ++ rt_i2c_r32(i2c, REG_CLKDIV_REG), ++ rt_i2c_r32(i2c, REG_DEVADDR_REG), ++ rt_i2c_r32(i2c, REG_ADDR_REG), ++ rt_i2c_r32(i2c, REG_DATAOUT_REG), ++ rt_i2c_r32(i2c, REG_DATAIN_REG), ++ rt_i2c_r32(i2c, REG_STATUS_REG), ++ rt_i2c_r32(i2c, REG_STARTXFR_REG), ++ rt_i2c_r32(i2c, REG_BYTECNT_REG)); ++} ++ ++static int rt_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int num) ++{ ++ struct rt_i2c *i2c; ++ struct i2c_msg *pmsg; ++ unsigned char addr; ++ int i, j, ret; ++ u32 cmd; ++ ++ i2c = i2c_get_adapdata(adap); ++ ++ for (i = 0; i < num; i++) { ++ pmsg = &msgs[i]; ++ if (i == (num - 1)) ++ cmd = 0; ++ else ++ cmd = NOSTOP_CMD; ++ ++ dev_dbg(i2c->dev, "addr: 0x%x, len: %d, flags: 0x%x, stop: %d\n", ++ pmsg->addr, pmsg->len, pmsg->flags, ++ (cmd == 0)? 1 : 0); ++ ++ /* wait hardware idle */ ++ if ((ret = rt_i2c_wait_idle(i2c))) ++ goto err_timeout; ++ ++ if (pmsg->flags & I2C_M_TEN) { ++ rt_i2c_w32(i2c, I2C_CONF_DEFAULT, REG_CONFIG_REG); ++ /* 10 bits address */ ++ addr = 0x78 | ((pmsg->addr >> 8) & 0x03); ++ rt_i2c_w32(i2c, addr & I2C_DEVADDR_MASK, ++ REG_DEVADDR_REG); ++ rt_i2c_w32(i2c, pmsg->addr & I2C_ADDR_MASK, ++ REG_ADDR_REG); ++ } else { ++ rt_i2c_w32(i2c, I2C_CONF_DEFAULT | I2C_ADDR_DIS, ++ REG_CONFIG_REG); ++ /* 7 bits address */ ++ rt_i2c_w32(i2c, pmsg->addr & I2C_DEVADDR_MASK, ++ REG_DEVADDR_REG); ++ } ++ ++ /* buffer length */ ++ if (pmsg->len == 0) ++ cmd |= NODATA_CMD; ++ else ++ rt_i2c_w32(i2c, SET_BYTECNT(pmsg->len), ++ REG_BYTECNT_REG); ++ ++ j = 0; ++ if (pmsg->flags & I2C_M_RD) { ++ cmd |= READ_CMD; ++ /* start transfer */ ++ barrier(); ++ rt_i2c_w32(i2c, cmd, REG_STARTXFR_REG); ++ do { ++ /* wait */ ++ if ((ret = rt_i2c_wait_rx_done(i2c))) ++ goto err_timeout; ++ /* read data */ ++ if (pmsg->len) ++ pmsg->buf[j] = rt_i2c_r32(i2c, ++ REG_DATAIN_REG); ++ j++; ++ } while (j < pmsg->len); ++ } else { ++ do { ++ /* write data */ ++ if (pmsg->len) ++ rt_i2c_w32(i2c, pmsg->buf[j], ++ REG_DATAOUT_REG); ++ /* start transfer */ ++ if (j == 0) { ++ barrier(); ++ rt_i2c_w32(i2c, cmd, REG_STARTXFR_REG); ++ } ++ /* wait */ ++ if ((ret = rt_i2c_wait_tx_done(i2c))) ++ goto err_timeout; ++ j++; ++ } while (j < pmsg->len); ++ } ++ } ++ /* the return value is number of executed messages */ ++ ret = i; ++ ++ return ret; ++ ++err_timeout: ++ rt_i2c_dump_reg(i2c); ++ rt_i2c_reset(i2c); ++ return ret; ++} ++ ++static u32 rt_i2c_func(struct i2c_adapter *a) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; ++} ++ ++static const struct i2c_algorithm rt_i2c_algo = { ++ .master_xfer = rt_i2c_master_xfer, ++ .functionality = rt_i2c_func, ++}; ++ ++static const struct of_device_id i2c_rt_dt_ids[] = { ++ { .compatible = "ralink,rt2880-i2c" }, ++ { /* sentinel */ } ++}; ++ ++MODULE_DEVICE_TABLE(of, i2c_rt_dt_ids); ++ ++static struct i2c_adapter_quirks rt_i2c_quirks = { ++ .max_write_len = BYTECNT_MAX, ++ .max_read_len = BYTECNT_MAX, ++}; ++ ++static int rt_i2c_init(struct rt_i2c *i2c) ++{ ++ u32 reg; ++ ++ /* i2c_sclk = periph_clk / ((2 * clk_div) + 5) */ ++ i2c->clk_div = (clk_get_rate(i2c->clk) - (5 * i2c->cur_clk)) / ++ (2 * i2c->cur_clk); ++ if (i2c->clk_div < 8) ++ i2c->clk_div = 8; ++ if (i2c->clk_div > I2C_CLKDIV_MASK) ++ i2c->clk_div = I2C_CLKDIV_MASK; ++ ++ /* check support combinde/repeated start message */ ++ rt_i2c_w32(i2c, NOSTOP_CMD, REG_STARTXFR_REG); ++ reg = rt_i2c_r32(i2c, REG_STARTXFR_REG) & NOSTOP_CMD; ++ ++ rt_i2c_reset(i2c); ++ ++ return reg; ++} ++ ++static int rt_i2c_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct rt_i2c *i2c; ++ struct i2c_adapter *adap; ++ const struct of_device_id *match; ++ int ret, restart; ++ ++ match = of_match_device(i2c_rt_dt_ids, &pdev->dev); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "no memory resource found\n"); ++ return -ENODEV; ++ } ++ ++ i2c = devm_kzalloc(&pdev->dev, sizeof(struct rt_i2c), GFP_KERNEL); ++ if (!i2c) { ++ dev_err(&pdev->dev, "failed to allocate i2c_adapter\n"); ++ return -ENOMEM; ++ } ++ ++ i2c->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(i2c->base)) ++ return PTR_ERR(i2c->base); ++ ++ i2c->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(i2c->clk)) { ++ dev_err(&pdev->dev, "no clock defined\n"); ++ return -ENODEV; ++ } ++ clk_prepare_enable(i2c->clk); ++ i2c->dev = &pdev->dev; ++ ++ if (of_property_read_u32(pdev->dev.of_node, ++ "clock-frequency", &i2c->cur_clk)) ++ i2c->cur_clk = 100000; ++ ++ adap = &i2c->adap; ++ adap->owner = THIS_MODULE; ++ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; ++ adap->algo = &rt_i2c_algo; ++ adap->retries = 3; ++ adap->dev.parent = &pdev->dev; ++ i2c_set_adapdata(adap, i2c); ++ adap->dev.of_node = pdev->dev.of_node; ++ strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); ++ adap->quirks = &rt_i2c_quirks; ++ ++ platform_set_drvdata(pdev, i2c); ++ ++ restart = rt_i2c_init(i2c); ++ ++ ret = i2c_add_adapter(adap); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "failed to add adapter\n"); ++ clk_disable_unprepare(i2c->clk); ++ return ret; ++ } ++ ++ dev_info(&pdev->dev, "clock %uKHz, re-start %ssupport\n", ++ i2c->cur_clk/1000, restart ? "" : "not "); ++ ++ return ret; ++} ++ ++static int rt_i2c_remove(struct platform_device *pdev) ++{ ++ struct rt_i2c *i2c = platform_get_drvdata(pdev); ++ ++ i2c_del_adapter(&i2c->adap); ++ clk_disable_unprepare(i2c->clk); ++ ++ return 0; ++} ++ ++static struct platform_driver rt_i2c_driver = { ++ .probe = rt_i2c_probe, ++ .remove = rt_i2c_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "i2c-ralink", ++ .of_match_table = i2c_rt_dt_ids, ++ }, ++}; ++ ++static int __init i2c_rt_init (void) ++{ ++ return platform_driver_register(&rt_i2c_driver); ++} ++subsys_initcall(i2c_rt_init); ++ ++static void __exit i2c_rt_exit (void) ++{ ++ platform_driver_unregister(&rt_i2c_driver); ++} ++module_exit(i2c_rt_exit); ++ ++MODULE_AUTHOR("Steven Liu <steven_liu@mediatek.com>"); ++MODULE_DESCRIPTION("Ralink I2c host driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:Ralink-I2C"); diff --git a/target/linux/ramips/patches-5.10/0046-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch b/target/linux/ramips/patches-5.10/0046-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch new file mode 100644 index 0000000000..f3912a1054 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0046-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch @@ -0,0 +1,43 @@ +From 23147af14531cbdada194b94120ef8774f46292d Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Thu, 13 Nov 2014 19:08:40 +0100 +Subject: [PATCH 46/53] mmc: MIPS: ralink: add sdhci for mt7620a SoC + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/mmc/host/Kconfig | 2 + + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/mtk-mmc/Kconfig | 16 + + drivers/mmc/host/mtk-mmc/Makefile | 42 + + drivers/mmc/host/mtk-mmc/board.h | 137 ++ + drivers/mmc/host/mtk-mmc/dbg.c | 347 ++++ + drivers/mmc/host/mtk-mmc/dbg.h | 156 ++ + drivers/mmc/host/mtk-mmc/mt6575_sd.h | 1001 +++++++++++ + drivers/mmc/host/mtk-mmc/sd.c | 3060 ++++++++++++++++++++++++++++++++++ + 9 files changed, 4762 insertions(+) + create mode 100644 drivers/mmc/host/mtk-mmc/Kconfig + create mode 100644 drivers/mmc/host/mtk-mmc/Makefile + create mode 100644 drivers/mmc/host/mtk-mmc/board.h + create mode 100644 drivers/mmc/host/mtk-mmc/dbg.c + create mode 100644 drivers/mmc/host/mtk-mmc/dbg.h + create mode 100644 drivers/mmc/host/mtk-mmc/mt6575_sd.h + create mode 100644 drivers/mmc/host/mtk-mmc/sd.c + +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -1019,3 +1019,5 @@ config MMC_SDHCI_AM654 + If you have a controller with this interface, say Y or M here. + + If unsure, say N. ++ ++source "drivers/mmc/host/mtk-mmc/Kconfig" +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -3,6 +3,7 @@ + # Makefile for MMC/SD host controller drivers + # + ++obj-$(CONFIG_MTK_MMC) += mtk-mmc/ + obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o + armmmci-y := mmci.o + armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o diff --git a/target/linux/ramips/patches-5.10/0048-asoc-add-mt7620-support.patch b/target/linux/ramips/patches-5.10/0048-asoc-add-mt7620-support.patch new file mode 100644 index 0000000000..d0d4141a8d --- /dev/null +++ b/target/linux/ramips/patches-5.10/0048-asoc-add-mt7620-support.patch @@ -0,0 +1,1046 @@ +From 7f29222b1731e8182ba94a331531dec18865a1e4 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Sun, 27 Jul 2014 09:31:47 +0100 +Subject: [PATCH 48/53] asoc: add mt7620 support + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/ralink/of.c | 2 + + sound/soc/Kconfig | 1 + + sound/soc/Makefile | 1 + + sound/soc/ralink/Kconfig | 15 ++ + sound/soc/ralink/Makefile | 11 + + sound/soc/ralink/mt7620-i2s.c | 436 ++++++++++++++++++++++++++++++++++++++ + sound/soc/ralink/mt7620-wm8960.c | 233 ++++++++++++++++++++ + 7 files changed, 699 insertions(+) + create mode 100644 sound/soc/ralink/Kconfig + create mode 100644 sound/soc/ralink/Makefile + create mode 100644 sound/soc/ralink/mt7620-i2s.c + create mode 100644 sound/soc/ralink/mt7620-wm8960.c + +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -13,6 +13,7 @@ + #include <linux/of_fdt.h> + #include <linux/kernel.h> + #include <linux/memblock.h> ++#include <linux/module.h> + #include <linux/of_platform.h> + #include <linux/of_address.h> + +@@ -24,6 +25,7 @@ + #include "common.h" + + __iomem void *rt_sysc_membase; ++EXPORT_SYMBOL(rt_sysc_membase); + __iomem void *rt_memc_membase; + + __iomem void *plat_of_remap_node(const char *node) +--- a/sound/soc/Kconfig ++++ b/sound/soc/Kconfig +@@ -60,6 +60,7 @@ source "sound/soc/mxs/Kconfig" + source "sound/soc/pxa/Kconfig" + source "sound/soc/qcom/Kconfig" + source "sound/soc/rockchip/Kconfig" ++source "sound/soc/ralink/Kconfig" + source "sound/soc/samsung/Kconfig" + source "sound/soc/sh/Kconfig" + source "sound/soc/sirf/Kconfig" +--- a/sound/soc/Makefile ++++ b/sound/soc/Makefile +@@ -43,6 +43,7 @@ obj-$(CONFIG_SND_SOC) += kirkwood/ + obj-$(CONFIG_SND_SOC) += pxa/ + obj-$(CONFIG_SND_SOC) += qcom/ + obj-$(CONFIG_SND_SOC) += rockchip/ ++obj-$(CONFIG_SND_SOC) += ralink/ + obj-$(CONFIG_SND_SOC) += samsung/ + obj-$(CONFIG_SND_SOC) += sh/ + obj-$(CONFIG_SND_SOC) += sirf/ +--- /dev/null ++++ b/sound/soc/ralink/Kconfig +@@ -0,0 +1,8 @@ ++config SND_RALINK_SOC_I2S ++ depends on RALINK && SND_SOC && !SOC_RT288X ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ select REGMAP_MMIO ++ tristate "SoC Audio (I2S protocol) for Ralink SoC" ++ help ++ Say Y if you want to use I2S protocol and I2S codec on Ralink/MediaTek ++ based boards. +--- /dev/null ++++ b/sound/soc/ralink/Makefile +@@ -0,0 +1,6 @@ ++# ++# Ralink/MediaTek Platform Support ++# ++snd-soc-ralink-i2s-objs := ralink-i2s.o ++ ++obj-$(CONFIG_SND_RALINK_SOC_I2S) += snd-soc-ralink-i2s.o +--- /dev/null ++++ b/sound/soc/ralink/ralink-i2s.c +@@ -0,0 +1,965 @@ ++/* ++ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> ++ * Copyright (C) 2016 Michael Lee <igvtee@gmail.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/clk.h> ++#include <linux/regmap.h> ++#include <linux/reset.h> ++#include <linux/debugfs.h> ++#include <linux/of_device.h> ++#include <sound/pcm_params.h> ++#include <sound/dmaengine_pcm.h> ++ ++#include <asm/mach-ralink/ralink_regs.h> ++ ++#define DRV_NAME "ralink-i2s" ++ ++#define I2S_REG_CFG0 0x00 ++#define I2S_REG_INT_STATUS 0x04 ++#define I2S_REG_INT_EN 0x08 ++#define I2S_REG_FF_STATUS 0x0c ++#define I2S_REG_WREG 0x10 ++#define I2S_REG_RREG 0x14 ++#define I2S_REG_CFG1 0x18 ++#define I2S_REG_DIVCMP 0x20 ++#define I2S_REG_DIVINT 0x24 ++ ++/* I2S_REG_CFG0 */ ++#define I2S_REG_CFG0_EN BIT(31) ++#define I2S_REG_CFG0_DMA_EN BIT(30) ++#define I2S_REG_CFG0_BYTE_SWAP BIT(28) ++#define I2S_REG_CFG0_TX_EN BIT(24) ++#define I2S_REG_CFG0_RX_EN BIT(20) ++#define I2S_REG_CFG0_SLAVE BIT(16) ++#define I2S_REG_CFG0_RX_THRES 12 ++#define I2S_REG_CFG0_TX_THRES 4 ++#define I2S_REG_CFG0_THRES_MASK (0xf << I2S_REG_CFG0_RX_THRES) | \ ++ (4 << I2S_REG_CFG0_TX_THRES) ++#define I2S_REG_CFG0_DFT_THRES (4 << I2S_REG_CFG0_RX_THRES) | \ ++ (4 << I2S_REG_CFG0_TX_THRES) ++/* RT305x */ ++#define I2S_REG_CFG0_CLK_DIS BIT(8) ++#define I2S_REG_CFG0_TXCH_SWAP BIT(3) ++#define I2S_REG_CFG0_TXCH1_OFF BIT(2) ++#define I2S_REG_CFG0_TXCH0_OFF BIT(1) ++#define I2S_REG_CFG0_SLAVE_EN BIT(0) ++/* RT3883 */ ++#define I2S_REG_CFG0_RXCH_SWAP BIT(11) ++#define I2S_REG_CFG0_RXCH1_OFF BIT(10) ++#define I2S_REG_CFG0_RXCH0_OFF BIT(9) ++#define I2S_REG_CFG0_WS_INV BIT(0) ++/* MT7628 */ ++#define I2S_REG_CFG0_FMT_LE BIT(29) ++#define I2S_REG_CFG0_SYS_BE BIT(28) ++#define I2S_REG_CFG0_NORM_24 BIT(18) ++#define I2S_REG_CFG0_DATA_24 BIT(17) ++ ++/* I2S_REG_INT_STATUS */ ++#define I2S_REG_INT_RX_FAULT BIT(7) ++#define I2S_REG_INT_RX_OVRUN BIT(6) ++#define I2S_REG_INT_RX_UNRUN BIT(5) ++#define I2S_REG_INT_RX_THRES BIT(4) ++#define I2S_REG_INT_TX_FAULT BIT(3) ++#define I2S_REG_INT_TX_OVRUN BIT(2) ++#define I2S_REG_INT_TX_UNRUN BIT(1) ++#define I2S_REG_INT_TX_THRES BIT(0) ++#define I2S_REG_INT_TX_MASK 0xf ++#define I2S_REG_INT_RX_MASK 0xf0 ++ ++/* I2S_REG_INT_STATUS */ ++#define I2S_RX_AVCNT(x) ((x >> 4) & 0xf) ++#define I2S_TX_AVCNT(x) (x & 0xf) ++/* MT7628 */ ++#define MT7628_I2S_RX_AVCNT(x) ((x >> 8) & 0x1f) ++#define MT7628_I2S_TX_AVCNT(x) (x & 0x1f) ++ ++/* I2S_REG_CFG1 */ ++#define I2S_REG_CFG1_LBK BIT(31) ++#define I2S_REG_CFG1_EXTLBK BIT(30) ++/* RT3883 */ ++#define I2S_REG_CFG1_LEFT_J BIT(0) ++#define I2S_REG_CFG1_RIGHT_J BIT(1) ++#define I2S_REG_CFG1_FMT_MASK 0x3 ++ ++/* I2S_REG_DIVCMP */ ++#define I2S_REG_DIVCMP_CLKEN BIT(31) ++#define I2S_REG_DIVCMP_DIVCOMP_MASK 0x1ff ++ ++/* I2S_REG_DIVINT */ ++#define I2S_REG_DIVINT_MASK 0x3ff ++ ++/* BCLK dividers */ ++#define RALINK_I2S_DIVCMP 0 ++#define RALINK_I2S_DIVINT 1 ++ ++/* FIFO */ ++#define RALINK_I2S_FIFO_SIZE 32 ++ ++/* feature flags */ ++#define RALINK_FLAGS_TXONLY BIT(0) ++#define RALINK_FLAGS_LEFT_J BIT(1) ++#define RALINK_FLAGS_RIGHT_J BIT(2) ++#define RALINK_FLAGS_ENDIAN BIT(3) ++#define RALINK_FLAGS_24BIT BIT(4) ++ ++#define RALINK_I2S_INT_EN 0 ++ ++struct ralink_i2s_stats { ++ u32 dmafault; ++ u32 overrun; ++ u32 underrun; ++ u32 belowthres; ++}; ++ ++struct ralink_i2s { ++ struct device *dev; ++ void __iomem *regs; ++ struct clk *clk; ++ struct regmap *regmap; ++ u32 flags; ++ unsigned int fmt; ++ u16 txdma_req; ++ u16 rxdma_req; ++ ++ struct snd_dmaengine_dai_dma_data playback_dma_data; ++ struct snd_dmaengine_dai_dma_data capture_dma_data; ++ ++ struct dentry *dbg_dir; ++ struct dentry *dbg_stats; ++ struct ralink_i2s_stats txstats; ++ struct ralink_i2s_stats rxstats; ++}; ++ ++static void ralink_i2s_dump_regs(struct ralink_i2s *i2s) ++{ ++ u32 buf[10]; ++ int ret; ++ ++ ret = regmap_bulk_read(i2s->regmap, I2S_REG_CFG0, ++ buf, ARRAY_SIZE(buf)); ++ ++ dev_dbg(i2s->dev, "CFG0: %08x, INTSTAT: %08x, INTEN: %08x, " \ ++ "FFSTAT: %08x, WREG: %08x, RREG: %08x, " \ ++ "CFG1: %08x, DIVCMP: %08x, DIVINT: %08x\n", ++ buf[0], buf[1], buf[2], buf[3], buf[4], ++ buf[5], buf[6], buf[8], buf[9]); ++} ++ ++static int ralink_i2s_set_sysclk(struct snd_soc_dai *dai, ++ int clk_id, unsigned int freq, int dir) ++{ ++ return 0; ++} ++ ++static int ralink_i2s_set_sys_bclk(struct snd_soc_dai *dai, int width, int rate) ++{ ++ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); ++ unsigned long clk = clk_get_rate(i2s->clk); ++ int div; ++ uint32_t data; ++ ++ /* disable clock at slave mode */ ++ if ((i2s->fmt & SND_SOC_DAIFMT_MASTER_MASK) == ++ SND_SOC_DAIFMT_CBM_CFM) { ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_CLK_DIS, ++ I2S_REG_CFG0_CLK_DIS); ++ return 0; ++ } ++ ++ /* FREQOUT = FREQIN / (I2S_CLK_DIV + 1) */ ++ div = (clk / rate ) - 1; ++ ++ data = rt_sysc_r32(0x30); ++ data &= (0xff << 8); ++ data |= (0x1 << 15) | (div << 8); ++ rt_sysc_w32(data, 0x30); ++ ++ /* enable clock */ ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, I2S_REG_CFG0_CLK_DIS, 0); ++ ++ dev_dbg(i2s->dev, "clk: %lu, rate: %u, div: %d\n", ++ clk, rate, div); ++ ++ return 0; ++} ++ ++static int ralink_i2s_set_bclk(struct snd_soc_dai *dai, int width, int rate) ++{ ++ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); ++ unsigned long clk = clk_get_rate(i2s->clk); ++ int divint, divcomp; ++ ++ /* disable clock at slave mode */ ++ if ((i2s->fmt & SND_SOC_DAIFMT_MASTER_MASK) == ++ SND_SOC_DAIFMT_CBM_CFM) { ++ regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, ++ I2S_REG_DIVCMP_CLKEN, 0); ++ return 0; ++ } ++ ++ /* FREQOUT = FREQIN * (1/2) * (1/(DIVINT + DIVCOMP/512)) */ ++ clk = clk / (2 * 2 * width); ++ divint = clk / rate; ++ divcomp = ((clk % rate) * 512) / rate; ++ ++ if ((divint > I2S_REG_DIVINT_MASK) || ++ (divcomp > I2S_REG_DIVCMP_DIVCOMP_MASK)) ++ return -EINVAL; ++ ++ regmap_update_bits(i2s->regmap, I2S_REG_DIVINT, ++ I2S_REG_DIVINT_MASK, divint); ++ regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, ++ I2S_REG_DIVCMP_DIVCOMP_MASK, divcomp); ++ ++ /* enable clock */ ++ regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, I2S_REG_DIVCMP_CLKEN, ++ I2S_REG_DIVCMP_CLKEN); ++ ++ dev_dbg(i2s->dev, "clk: %lu, rate: %u, int: %d, comp: %d\n", ++ clk_get_rate(i2s->clk), rate, divint, divcomp); ++ ++ return 0; ++} ++ ++static int ralink_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ++{ ++ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); ++ unsigned int cfg0 = 0, cfg1 = 0; ++ ++ /* set master/slave audio interface */ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBM_CFM: ++ if (i2s->flags & RALINK_FLAGS_TXONLY) ++ cfg0 |= I2S_REG_CFG0_SLAVE_EN; ++ else ++ cfg0 |= I2S_REG_CFG0_SLAVE; ++ break; ++ case SND_SOC_DAIFMT_CBS_CFS: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* interface format */ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ break; ++ case SND_SOC_DAIFMT_RIGHT_J: ++ if (i2s->flags & RALINK_FLAGS_RIGHT_J) { ++ cfg1 |= I2S_REG_CFG1_RIGHT_J; ++ break; ++ } ++ return -EINVAL; ++ case SND_SOC_DAIFMT_LEFT_J: ++ if (i2s->flags & RALINK_FLAGS_LEFT_J) { ++ cfg1 |= I2S_REG_CFG1_LEFT_J; ++ break; ++ } ++ return -EINVAL; ++ default: ++ return -EINVAL; ++ } ++ ++ /* clock inversion */ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (i2s->flags & RALINK_FLAGS_TXONLY) { ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_SLAVE_EN, cfg0); ++ } else { ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_SLAVE, cfg0); ++ } ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG1, ++ I2S_REG_CFG1_FMT_MASK, cfg1); ++ i2s->fmt = fmt; ++ ++ return 0; ++} ++ ++static int ralink_i2s_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); ++ ++ if (dai->active) ++ return 0; ++ ++ /* setup status interrupt */ ++#if (RALINK_I2S_INT_EN) ++ regmap_write(i2s->regmap, I2S_REG_INT_EN, 0xff); ++#else ++ regmap_write(i2s->regmap, I2S_REG_INT_EN, 0x0); ++#endif ++ ++ /* enable */ ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_EN | I2S_REG_CFG0_DMA_EN | ++ I2S_REG_CFG0_THRES_MASK, ++ I2S_REG_CFG0_EN | I2S_REG_CFG0_DMA_EN | ++ I2S_REG_CFG0_DFT_THRES); ++ ++ return 0; ++} ++ ++static void ralink_i2s_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); ++ ++ /* If both streams are stopped, disable module and clock */ ++ if (dai->active) ++ return; ++ ++ /* ++ * datasheet mention when disable all control regs are cleared ++ * to initial values. need reinit at startup. ++ */ ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, I2S_REG_CFG0_EN, 0); ++} ++ ++static int ralink_i2s_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) ++{ ++ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); ++ int width; ++ int ret; ++ ++ width = params_width(params); ++ switch (width) { ++ case 16: ++ if (i2s->flags & RALINK_FLAGS_24BIT) ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_DATA_24, 0); ++ break; ++ case 24: ++ if (i2s->flags & RALINK_FLAGS_24BIT) { ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_DATA_24, ++ I2S_REG_CFG0_DATA_24); ++ break; ++ } ++ return -EINVAL; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (params_channels(params)) { ++ case 2: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (i2s->flags & RALINK_FLAGS_ENDIAN) { ++ /* system endian */ ++#ifdef SNDRV_LITTLE_ENDIAN ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_SYS_BE, 0); ++#else ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_SYS_BE, ++ I2S_REG_CFG0_SYS_BE); ++#endif ++ ++ /* data endian */ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ case SNDRV_PCM_FORMAT_S24_LE: ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_FMT_LE, ++ I2S_REG_CFG0_FMT_LE); ++ break; ++ case SNDRV_PCM_FORMAT_S16_BE: ++ case SNDRV_PCM_FORMAT_S24_BE: ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, ++ I2S_REG_CFG0_FMT_LE, 0); ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ /* setup bclk rate */ ++ if (i2s->flags & RALINK_FLAGS_TXONLY) ++ ret = ralink_i2s_set_sys_bclk(dai, width, params_rate(params)); ++ else ++ ret = ralink_i2s_set_bclk(dai, width, params_rate(params)); ++ ++ return ret; ++} ++ ++static int ralink_i2s_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); ++ unsigned int mask, val; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ mask = I2S_REG_CFG0_TX_EN; ++ else ++ mask = I2S_REG_CFG0_RX_EN; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ val = mask; ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ val = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, mask, val); ++ ++ return 0; ++} ++ ++static void ralink_i2s_init_dma_data(struct ralink_i2s *i2s, ++ struct resource *res) ++{ ++ struct snd_dmaengine_dai_dma_data *dma_data; ++ ++ /* Playback */ ++ dma_data = &i2s->playback_dma_data; ++ dma_data->addr = res->start + I2S_REG_WREG; ++ dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dma_data->maxburst = 1; ++ dma_data->slave_id = i2s->txdma_req; ++ ++ if (i2s->flags & RALINK_FLAGS_TXONLY) ++ return; ++ ++ /* Capture */ ++ dma_data = &i2s->capture_dma_data; ++ dma_data->addr = res->start + I2S_REG_RREG; ++ dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dma_data->maxburst = 1; ++ dma_data->slave_id = i2s->rxdma_req; ++} ++ ++static int ralink_i2s_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); ++ ++ snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, ++ &i2s->capture_dma_data); ++ ++ return 0; ++} ++ ++static int ralink_i2s_dai_remove(struct snd_soc_dai *dai) ++{ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops ralink_i2s_dai_ops = { ++ .set_sysclk = ralink_i2s_set_sysclk, ++ .set_fmt = ralink_i2s_set_fmt, ++ .startup = ralink_i2s_startup, ++ .shutdown = ralink_i2s_shutdown, ++ .hw_params = ralink_i2s_hw_params, ++ .trigger = ralink_i2s_trigger, ++}; ++ ++static struct snd_soc_dai_driver ralink_i2s_dai = { ++ .name = DRV_NAME, ++ .probe = ralink_i2s_dai_probe, ++ .remove = ralink_i2s_dai_remove, ++ .ops = &ralink_i2s_dai_ops, ++ .capture = { ++ .stream_name = "I2S Capture", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = 5512, ++ .rate_max = 192000, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .playback = { ++ .stream_name = "I2S Playback", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = 5512, ++ .rate_max = 192000, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .symmetric_rates = 1, ++}; ++ ++static struct snd_pcm_hardware ralink_pcm_hardware = { ++ .info = SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ .channels_min = 2, ++ .channels_max = 2, ++ .period_bytes_min = PAGE_SIZE, ++ .period_bytes_max = PAGE_SIZE * 2, ++ .periods_min = 2, ++ .periods_max = 128, ++ .buffer_bytes_max = 128 * 1024, ++ .fifo_size = RALINK_I2S_FIFO_SIZE, ++}; ++ ++static const struct snd_dmaengine_pcm_config ralink_dmaengine_pcm_config = { ++ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, ++ .pcm_hardware = &ralink_pcm_hardware, ++ .prealloc_buffer_size = 256 * PAGE_SIZE, ++}; ++ ++static const struct snd_soc_component_driver ralink_i2s_component = { ++ .name = DRV_NAME, ++}; ++ ++static bool ralink_i2s_readable_reg(struct device *dev, unsigned int reg) ++{ ++ return true; ++} ++ ++static bool ralink_i2s_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case I2S_REG_INT_STATUS: ++ case I2S_REG_FF_STATUS: ++ return true; ++ } ++ return false; ++} ++ ++static bool ralink_i2s_writeable_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case I2S_REG_FF_STATUS: ++ case I2S_REG_RREG: ++ return false; ++ } ++ return true; ++} ++ ++static const struct regmap_config ralink_i2s_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .writeable_reg = ralink_i2s_writeable_reg, ++ .readable_reg = ralink_i2s_readable_reg, ++ .volatile_reg = ralink_i2s_volatile_reg, ++ .max_register = I2S_REG_DIVINT, ++}; ++ ++#if (RALINK_I2S_INT_EN) ++static irqreturn_t ralink_i2s_irq(int irq, void *devid) ++{ ++ struct ralink_i2s *i2s = devid; ++ u32 status; ++ ++ regmap_read(i2s->regmap, I2S_REG_INT_STATUS, &status); ++ if (unlikely(!status)) ++ return IRQ_NONE; ++ ++ /* tx stats */ ++ if (status & I2S_REG_INT_TX_MASK) { ++ if (status & I2S_REG_INT_TX_THRES) ++ i2s->txstats.belowthres++; ++ if (status & I2S_REG_INT_TX_UNRUN) ++ i2s->txstats.underrun++; ++ if (status & I2S_REG_INT_TX_OVRUN) ++ i2s->txstats.overrun++; ++ if (status & I2S_REG_INT_TX_FAULT) ++ i2s->txstats.dmafault++; ++ } ++ ++ /* rx stats */ ++ if (status & I2S_REG_INT_RX_MASK) { ++ if (status & I2S_REG_INT_RX_THRES) ++ i2s->rxstats.belowthres++; ++ if (status & I2S_REG_INT_RX_UNRUN) ++ i2s->rxstats.underrun++; ++ if (status & I2S_REG_INT_RX_OVRUN) ++ i2s->rxstats.overrun++; ++ if (status & I2S_REG_INT_RX_FAULT) ++ i2s->rxstats.dmafault++; ++ } ++ ++ /* clean status bits */ ++ regmap_write(i2s->regmap, I2S_REG_INT_STATUS, status); ++ ++ return IRQ_HANDLED; ++} ++#endif ++ ++#if IS_ENABLED(CONFIG_DEBUG_FS) ++static int ralink_i2s_stats_show(struct seq_file *s, void *unused) ++{ ++ struct ralink_i2s *i2s = s->private; ++ ++ seq_printf(s, "tx stats\n"); ++ seq_printf(s, "\tbelow threshold\t%u\n", i2s->txstats.belowthres); ++ seq_printf(s, "\tunder run\t%u\n", i2s->txstats.underrun); ++ seq_printf(s, "\tover run\t%u\n", i2s->txstats.overrun); ++ seq_printf(s, "\tdma fault\t%u\n", i2s->txstats.dmafault); ++ ++ seq_printf(s, "rx stats\n"); ++ seq_printf(s, "\tbelow threshold\t%u\n", i2s->rxstats.belowthres); ++ seq_printf(s, "\tunder run\t%u\n", i2s->rxstats.underrun); ++ seq_printf(s, "\tover run\t%u\n", i2s->rxstats.overrun); ++ seq_printf(s, "\tdma fault\t%u\n", i2s->rxstats.dmafault); ++ ++ ralink_i2s_dump_regs(i2s); ++ ++ return 0; ++} ++ ++static int ralink_i2s_stats_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, ralink_i2s_stats_show, inode->i_private); ++} ++ ++static const struct file_operations ralink_i2s_stats_ops = { ++ .open = ralink_i2s_stats_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static inline int ralink_i2s_debugfs_create(struct ralink_i2s *i2s) ++{ ++ i2s->dbg_dir = debugfs_create_dir(dev_name(i2s->dev), NULL); ++ if (!i2s->dbg_dir) ++ return -ENOMEM; ++ ++ i2s->dbg_stats = debugfs_create_file("stats", S_IRUGO, ++ i2s->dbg_dir, i2s, &ralink_i2s_stats_ops); ++ if (!i2s->dbg_stats) { ++ debugfs_remove(i2s->dbg_dir); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static inline void ralink_i2s_debugfs_remove(struct ralink_i2s *i2s) ++{ ++ debugfs_remove(i2s->dbg_stats); ++ debugfs_remove(i2s->dbg_dir); ++} ++#else ++static inline int ralink_i2s_debugfs_create(struct ralink_i2s *i2s) ++{ ++ return 0; ++} ++ ++static inline void ralink_i2s_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg) ++{ ++} ++#endif ++ ++/* ++ * TODO: these refclk setup functions should use ++ * clock framework instead. hardcode it now. ++ */ ++static void rt3350_refclk_setup(void) ++{ ++ uint32_t data; ++ ++ /* set refclk output 12Mhz clock */ ++ data = rt_sysc_r32(0x2c); ++ data |= (0x1 << 8); ++ rt_sysc_w32(data, 0x2c); ++} ++ ++static void rt3883_refclk_setup(void) ++{ ++ uint32_t data; ++ ++ /* set refclk output 12Mhz clock */ ++ data = rt_sysc_r32(0x2c); ++ data &= ~(0x3 << 13); ++ data |= (0x1 << 13); ++ rt_sysc_w32(data, 0x2c); ++} ++ ++static void rt3552_refclk_setup(void) ++{ ++ uint32_t data; ++ ++ /* set refclk output 12Mhz clock */ ++ data = rt_sysc_r32(0x2c); ++ data &= ~(0xf << 8); ++ data |= (0x3 << 8); ++ rt_sysc_w32(data, 0x2c); ++} ++ ++static void mt7620_refclk_setup(void) ++{ ++ uint32_t data; ++ ++ /* set refclk output 12Mhz clock */ ++ data = rt_sysc_r32(0x2c); ++ data &= ~(0x7 << 9); ++ data |= 0x1 << 9; ++ rt_sysc_w32(data, 0x2c); ++} ++ ++static void mt7621_refclk_setup(void) ++{ ++ uint32_t data; ++ ++ /* set refclk output 12Mhz clock */ ++ data = rt_sysc_r32(0x2c); ++ data &= ~(0x1f << 18); ++ data |= (0x19 << 18); ++ data &= ~(0x1f << 12); ++ data |= (0x1 << 12); ++ data &= ~(0x7 << 9); ++ data |= (0x5 << 9); ++ rt_sysc_w32(data, 0x2c); ++} ++ ++static void mt7628_refclk_setup(void) ++{ ++ uint32_t data; ++ ++ /* set i2s and refclk digital pad */ ++ data = rt_sysc_r32(0x3c); ++ data |= 0x1f; ++ rt_sysc_w32(data, 0x3c); ++ ++ /* Adjust REFCLK0's driving strength */ ++ data = rt_sysc_r32(0x1354); ++ data &= ~(0x1 << 5); ++ rt_sysc_w32(data, 0x1354); ++ data = rt_sysc_r32(0x1364); ++ data |= ~(0x1 << 5); ++ rt_sysc_w32(data, 0x1364); ++ ++ /* set refclk output 12Mhz clock */ ++ data = rt_sysc_r32(0x2c); ++ data &= ~(0x7 << 9); ++ data |= 0x1 << 9; ++ rt_sysc_w32(data, 0x2c); ++} ++ ++struct rt_i2s_data { ++ u32 flags; ++ void (*refclk_setup)(void); ++}; ++ ++struct rt_i2s_data rt3050_i2s_data = { .flags = RALINK_FLAGS_TXONLY }; ++struct rt_i2s_data rt3350_i2s_data = { .flags = RALINK_FLAGS_TXONLY, ++ .refclk_setup = rt3350_refclk_setup }; ++struct rt_i2s_data rt3883_i2s_data = { ++ .flags = (RALINK_FLAGS_LEFT_J | RALINK_FLAGS_RIGHT_J), ++ .refclk_setup = rt3883_refclk_setup }; ++struct rt_i2s_data rt3352_i2s_data = { .refclk_setup = rt3552_refclk_setup}; ++struct rt_i2s_data mt7620_i2s_data = { .refclk_setup = mt7620_refclk_setup}; ++struct rt_i2s_data mt7621_i2s_data = { .refclk_setup = mt7621_refclk_setup}; ++struct rt_i2s_data mt7628_i2s_data = { ++ .flags = (RALINK_FLAGS_ENDIAN | RALINK_FLAGS_24BIT | ++ RALINK_FLAGS_LEFT_J), ++ .refclk_setup = mt7628_refclk_setup}; ++ ++static const struct of_device_id ralink_i2s_match_table[] = { ++ { .compatible = "ralink,rt3050-i2s", ++ .data = (void *)&rt3050_i2s_data }, ++ { .compatible = "ralink,rt3350-i2s", ++ .data = (void *)&rt3350_i2s_data }, ++ { .compatible = "ralink,rt3883-i2s", ++ .data = (void *)&rt3883_i2s_data }, ++ { .compatible = "ralink,rt3352-i2s", ++ .data = (void *)&rt3352_i2s_data }, ++ { .compatible = "mediatek,mt7620-i2s", ++ .data = (void *)&mt7620_i2s_data }, ++ { .compatible = "mediatek,mt7621-i2s", ++ .data = (void *)&mt7621_i2s_data }, ++ { .compatible = "mediatek,mt7628-i2s", ++ .data = (void *)&mt7628_i2s_data }, ++}; ++MODULE_DEVICE_TABLE(of, ralink_i2s_match_table); ++ ++static int ralink_i2s_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *match; ++ struct device_node *np = pdev->dev.of_node; ++ struct ralink_i2s *i2s; ++ struct resource *res; ++ int irq, ret; ++ u32 dma_req; ++ struct rt_i2s_data *data; ++ ++ i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); ++ if (!i2s) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, i2s); ++ i2s->dev = &pdev->dev; ++ ++ match = of_match_device(ralink_i2s_match_table, &pdev->dev); ++ if (!match) ++ return -EINVAL; ++ data = (struct rt_i2s_data *)match->data; ++ i2s->flags = data->flags; ++ /* setup out 12Mhz refclk to codec as mclk */ ++ if (data->refclk_setup) ++ data->refclk_setup(); ++ ++ if (of_property_read_u32(np, "txdma-req", &dma_req)) { ++ dev_err(&pdev->dev, "no txdma-req define\n"); ++ return -EINVAL; ++ } ++ i2s->txdma_req = (u16)dma_req; ++ if (!(i2s->flags & RALINK_FLAGS_TXONLY)) { ++ if (of_property_read_u32(np, "rxdma-req", &dma_req)) { ++ dev_err(&pdev->dev, "no rxdma-req define\n"); ++ return -EINVAL; ++ } ++ i2s->rxdma_req = (u16)dma_req; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ i2s->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(i2s->regs)) ++ return PTR_ERR(i2s->regs); ++ ++ i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs, ++ &ralink_i2s_regmap_config); ++ if (IS_ERR(i2s->regmap)) { ++ dev_err(&pdev->dev, "regmap init failed\n"); ++ return PTR_ERR(i2s->regmap); ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "failed to get irq\n"); ++ return -EINVAL; ++ } ++ ++#if (RALINK_I2S_INT_EN) ++ ret = devm_request_irq(&pdev->dev, irq, ralink_i2s_irq, ++ 0, dev_name(&pdev->dev), i2s); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to request irq\n"); ++ return ret; ++ } ++#endif ++ ++ i2s->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(i2s->clk)) { ++ dev_err(&pdev->dev, "no clock defined\n"); ++ return PTR_ERR(i2s->clk); ++ } ++ ++ ret = clk_prepare_enable(i2s->clk); ++ if (ret) ++ return ret; ++ ++ ralink_i2s_init_dma_data(i2s, res); ++ ++ device_reset(&pdev->dev); ++ ++ ret = ralink_i2s_debugfs_create(i2s); ++ if (ret) { ++ dev_err(&pdev->dev, "create debugfs failed\n"); ++ goto err_clk_disable; ++ } ++ ++ /* enable 24bits support */ ++ if (i2s->flags & RALINK_FLAGS_24BIT) { ++ ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S24_LE; ++ ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S24_LE; ++ } ++ ++ /* enable big endian support */ ++ if (i2s->flags & RALINK_FLAGS_ENDIAN) { ++ ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S16_BE; ++ ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S16_BE; ++ ralink_pcm_hardware.formats |= SNDRV_PCM_FMTBIT_S16_BE; ++ if (i2s->flags & RALINK_FLAGS_24BIT) { ++ ralink_i2s_dai.capture.formats |= ++ SNDRV_PCM_FMTBIT_S24_BE; ++ ralink_i2s_dai.playback.formats |= ++ SNDRV_PCM_FMTBIT_S24_BE; ++ ralink_pcm_hardware.formats |= ++ SNDRV_PCM_FMTBIT_S24_BE; ++ } ++ } ++ ++ /* disable capture support */ ++ if (i2s->flags & RALINK_FLAGS_TXONLY) ++ memset(&ralink_i2s_dai.capture, sizeof(ralink_i2s_dai.capture), ++ 0); ++ ++ ret = devm_snd_soc_register_component(&pdev->dev, &ralink_i2s_component, ++ &ralink_i2s_dai, 1); ++ if (ret) ++ goto err_debugfs; ++ ++ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, ++ &ralink_dmaengine_pcm_config, ++ SND_DMAENGINE_PCM_FLAG_COMPAT); ++ if (ret) ++ goto err_debugfs; ++ ++ dev_info(i2s->dev, "mclk %luMHz\n", clk_get_rate(i2s->clk) / 1000000); ++ ++ return 0; ++ ++err_debugfs: ++ ralink_i2s_debugfs_remove(i2s); ++ ++err_clk_disable: ++ clk_disable_unprepare(i2s->clk); ++ ++ return ret; ++} ++ ++static int ralink_i2s_remove(struct platform_device *pdev) ++{ ++ struct ralink_i2s *i2s = platform_get_drvdata(pdev); ++ ++ ralink_i2s_debugfs_remove(i2s); ++ clk_disable_unprepare(i2s->clk); ++ ++ return 0; ++} ++ ++static struct platform_driver ralink_i2s_driver = { ++ .probe = ralink_i2s_probe, ++ .remove = ralink_i2s_remove, ++ .driver = { ++ .name = DRV_NAME, ++ .of_match_table = ralink_i2s_match_table, ++ }, ++}; ++module_platform_driver(ralink_i2s_driver); ++ ++MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>"); ++MODULE_DESCRIPTION("Ralink/MediaTek I2S driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:" DRV_NAME); diff --git a/target/linux/ramips/patches-5.10/0051-serial-add-ugly-custom-baud-rate-hack.patch b/target/linux/ramips/patches-5.10/0051-serial-add-ugly-custom-baud-rate-hack.patch new file mode 100644 index 0000000000..4822226610 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0051-serial-add-ugly-custom-baud-rate-hack.patch @@ -0,0 +1,22 @@ +From a7eb46e0ea4a11e4dfb56ab129bf816d1059a6c5 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Mon, 7 Dec 2015 17:31:08 +0100 +Subject: [PATCH 51/53] serial: add ugly custom baud rate hack + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/tty/serial/serial_core.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/tty/serial/serial_core.c ++++ b/drivers/tty/serial/serial_core.c +@@ -416,6 +416,9 @@ uart_get_baud_rate(struct uart_port *por + break; + } + ++ if (tty_termios_baud_rate(termios) == 2500000) ++ return 250000; ++ + for (try = 0; try < 2; try++) { + baud = tty_termios_baud_rate(termios); + diff --git a/target/linux/ramips/patches-5.10/0052-pwm-add-mediatek-support.patch b/target/linux/ramips/patches-5.10/0052-pwm-add-mediatek-support.patch new file mode 100644 index 0000000000..d2c5724c06 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0052-pwm-add-mediatek-support.patch @@ -0,0 +1,217 @@ +From fc8f96309c21c1bc3276427309cd7d361347d66e Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Mon, 7 Dec 2015 17:16:50 +0100 +Subject: [PATCH 52/53] pwm: add mediatek support + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/pwm/Kconfig | 9 +++ + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-mediatek.c | 173 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 183 insertions(+) + create mode 100644 drivers/pwm/pwm-mediatek.c + +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -316,6 +316,15 @@ config PWM_MEDIATEK + To compile this driver as a module, choose M here: the module + will be called pwm-mediatek. + ++config PWM_MEDIATEK_RAMIPS ++ tristate "Mediatek PWM support" ++ depends on RALINK && OF ++ help ++ Generic PWM framework driver for Mediatek ARM SoC. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-mxs. ++ + config PWM_MXS + tristate "Freescale MXS PWM support" + depends on ARCH_MXS && OF +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -29,6 +29,7 @@ obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-p + obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o + obj-$(CONFIG_PWM_MESON) += pwm-meson.o + obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o ++obj-$(CONFIG_PWM_MEDIATEK_RAMIPS) += pwm-mediatek-ramips.o + obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o + obj-$(CONFIG_PWM_MXS) += pwm-mxs.o + obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o +--- /dev/null ++++ b/drivers/pwm/pwm-mediatek-ramips.c +@@ -0,0 +1,173 @@ ++/* ++ * Mediatek Pulse Width Modulator driver ++ * ++ * Copyright (C) 2015 John Crispin <blogic@openwrt.org> ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/ioport.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pwm.h> ++#include <linux/slab.h> ++#include <linux/types.h> ++ ++#define NUM_PWM 4 ++ ++/* PWM registers and bits definitions */ ++#define PWMCON 0x00 ++#define PWMHDUR 0x04 ++#define PWMLDUR 0x08 ++#define PWMGDUR 0x0c ++#define PWMWAVENUM 0x28 ++#define PWMDWIDTH 0x2c ++#define PWMTHRES 0x30 ++ ++/** ++ * struct mtk_pwm_chip - struct representing pwm chip ++ * ++ * @mmio_base: base address of pwm chip ++ * @chip: linux pwm chip representation ++ */ ++struct mtk_pwm_chip { ++ void __iomem *mmio_base; ++ struct pwm_chip chip; ++}; ++ ++static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip) ++{ ++ return container_of(chip, struct mtk_pwm_chip, chip); ++} ++ ++static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num, ++ unsigned long offset) ++{ ++ return ioread32(chip->mmio_base + 0x10 + (num * 0x40) + offset); ++} ++ ++static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip, ++ unsigned int num, unsigned long offset, ++ unsigned long val) ++{ ++ iowrite32(val, chip->mmio_base + 0x10 + (num * 0x40) + offset); ++} ++ ++static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ++ int duty_ns, int period_ns) ++{ ++ struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); ++ u32 resolution = 100 / 4; ++ u32 clkdiv = 0; ++ ++ while (period_ns / resolution > 8191) { ++ clkdiv++; ++ resolution *= 2; ++ } ++ ++ if (clkdiv > 7) ++ return -1; ++ ++ mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | clkdiv); ++ mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution); ++ mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution); ++ return 0; ++} ++ ++static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); ++ u32 val; ++ ++ val = ioread32(pc->mmio_base); ++ val |= BIT(pwm->hwpwm); ++ iowrite32(val, pc->mmio_base); ++ ++ return 0; ++} ++ ++static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); ++ u32 val; ++ ++ val = ioread32(pc->mmio_base); ++ val &= ~BIT(pwm->hwpwm); ++ iowrite32(val, pc->mmio_base); ++} ++ ++static const struct pwm_ops mtk_pwm_ops = { ++ .config = mtk_pwm_config, ++ .enable = mtk_pwm_enable, ++ .disable = mtk_pwm_disable, ++ .owner = THIS_MODULE, ++}; ++ ++static int mtk_pwm_probe(struct platform_device *pdev) ++{ ++ struct mtk_pwm_chip *pc; ++ struct resource *r; ++ int ret; ++ ++ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); ++ if (!pc) ++ return -ENOMEM; ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ pc->mmio_base = devm_ioremap_resource(&pdev->dev, r); ++ if (IS_ERR(pc->mmio_base)) ++ return PTR_ERR(pc->mmio_base); ++ ++ platform_set_drvdata(pdev, pc); ++ ++ pc->chip.dev = &pdev->dev; ++ pc->chip.ops = &mtk_pwm_ops; ++ pc->chip.base = -1; ++ pc->chip.npwm = NUM_PWM; ++ ++ ret = pwmchip_add(&pc->chip); ++ if (ret < 0) ++ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int mtk_pwm_remove(struct platform_device *pdev) ++{ ++ struct mtk_pwm_chip *pc = platform_get_drvdata(pdev); ++ int i; ++ ++ for (i = 0; i < NUM_PWM; i++) ++ pwm_disable(&pc->chip.pwms[i]); ++ ++ return pwmchip_remove(&pc->chip); ++} ++ ++static const struct of_device_id mtk_pwm_of_match[] = { ++ { .compatible = "mediatek,mt7628-pwm" }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(of, mtk_pwm_of_match); ++ ++static struct platform_driver mtk_pwm_driver = { ++ .driver = { ++ .name = "mtk-pwm", ++ .owner = THIS_MODULE, ++ .of_match_table = mtk_pwm_of_match, ++ }, ++ .probe = mtk_pwm_probe, ++ .remove = mtk_pwm_remove, ++}; ++ ++module_platform_driver(mtk_pwm_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); ++MODULE_ALIAS("platform:mtk-pwm"); diff --git a/target/linux/ramips/patches-5.10/0069-awake-rt305x-dwc2-controller.patch b/target/linux/ramips/patches-5.10/0069-awake-rt305x-dwc2-controller.patch new file mode 100644 index 0000000000..7110a5b808 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0069-awake-rt305x-dwc2-controller.patch @@ -0,0 +1,15 @@ +--- a/drivers/usb/dwc2/platform.c ++++ b/drivers/usb/dwc2/platform.c +@@ -431,6 +431,12 @@ static int dwc2_driver_probe(struct plat + if (retval) + return retval; + ++ /* Enable USB port before any regs access */ ++ if (readl(hsotg->regs + PCGCTL) & 0x0f) { ++ writel(0x00, hsotg->regs + PCGCTL); ++ /* TODO: mdelay(25) here? vendor driver don't use it */ ++ } ++ + hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg); + + retval = dwc2_get_dr_mode(hsotg); diff --git a/target/linux/ramips/patches-5.10/0070-weak_reordering.patch b/target/linux/ramips/patches-5.10/0070-weak_reordering.patch new file mode 100644 index 0000000000..fe3fdec289 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0070-weak_reordering.patch @@ -0,0 +1,10 @@ +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -57,6 +57,7 @@ choice + select COMMON_CLK + select CLKSRC_MIPS_GIC + select HAVE_PCI if PCI_MT7621 ++ select WEAK_REORDERING_BEYOND_LLSC + endchoice + + choice diff --git a/target/linux/ramips/patches-5.10/0098-disable_cm.patch b/target/linux/ramips/patches-5.10/0098-disable_cm.patch new file mode 100644 index 0000000000..9b280aef99 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0098-disable_cm.patch @@ -0,0 +1,19 @@ +--- a/arch/mips/kernel/mips-cm.c ++++ b/arch/mips/kernel/mips-cm.c +@@ -233,6 +233,7 @@ int mips_cm_probe(void) + + /* disable CM regions */ + write_gcr_reg0_base(CM_GCR_REGn_BASE_BASEADDR); ++ /* + write_gcr_reg0_mask(CM_GCR_REGn_MASK_ADDRMASK); + write_gcr_reg1_base(CM_GCR_REGn_BASE_BASEADDR); + write_gcr_reg1_mask(CM_GCR_REGn_MASK_ADDRMASK); +@@ -240,7 +241,7 @@ int mips_cm_probe(void) + write_gcr_reg2_mask(CM_GCR_REGn_MASK_ADDRMASK); + write_gcr_reg3_base(CM_GCR_REGn_BASE_BASEADDR); + write_gcr_reg3_mask(CM_GCR_REGn_MASK_ADDRMASK); +- ++*/ + /* probe for an L2-only sync region */ + mips_cm_probe_l2sync(); + diff --git a/target/linux/ramips/patches-5.10/0100-staging-mt7621-pci-simplify-mt7621_pcie_init_virtual.patch b/target/linux/ramips/patches-5.10/0100-staging-mt7621-pci-simplify-mt7621_pcie_init_virtual.patch new file mode 100644 index 0000000000..b9bcd63488 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0100-staging-mt7621-pci-simplify-mt7621_pcie_init_virtual.patch @@ -0,0 +1,133 @@ +From b327cd58c3fec1c6382128e929eab9bc0d68e912 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Sun, 8 Mar 2020 10:19:27 +0100 +Subject: [PATCH] staging: mt7621-pci: simplify + 'mt7621_pcie_init_virtual_bridges' function + +Function 'mt7621_pcie_init_virtual_bridges' is a bit mess and can be +refactorized properly in a cleaner way. Introduce new 'pcie_rmw' inline +function helper to do clear and set the correct bits this function needs +to work. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200308091928.17177-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 85 +++++++++++++-------------------- + 1 file changed, 33 insertions(+), 52 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -57,13 +57,13 @@ + #define RALINK_PCI_IOBASE 0x002C + + /* PCICFG virtual bridges */ +-#define MT7621_BR0_MASK GENMASK(19, 16) +-#define MT7621_BR1_MASK GENMASK(23, 20) +-#define MT7621_BR2_MASK GENMASK(27, 24) +-#define MT7621_BR_ALL_MASK GENMASK(27, 16) +-#define MT7621_BR0_SHIFT 16 +-#define MT7621_BR1_SHIFT 20 +-#define MT7621_BR2_SHIFT 24 ++#define PCIE_P2P_MAX 3 ++#define PCIE_P2P_BR_DEVNUM_SHIFT(p) (16 + (p) * 4) ++#define PCIE_P2P_BR_DEVNUM0_SHIFT PCIE_P2P_BR_DEVNUM_SHIFT(0) ++#define PCIE_P2P_BR_DEVNUM1_SHIFT PCIE_P2P_BR_DEVNUM_SHIFT(1) ++#define PCIE_P2P_BR_DEVNUM2_SHIFT PCIE_P2P_BR_DEVNUM_SHIFT(2) ++#define PCIE_P2P_BR_DEVNUM_MASK 0xf ++#define PCIE_P2P_BR_DEVNUM_MASK_FULL (0xfff << PCIE_P2P_BR_DEVNUM0_SHIFT) + + /* PCIe RC control registers */ + #define MT7621_PCIE_OFFSET 0x2000 +@@ -154,6 +154,15 @@ static inline void pcie_write(struct mt7 + writel(val, pcie->base + reg); + } + ++static inline void pcie_rmw(struct mt7621_pcie *pcie, u32 reg, u32 clr, u32 set) ++{ ++ u32 val = readl(pcie->base + reg); ++ ++ val &= ~clr; ++ val |= set; ++ writel(val, pcie->base + reg); ++} ++ + static inline u32 pcie_port_read(struct mt7621_pcie_port *port, u32 reg) + { + return readl(port->base + reg); +@@ -554,7 +563,9 @@ static void mt7621_pcie_enable_ports(str + static int mt7621_pcie_init_virtual_bridges(struct mt7621_pcie *pcie) + { + u32 pcie_link_status = 0; +- u32 val = 0; ++ u32 n; ++ int i; ++ u32 p2p_br_devnum[PCIE_P2P_MAX]; + struct mt7621_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) { +@@ -567,50 +578,20 @@ static int mt7621_pcie_init_virtual_brid + if (pcie_link_status == 0) + return -1; + +- /* +- * pcie(2/1/0) link status pcie2_num pcie1_num pcie0_num +- * 3'b000 x x x +- * 3'b001 x x 0 +- * 3'b010 x 0 x +- * 3'b011 x 1 0 +- * 3'b100 0 x x +- * 3'b101 1 x 0 +- * 3'b110 1 0 x +- * 3'b111 2 1 0 +- */ +- switch (pcie_link_status) { +- case 2: +- val = pcie_read(pcie, RALINK_PCI_PCICFG_ADDR); +- val &= ~(MT7621_BR0_MASK | MT7621_BR1_MASK); +- val |= 0x1 << MT7621_BR0_SHIFT; +- val |= 0x0 << MT7621_BR1_SHIFT; +- pcie_write(pcie, val, RALINK_PCI_PCICFG_ADDR); +- break; +- case 4: +- val = pcie_read(pcie, RALINK_PCI_PCICFG_ADDR); +- val &= ~MT7621_BR_ALL_MASK; +- val |= 0x1 << MT7621_BR0_SHIFT; +- val |= 0x2 << MT7621_BR1_SHIFT; +- val |= 0x0 << MT7621_BR2_SHIFT; +- pcie_write(pcie, val, RALINK_PCI_PCICFG_ADDR); +- break; +- case 5: +- val = pcie_read(pcie, RALINK_PCI_PCICFG_ADDR); +- val &= ~MT7621_BR_ALL_MASK; +- val |= 0x0 << MT7621_BR0_SHIFT; +- val |= 0x2 << MT7621_BR1_SHIFT; +- val |= 0x1 << MT7621_BR2_SHIFT; +- pcie_write(pcie, val, RALINK_PCI_PCICFG_ADDR); +- break; +- case 6: +- val = pcie_read(pcie, RALINK_PCI_PCICFG_ADDR); +- val &= ~MT7621_BR_ALL_MASK; +- val |= 0x2 << MT7621_BR0_SHIFT; +- val |= 0x0 << MT7621_BR1_SHIFT; +- val |= 0x1 << MT7621_BR2_SHIFT; +- pcie_write(pcie, val, RALINK_PCI_PCICFG_ADDR); +- break; +- } ++ n = 0; ++ for (i = 0; i < PCIE_P2P_MAX; i++) ++ if (pcie_link_status & BIT(i)) ++ p2p_br_devnum[i] = n++; ++ ++ for (i = 0; i < PCIE_P2P_MAX; i++) ++ if ((pcie_link_status & BIT(i)) == 0) ++ p2p_br_devnum[i] = n++; ++ ++ pcie_rmw(pcie, RALINK_PCI_CONFIG_ADDR, ++ PCIE_P2P_BR_DEVNUM_MASK_FULL, ++ (p2p_br_devnum[0] << PCIE_P2P_BR_DEVNUM0_SHIFT) | ++ (p2p_br_devnum[1] << PCIE_P2P_BR_DEVNUM1_SHIFT) | ++ (p2p_br_devnum[2] << PCIE_P2P_BR_DEVNUM2_SHIFT)); + + return 0; + } diff --git a/target/linux/ramips/patches-5.10/0101-staging-mt7621-pci-enable-clock-bit-for-each-port.patch b/target/linux/ramips/patches-5.10/0101-staging-mt7621-pci-enable-clock-bit-for-each-port.patch new file mode 100644 index 0000000000..3939280a16 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0101-staging-mt7621-pci-enable-clock-bit-for-each-port.patch @@ -0,0 +1,74 @@ +From 550fabd71d7fcdfe099bbf41e00e28719737161e Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Tue, 10 Mar 2020 12:34:59 +0100 +Subject: [PATCH] staging: mt7621-pci: enable clock bit for each port + +The clock related code concerns me from the very beginning because +there are some set ups got from legacy driver that are not documented +anywhere. According to the programming guide 0x7c is 'CPE_ROSC_SEL1' +register and 0x80 is 'CPU_CPE_CN'. I do think this set up is not needed +at all and the proper thing to do is just enable the clock bit for each +pcie port. Hence remove useless code and do the right thing which is +setting up the clock bit for each port enabled. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200310113459.30539-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 17 ++++++----------- + 1 file changed, 6 insertions(+), 11 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -45,8 +45,6 @@ + + /* rt_sysc_membase relative registers */ + #define RALINK_CLKCFG1 0x30 +-#define RALINK_PCIE_CLK_GEN 0x7c +-#define RALINK_PCIE_CLK_GEN1 0x80 + + /* Host-PCI bridge registers */ + #define RALINK_PCI_PCICFG_ADDR 0x0000 +@@ -85,10 +83,6 @@ + #define PCIE_PORT_CLK_EN(x) BIT(24 + (x)) + #define PCIE_PORT_LINKUP BIT(0) + +-#define PCIE_CLK_GEN_EN BIT(31) +-#define PCIE_CLK_GEN_DIS 0 +-#define PCIE_CLK_GEN1_DIS GENMASK(30, 24) +-#define PCIE_CLK_GEN1_EN (BIT(27) | BIT(25)) + #define MEMORY_BASE 0x0 + #define PERST_MODE_MASK GENMASK(11, 10) + #define PERST_MODE_GPIO BIT(10) +@@ -233,6 +227,11 @@ static inline bool mt7621_pcie_port_is_l + return (pcie_port_read(port, RALINK_PCI_STATUS) & PCIE_PORT_LINKUP) != 0; + } + ++static inline void mt7621_pcie_port_clk_enable(struct mt7621_pcie_port *port) ++{ ++ rt_sysc_m32(0, PCIE_PORT_CLK_EN(port->slot), RALINK_CLKCFG1); ++} ++ + static inline void mt7621_pcie_port_clk_disable(struct mt7621_pcie_port *port) + { + rt_sysc_m32(PCIE_PORT_CLK_EN(port->slot), 0, RALINK_CLKCFG1); +@@ -501,11 +500,6 @@ static void mt7621_pcie_init_ports(struc + } + } + +- rt_sysc_m32(0x30, 2 << 4, SYSC_REG_SYSTEM_CONFIG1); +- rt_sysc_m32(PCIE_CLK_GEN_EN, PCIE_CLK_GEN_DIS, RALINK_PCIE_CLK_GEN); +- rt_sysc_m32(PCIE_CLK_GEN1_DIS, PCIE_CLK_GEN1_EN, RALINK_PCIE_CLK_GEN1); +- rt_sysc_m32(PCIE_CLK_GEN_DIS, PCIE_CLK_GEN_EN, RALINK_PCIE_CLK_GEN); +- msleep(50); + reset_control_deassert(pcie->rst); + } + +@@ -542,6 +536,7 @@ static void mt7621_pcie_enable_ports(str + + list_for_each_entry(port, &pcie->ports, list) { + if (port->enabled) { ++ mt7621_pcie_port_clk_enable(port); + mt7621_pcie_enable_port(port); + dev_info(dev, "PCIE%d enabled\n", num_slots_enabled); + num_slots_enabled++; diff --git a/target/linux/ramips/patches-5.10/0102-staging-mt7621-pci-use-gpios-for-properly-reset.patch b/target/linux/ramips/patches-5.10/0102-staging-mt7621-pci-use-gpios-for-properly-reset.patch new file mode 100644 index 0000000000..1b7828f1e6 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0102-staging-mt7621-pci-use-gpios-for-properly-reset.patch @@ -0,0 +1,222 @@ +From 227a8bf421ff8b085444e51e471ef06a87228cfd Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Fri, 13 Mar 2020 21:09:08 +0100 +Subject: [PATCH] staging: mt7621-pci: use gpios for properly reset + +Original driver code was using three gpio's for reset +asserts and deasserts the pcis. Instead of using that +a general reset control with a perst gpio was introduced +and it seems it is partially working but sometimes there +are some unexpected hangs on boot. This commit make use of +the three original gpios using 'reset-gpios' property of +the device tree and removes the reset line and perst gpio. +According to the mediatek aplication note v0.1 there are +three gpios used for pcie ports reset control: gpio#19, +gpio#8 and gpio#7 for slots 0, 1 and 2 respectively. +This schema can be used separately for mt7621A but in some +boards due to pin share issue, if the PCM and I2S function +are enable at the same time, there are no enough GPIO to +control per-port PCIe reset. In those cases gpio#19 is enought +for reset the three ports together. Because of this we just +try to get the three gpios but if some of them fail we are not +failing in boot process, just prints a kernel notice and take +after into account if the descriptor is or not valid in order +to use it. All of them are set as GPIO output low configuration. +The gpio descriptor's API takes device tree property into account +and invert value if the pin is configured as active low. +So we also have to properly request pins from device tree +and set values correct in assert and deassert functions. +After this changes the order to make all assert and +deassert in the 'probe' process makes more sense: +* Parse device tree. +* make assert of the RC's and EP's before doing anything else. +* make deassert of the RC's before initializing the phy. +* Init the phy. +* make deassert of the EP's before initialize pci ports. +* Normal PCI initialization. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200313200913.24321-2-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 84 ++++++++++++++++++++------------- + 1 file changed, 51 insertions(+), 33 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -95,6 +95,7 @@ + * @pcie: pointer to PCIe host info + * @phy: pointer to PHY control block + * @pcie_rst: pointer to port reset control ++ * @gpio_rst: gpio reset + * @slot: port slot + * @enabled: indicates if port is enabled + */ +@@ -104,6 +105,7 @@ struct mt7621_pcie_port { + struct mt7621_pcie *pcie; + struct phy *phy; + struct reset_control *pcie_rst; ++ struct gpio_desc *gpio_rst; + u32 slot; + bool enabled; + }; +@@ -117,8 +119,6 @@ struct mt7621_pcie_port { + * @offset: IO / Memory offset + * @dev: Pointer to PCIe device + * @ports: pointer to PCIe port information +- * @perst: gpio reset +- * @rst: pointer to pcie reset + * @resets_inverted: depends on chip revision + * reset lines are inverted. + */ +@@ -133,8 +133,6 @@ struct mt7621_pcie { + resource_size_t io; + } offset; + struct list_head ports; +- struct gpio_desc *perst; +- struct reset_control *rst; + bool resets_inverted; + }; + +@@ -210,16 +208,16 @@ static void write_config(struct mt7621_p + pcie_write(pcie, val, RALINK_PCI_CONFIG_DATA); + } + +-static inline void mt7621_perst_gpio_pcie_assert(struct mt7621_pcie *pcie) ++static inline void mt7621_rst_gpio_pcie_assert(struct mt7621_pcie_port *port) + { +- gpiod_set_value(pcie->perst, 0); +- mdelay(PERST_DELAY_US); ++ if (port->gpio_rst) ++ gpiod_set_value(port->gpio_rst, 1); + } + +-static inline void mt7621_perst_gpio_pcie_deassert(struct mt7621_pcie *pcie) ++static inline void mt7621_rst_gpio_pcie_deassert(struct mt7621_pcie_port *port) + { +- gpiod_set_value(pcie->perst, 1); +- mdelay(PERST_DELAY_US); ++ if (port->gpio_rst) ++ gpiod_set_value(port->gpio_rst, 0); + } + + static inline bool mt7621_pcie_port_is_linkup(struct mt7621_pcie_port *port) +@@ -367,6 +365,13 @@ static int mt7621_pcie_parse_port(struct + if (IS_ERR(port->phy)) + return PTR_ERR(port->phy); + ++ port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot, ++ GPIOD_OUT_LOW); ++ if (IS_ERR(port->gpio_rst)) { ++ dev_err(dev, "Failed to get GPIO for PCIe%d\n", slot); ++ return PTR_ERR(port->gpio_rst); ++ } ++ + port->slot = slot; + port->pcie = pcie; + +@@ -383,12 +388,6 @@ static int mt7621_pcie_parse_dt(struct m + struct resource regs; + int err; + +- pcie->perst = devm_gpiod_get(dev, "perst", GPIOD_OUT_HIGH); +- if (IS_ERR(pcie->perst)) { +- dev_err(dev, "failed to get gpio perst\n"); +- return PTR_ERR(pcie->perst); +- } +- + err = of_address_to_resource(node, 0, ®s); + if (err) { + dev_err(dev, "missing \"reg\" property\n"); +@@ -399,12 +398,6 @@ static int mt7621_pcie_parse_dt(struct m + if (IS_ERR(pcie->base)) + return PTR_ERR(pcie->base); + +- pcie->rst = devm_reset_control_get_exclusive(dev, "pcie"); +- if (PTR_ERR(pcie->rst) == -EPROBE_DEFER) { +- dev_err(dev, "failed to get pcie reset control\n"); +- return PTR_ERR(pcie->rst); +- } +- + for_each_available_child_of_node(node, child) { + int slot; + +@@ -458,16 +451,49 @@ static int mt7621_pcie_init_port(struct + return 0; + } + ++static void mt7621_pcie_reset_assert(struct mt7621_pcie *pcie) ++{ ++ struct mt7621_pcie_port *port; ++ ++ list_for_each_entry(port, &pcie->ports, list) { ++ /* PCIe RC reset assert */ ++ mt7621_control_assert(port); ++ ++ /* PCIe EP reset assert */ ++ mt7621_rst_gpio_pcie_assert(port); ++ } ++ ++ mdelay(PERST_DELAY_US); ++} ++ ++static void mt7621_pcie_reset_rc_deassert(struct mt7621_pcie *pcie) ++{ ++ struct mt7621_pcie_port *port; ++ ++ list_for_each_entry(port, &pcie->ports, list) ++ mt7621_control_deassert(port); ++} ++ ++static void mt7621_pcie_reset_ep_deassert(struct mt7621_pcie *pcie) ++{ ++ struct mt7621_pcie_port *port; ++ ++ list_for_each_entry(port, &pcie->ports, list) ++ mt7621_rst_gpio_pcie_deassert(port); ++ ++ mdelay(PERST_DELAY_US); ++} ++ + static void mt7621_pcie_init_ports(struct mt7621_pcie *pcie) + { + struct device *dev = pcie->dev; + struct mt7621_pcie_port *port, *tmp; +- u32 val = 0; + int err; + + rt_sysc_m32(PERST_MODE_MASK, PERST_MODE_GPIO, MT7621_GPIO_MODE); + +- mt7621_perst_gpio_pcie_assert(pcie); ++ mt7621_pcie_reset_assert(pcie); ++ mt7621_pcie_reset_rc_deassert(pcie); + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + u32 slot = port->slot; +@@ -476,16 +502,10 @@ static void mt7621_pcie_init_ports(struc + if (err) { + dev_err(dev, "Initiating port %d failed\n", slot); + list_del(&port->list); +- } else { +- val = read_config(pcie, slot, PCIE_FTS_NUM); +- dev_info(dev, "Port %d N_FTS = %x\n", slot, +- (unsigned int)val); + } + } + +- reset_control_assert(pcie->rst); +- +- mt7621_perst_gpio_pcie_deassert(pcie); ++ mt7621_pcie_reset_ep_deassert(pcie); + + list_for_each_entry(port, &pcie->ports, list) { + u32 slot = port->slot; +@@ -499,8 +519,6 @@ static void mt7621_pcie_init_ports(struc + port->enabled = false; + } + } +- +- reset_control_deassert(pcie->rst); + } + + static void mt7621_pcie_enable_port(struct mt7621_pcie_port *port) diff --git a/target/linux/ramips/patches-5.10/0103-staging-mt7621-pci-change-value-for-PERST_DELAY_MS.patch b/target/linux/ramips/patches-5.10/0103-staging-mt7621-pci-change-value-for-PERST_DELAY_MS.patch new file mode 100644 index 0000000000..3d86355b29 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0103-staging-mt7621-pci-change-value-for-PERST_DELAY_MS.patch @@ -0,0 +1,45 @@ +From e462e7d3211479df42357a620fa788a2257556b7 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Fri, 13 Mar 2020 21:09:09 +0100 +Subject: [PATCH] staging: mt7621-pci: change value for 'PERST_DELAY_MS' + +Value of 'PERST_DELAY_MS' is too high and it is ok just +to set up to 100 ms. Update also define name from +'PERST_DELAY_US' into 'PERST_DELAY_MS' + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200313200913.24321-3-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -86,7 +86,7 @@ + #define MEMORY_BASE 0x0 + #define PERST_MODE_MASK GENMASK(11, 10) + #define PERST_MODE_GPIO BIT(10) +-#define PERST_DELAY_US 1000 ++#define PERST_DELAY_MS 100 + + /** + * struct mt7621_pcie_port - PCIe port information +@@ -463,7 +463,7 @@ static void mt7621_pcie_reset_assert(str + mt7621_rst_gpio_pcie_assert(port); + } + +- mdelay(PERST_DELAY_US); ++ mdelay(PERST_DELAY_MS); + } + + static void mt7621_pcie_reset_rc_deassert(struct mt7621_pcie *pcie) +@@ -481,7 +481,7 @@ static void mt7621_pcie_reset_ep_deasser + list_for_each_entry(port, &pcie->ports, list) + mt7621_rst_gpio_pcie_deassert(port); + +- mdelay(PERST_DELAY_US); ++ mdelay(PERST_DELAY_MS); + } + + static void mt7621_pcie_init_ports(struct mt7621_pcie *pcie) diff --git a/target/linux/ramips/patches-5.10/0104-staging-mt7621-pci-release-gpios-after-pci-initializ.patch b/target/linux/ramips/patches-5.10/0104-staging-mt7621-pci-release-gpios-after-pci-initializ.patch new file mode 100644 index 0000000000..f24bf833c9 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0104-staging-mt7621-pci-release-gpios-after-pci-initializ.patch @@ -0,0 +1,76 @@ +From 4d6a758f2cd2122a7d895f913854c13da62ba6df Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Fri, 13 Mar 2020 21:09:12 +0100 +Subject: [PATCH] staging: mt7621-pci: release gpios after pci initialization + +R3G's LEDs fail to initialize because one of them uses GPIO8 +Hence, release the GPIO resources after PCIe initialization +and properly release also in driver error path. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200313200913.24321-6-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 23 ++++++++++++++++++----- + 1 file changed, 18 insertions(+), 5 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -484,6 +484,15 @@ static void mt7621_pcie_reset_ep_deasser + mdelay(PERST_DELAY_MS); + } + ++static void mt7621_pcie_release_gpios(struct mt7621_pcie *pcie) ++{ ++ struct mt7621_pcie_port *port; ++ ++ list_for_each_entry(port, &pcie->ports, list) ++ if (port->gpio_rst) ++ gpiod_put(port->gpio_rst); ++} ++ + static void mt7621_pcie_init_ports(struct mt7621_pcie *pcie) + { + struct device *dev = pcie->dev; +@@ -683,7 +692,8 @@ static int mt7621_pci_probe(struct platf + err = mt7621_pcie_init_virtual_bridges(pcie); + if (err) { + dev_err(dev, "Nothing is connected in virtual bridges. Exiting..."); +- return 0; ++ err = 0; ++ goto out_release_gpios; + } + + mt7621_pcie_enable_ports(pcie); +@@ -691,7 +701,7 @@ static int mt7621_pci_probe(struct platf + err = mt7621_pci_parse_request_of_pci_ranges(pcie); + if (err) { + dev_err(dev, "Error requesting pci resources from ranges"); +- return err; ++ goto out_release_gpios; + } + + setup_cm_memory_region(pcie); +@@ -699,16 +709,19 @@ static int mt7621_pci_probe(struct platf + err = mt7621_pcie_request_resources(pcie, &res); + if (err) { + dev_err(dev, "Error requesting resources\n"); +- return err; ++ goto out_release_gpios; + } + + err = mt7621_pcie_register_host(bridge, &res); + if (err) { + dev_err(dev, "Error registering host\n"); +- return err; ++ goto out_release_gpios; + } + +- return 0; ++out_release_gpios: ++ mt7621_pcie_release_gpios(pcie); ++ ++ return err; + } + + static const struct of_device_id mt7621_pci_ids[] = { diff --git a/target/linux/ramips/patches-5.10/0105-staging-mt7621-pci-delete-no-more-needed-mt7621_rese.patch b/target/linux/ramips/patches-5.10/0105-staging-mt7621-pci-delete-no-more-needed-mt7621_rese.patch new file mode 100644 index 0000000000..fc509e945b --- /dev/null +++ b/target/linux/ramips/patches-5.10/0105-staging-mt7621-pci-delete-no-more-needed-mt7621_rese.patch @@ -0,0 +1,46 @@ +From 4be54c3a495f08c05a8e485566e5b88cd3537f16 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Fri, 13 Mar 2020 21:09:13 +0100 +Subject: [PATCH] staging: mt7621-pci: delete no more needed + 'mt7621_reset_port' + +After review all the resets at the beggining the function +'mt7621_reset_port' is not needed anymore. Hence delete it +and its uses. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200313200913.24321-7-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 13 ------------- + 1 file changed, 13 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -255,13 +255,6 @@ static inline void mt7621_control_deasse + reset_control_assert(port->pcie_rst); + } + +-static void mt7621_reset_port(struct mt7621_pcie_port *port) +-{ +- mt7621_control_assert(port); +- msleep(100); +- mt7621_control_deassert(port); +-} +- + static void setup_cm_memory_region(struct mt7621_pcie *pcie) + { + struct resource *mem_resource = &pcie->mem; +@@ -427,12 +420,6 @@ static int mt7621_pcie_init_port(struct + u32 slot = port->slot; + int err; + +- /* +- * Any MT7621 Ralink pcie controller that doesn't have 0x0101 at +- * the end of the chip_id has inverted PCI resets. +- */ +- mt7621_reset_port(port); +- + err = phy_init(port->phy); + if (err) { + dev_err(dev, "failed to initialize port%d phy\n", slot); diff --git a/target/linux/ramips/patches-5.10/0106-staging-mt7621-pci-phy-add-mt7621_phy_rmw-to-simplif.patch b/target/linux/ramips/patches-5.10/0106-staging-mt7621-pci-phy-add-mt7621_phy_rmw-to-simplif.patch new file mode 100644 index 0000000000..234af32308 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0106-staging-mt7621-pci-phy-add-mt7621_phy_rmw-to-simplif.patch @@ -0,0 +1,234 @@ +From bf0c6782e5b9a6deee4e223655325dc004fae8dd Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Sun, 15 Mar 2020 17:01:54 +0100 +Subject: [PATCH] staging: mt7621-pci-phy: add 'mt7621_phy_rmw' to simplify + code + +In order to simplify driver code and decrease a bit LOC add new +function 'mt7621_phy_rmw' where clear and set bits are passed as +arguments. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200315160154.10292-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c | 158 +++++++++++------------- + 1 file changed, 71 insertions(+), 87 deletions(-) + +--- a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c ++++ b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c +@@ -120,17 +120,25 @@ static inline void phy_write(struct mt76 + regmap_write(phy->regmap, reg, val); + } + ++static inline void mt7621_phy_rmw(struct mt7621_pci_phy *phy, ++ u32 reg, u32 clr, u32 set) ++{ ++ u32 val = phy_read(phy, reg); ++ ++ val &= ~clr; ++ val |= set; ++ phy_write(phy, val, reg); ++} ++ + static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy, + struct mt7621_pci_phy_instance *instance) + { + u32 offset = (instance->index != 1) ? + RG_PE1_PIPE_REG : RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH; +- u32 reg; + +- reg = phy_read(phy, offset); +- reg &= ~(RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC); +- reg |= (RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC); +- phy_write(phy, reg, offset); ++ mt7621_phy_rmw(phy, offset, ++ RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC, ++ RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC); + } + + static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy, +@@ -139,97 +147,77 @@ static void mt7621_set_phy_for_ssc(struc + struct device *dev = phy->dev; + u32 reg = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0); + u32 offset; +- u32 val; + + reg = (reg >> 6) & 0x7; + /* Set PCIe Port PHY to disable SSC */ + /* Debug Xtal Type */ +- val = phy_read(phy, RG_PE1_FRC_H_XTAL_REG); +- val &= ~(RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE); +- val |= RG_PE1_FRC_H_XTAL_TYPE; +- val |= RG_PE1_H_XTAL_TYPE_VAL(0x00); +- phy_write(phy, val, RG_PE1_FRC_H_XTAL_REG); ++ mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG, ++ RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE, ++ RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE_VAL(0x00)); + + /* disable port */ + offset = (instance->index != 1) ? + RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH; +- val = phy_read(phy, offset); +- val &= ~(RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN); +- val |= RG_PE1_FRC_PHY_EN; +- phy_write(phy, val, offset); +- +- /* Set Pre-divider ratio (for host mode) */ +- val = phy_read(phy, RG_PE1_H_PLL_REG); +- val &= ~(RG_PE1_H_PLL_PREDIV); ++ mt7621_phy_rmw(phy, offset, ++ RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); + + if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */ +- val |= RG_PE1_H_PLL_PREDIV_VAL(0x01); +- phy_write(phy, val, RG_PE1_H_PLL_REG); ++ /* Set Pre-divider ratio (for host mode) */ ++ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, ++ RG_PE1_H_PLL_PREDIV, ++ RG_PE1_H_PLL_PREDIV_VAL(0x01)); + dev_info(dev, "Xtal is 40MHz\n"); +- } else { /* 25MHz | 20MHz Xtal */ +- val |= RG_PE1_H_PLL_PREDIV_VAL(0x00); +- phy_write(phy, val, RG_PE1_H_PLL_REG); +- if (reg >= 6) { +- dev_info(dev, "Xtal is 25MHz\n"); +- +- /* Select feedback clock */ +- val = phy_read(phy, RG_PE1_H_PLL_FBKSEL_REG); +- val &= ~(RG_PE1_H_PLL_FBKSEL); +- val |= RG_PE1_H_PLL_FBKSEL_VAL(0x01); +- phy_write(phy, val, RG_PE1_H_PLL_FBKSEL_REG); +- +- /* DDS NCPO PCW (for host mode) */ +- val = phy_read(phy, RG_PE1_H_LCDDS_SSC_PRD_REG); +- val &= ~(RG_PE1_H_LCDDS_SSC_PRD); +- val |= RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18000000); +- phy_write(phy, val, RG_PE1_H_LCDDS_SSC_PRD_REG); +- +- /* DDS SSC dither period control */ +- val = phy_read(phy, RG_PE1_H_LCDDS_SSC_PRD_REG); +- val &= ~(RG_PE1_H_LCDDS_SSC_PRD); +- val |= RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18d); +- phy_write(phy, val, RG_PE1_H_LCDDS_SSC_PRD_REG); +- +- /* DDS SSC dither amplitude control */ +- val = phy_read(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG); +- val &= ~(RG_PE1_H_LCDDS_SSC_DELTA | +- RG_PE1_H_LCDDS_SSC_DELTA1); +- val |= RG_PE1_H_LCDDS_SSC_DELTA_VAL(0x4a); +- val |= RG_PE1_H_LCDDS_SSC_DELTA1_VAL(0x4a); +- phy_write(phy, val, RG_PE1_H_LCDDS_SSC_DELTA_REG); +- } else { +- dev_info(dev, "Xtal is 20MHz\n"); +- } ++ } else if (reg >= 6) { /* 25MHz Xal */ ++ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, ++ RG_PE1_H_PLL_PREDIV, ++ RG_PE1_H_PLL_PREDIV_VAL(0x00)); ++ /* Select feedback clock */ ++ mt7621_phy_rmw(phy, RG_PE1_H_PLL_FBKSEL_REG, ++ RG_PE1_H_PLL_FBKSEL, ++ RG_PE1_H_PLL_FBKSEL_VAL(0x01)); ++ /* DDS NCPO PCW (for host mode) */ ++ mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, ++ RG_PE1_H_LCDDS_SSC_PRD, ++ RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18000000)); ++ /* DDS SSC dither period control */ ++ mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, ++ RG_PE1_H_LCDDS_SSC_PRD, ++ RG_PE1_H_LCDDS_SSC_PRD_VAL(0x18d)); ++ /* DDS SSC dither amplitude control */ ++ mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG, ++ RG_PE1_H_LCDDS_SSC_DELTA | ++ RG_PE1_H_LCDDS_SSC_DELTA1, ++ RG_PE1_H_LCDDS_SSC_DELTA_VAL(0x4a) | ++ RG_PE1_H_LCDDS_SSC_DELTA1_VAL(0x4a)); ++ dev_info(dev, "Xtal is 25MHz\n"); ++ } else { /* 20MHz Xtal */ ++ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, ++ RG_PE1_H_PLL_PREDIV, ++ RG_PE1_H_PLL_PREDIV_VAL(0x00)); ++ ++ dev_info(dev, "Xtal is 20MHz\n"); + } + + /* DDS clock inversion */ +- val = phy_read(phy, RG_PE1_LCDDS_CLK_PH_INV_REG); +- val &= ~(RG_PE1_LCDDS_CLK_PH_INV); +- val |= RG_PE1_LCDDS_CLK_PH_INV; +- phy_write(phy, val, RG_PE1_LCDDS_CLK_PH_INV_REG); ++ mt7621_phy_rmw(phy, RG_PE1_LCDDS_CLK_PH_INV_REG, ++ RG_PE1_LCDDS_CLK_PH_INV, RG_PE1_LCDDS_CLK_PH_INV); + + /* Set PLL bits */ +- val = phy_read(phy, RG_PE1_H_PLL_REG); +- val &= ~(RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR | +- RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN); +- val |= RG_PE1_H_PLL_BC_VAL(0x02); +- val |= RG_PE1_H_PLL_BP_VAL(0x06); +- val |= RG_PE1_H_PLL_IR_VAL(0x02); +- val |= RG_PE1_H_PLL_IC_VAL(0x01); +- val |= RG_PE1_PLL_DIVEN_VAL(0x02); +- phy_write(phy, val, RG_PE1_H_PLL_REG); +- +- val = phy_read(phy, RG_PE1_H_PLL_BR_REG); +- val &= ~(RG_PE1_H_PLL_BR); +- val |= RG_PE1_H_PLL_BR_VAL(0x00); +- phy_write(phy, val, RG_PE1_H_PLL_BR_REG); ++ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, ++ RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR | ++ RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN, ++ RG_PE1_H_PLL_BC_VAL(0x02) | RG_PE1_H_PLL_BP_VAL(0x06) | ++ RG_PE1_H_PLL_IR_VAL(0x02) | RG_PE1_H_PLL_IC_VAL(0x01) | ++ RG_PE1_PLL_DIVEN_VAL(0x02)); ++ ++ mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG, ++ RG_PE1_H_PLL_BR, RG_PE1_H_PLL_BR_VAL(0x00)); + + if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */ + /* set force mode enable of da_pe1_mstckdiv */ +- val = phy_read(phy, RG_PE1_MSTCKDIV_REG); +- val &= ~(RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV); +- val |= (RG_PE1_MSTCKDIV_VAL(0x01) | RG_PE1_FRC_MSTCKDIV); +- phy_write(phy, val, RG_PE1_MSTCKDIV_REG); ++ mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG, ++ RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV, ++ RG_PE1_MSTCKDIV_VAL(0x01) | RG_PE1_FRC_MSTCKDIV); + } + } + +@@ -252,13 +240,11 @@ static int mt7621_pci_phy_power_on(struc + struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent); + u32 offset = (instance->index != 1) ? + RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH; +- u32 val; + + /* Enable PHY and disable force mode */ +- val = phy_read(mphy, offset); +- val &= ~(RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN); +- val |= (RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN); +- phy_write(mphy, val, offset); ++ mt7621_phy_rmw(mphy, offset, ++ RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN, ++ RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN); + + return 0; + } +@@ -269,13 +255,11 @@ static int mt7621_pci_phy_power_off(stru + struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent); + u32 offset = (instance->index != 1) ? + RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH; +- u32 val; + + /* Disable PHY */ +- val = phy_read(mphy, offset); +- val &= ~(RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN); +- val |= RG_PE1_FRC_PHY_EN; +- phy_write(mphy, val, offset); ++ mt7621_phy_rmw(mphy, offset, ++ RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN, ++ RG_PE1_FRC_PHY_EN); + + return 0; + } diff --git a/target/linux/ramips/patches-5.10/0107-staging-mt7621-pci-fix-io-space-and-properly-set-res.patch b/target/linux/ramips/patches-5.10/0107-staging-mt7621-pci-fix-io-space-and-properly-set-res.patch new file mode 100644 index 0000000000..393fd4d5dd --- /dev/null +++ b/target/linux/ramips/patches-5.10/0107-staging-mt7621-pci-fix-io-space-and-properly-set-res.patch @@ -0,0 +1,131 @@ +From 3faf4e1c537de86058fc22a851cd979489b9185e Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Wed, 18 Mar 2020 10:44:45 +0100 +Subject: [PATCH] staging: mt7621-pci: fix io space and properly set resource + limits + +Function 'mt7621_pci_parse_request_of_pci_ranges' is using +'of_pci_range_to_resource' to get both mem and io resources. +Internally this function calls to 'pci_address_to_pio' which +returns -1 if io space address is an address > IO_SPACE_LIMIT +which is 0xFFFF for mips. This mt7621 soc has io space in physical +address 0x1e160000. In order to fix this, overwrite invalid io +0xffffffff with properly values from the device tree and set +mapped address of this resource as io port base memory address +calling 'set_io_port_base' function. There is also need to properly +setup resource limits and io and memory windows with properly +parsed values instead of set them as 'no limit' which it is wrong. +For any reason I don't really know legacy driver sets up mem window +as 0xFFFFFFFF and any other value seems to does not work as expected, +so set up also here with same values. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200318094445.19669-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 43 +++++++++++++++++++-------------- + 1 file changed, 25 insertions(+), 18 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -118,6 +118,7 @@ struct mt7621_pcie_port { + * @busn: bus range + * @offset: IO / Memory offset + * @dev: Pointer to PCIe device ++ * @io_map_base: virtual memory base address for io + * @ports: pointer to PCIe port information + * @resets_inverted: depends on chip revision + * reset lines are inverted. +@@ -132,6 +133,7 @@ struct mt7621_pcie { + resource_size_t mem; + resource_size_t io; + } offset; ++ unsigned long io_map_base; + struct list_head ports; + bool resets_inverted; + }; +@@ -291,22 +293,21 @@ static int mt7621_pci_parse_request_of_p + } + + for_each_of_pci_range(&parser, &range) { +- struct resource *res = NULL; +- + switch (range.flags & IORESOURCE_TYPE_BITS) { + case IORESOURCE_IO: +- ioremap(range.cpu_addr, range.size); +- res = &pcie->io; ++ pcie->io_map_base = ++ (unsigned long)ioremap(range.cpu_addr, ++ range.size); ++ of_pci_range_to_resource(&range, node, &pcie->io); ++ pcie->io.start = range.cpu_addr; ++ pcie->io.end = range.cpu_addr + range.size - 1; + pcie->offset.io = 0x00000000UL; + break; + case IORESOURCE_MEM: +- res = &pcie->mem; ++ of_pci_range_to_resource(&range, node, &pcie->mem); + pcie->offset.mem = 0x00000000UL; + break; + } +- +- if (res) +- of_pci_range_to_resource(&range, node, res); + } + + err = of_pci_parse_bus_range(node, &pcie->busn); +@@ -318,6 +319,8 @@ static int mt7621_pci_parse_request_of_p + pcie->busn.flags = IORESOURCE_BUS; + } + ++ set_io_port_base(pcie->io_map_base); ++ + return 0; + } + +@@ -548,6 +551,10 @@ static void mt7621_pcie_enable_ports(str + u32 slot; + u32 val; + ++ /* Setup MEMWIN and IOWIN */ ++ pcie_write(pcie, 0xffffffff, RALINK_PCI_MEMBASE); ++ pcie_write(pcie, pcie->io.start, RALINK_PCI_IOBASE); ++ + list_for_each_entry(port, &pcie->ports, list) { + if (port->enabled) { + mt7621_pcie_port_clk_enable(port); +@@ -668,11 +675,17 @@ static int mt7621_pci_probe(struct platf + return err; + } + ++ err = mt7621_pci_parse_request_of_pci_ranges(pcie); ++ if (err) { ++ dev_err(dev, "Error requesting pci resources from ranges"); ++ goto out_release_gpios; ++ } ++ + /* set resources limits */ +- iomem_resource.start = 0; +- iomem_resource.end = ~0UL; /* no limit */ +- ioport_resource.start = 0; +- ioport_resource.end = ~0UL; /* no limit */ ++ iomem_resource.start = pcie->mem.start; ++ iomem_resource.end = pcie->mem.end; ++ ioport_resource.start = pcie->io.start; ++ ioport_resource.end = pcie->io.end; + + mt7621_pcie_init_ports(pcie); + +@@ -685,12 +698,6 @@ static int mt7621_pci_probe(struct platf + + mt7621_pcie_enable_ports(pcie); + +- err = mt7621_pci_parse_request_of_pci_ranges(pcie); +- if (err) { +- dev_err(dev, "Error requesting pci resources from ranges"); +- goto out_release_gpios; +- } +- + setup_cm_memory_region(pcie); + + err = mt7621_pcie_request_resources(pcie, &res); diff --git a/target/linux/ramips/patches-5.10/0108-staging-mt7621-pci-fix-register-to-set-up-virtual-br.patch b/target/linux/ramips/patches-5.10/0108-staging-mt7621-pci-fix-register-to-set-up-virtual-br.patch new file mode 100644 index 0000000000..5b4c461b2f --- /dev/null +++ b/target/linux/ramips/patches-5.10/0108-staging-mt7621-pci-fix-register-to-set-up-virtual-br.patch @@ -0,0 +1,28 @@ +From 0a3085ae142d8f5cf905b104bc66db3721a2fa33 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Thu, 19 Mar 2020 10:57:33 +0100 +Subject: [PATCH] staging: mt7621-pci: fix register to set up virtual bridges + +Instead of being using PCI Configuration and Status Register to +set up virtual bridges we are using CONFIG_ADDR Register which is +wrong. Hence, set the correct value. + +Fixes: 9a5e71a68d20 ("staging: mt7621-pci: simplify 'mt7621_pcie_init_virtual_bridges' function") +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200319095733.1557-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -603,7 +603,7 @@ static int mt7621_pcie_init_virtual_brid + if ((pcie_link_status & BIT(i)) == 0) + p2p_br_devnum[i] = n++; + +- pcie_rmw(pcie, RALINK_PCI_CONFIG_ADDR, ++ pcie_rmw(pcie, RALINK_PCI_PCICFG_ADDR, + PCIE_P2P_BR_DEVNUM_MASK_FULL, + (p2p_br_devnum[0] << PCIE_P2P_BR_DEVNUM0_SHIFT) | + (p2p_br_devnum[1] << PCIE_P2P_BR_DEVNUM1_SHIFT) | diff --git a/target/linux/ramips/patches-5.10/0109-staging-mt7621-pci-don-t-return-if-get-gpio-fails.patch b/target/linux/ramips/patches-5.10/0109-staging-mt7621-pci-don-t-return-if-get-gpio-fails.patch new file mode 100644 index 0000000000..55abb8999f --- /dev/null +++ b/target/linux/ramips/patches-5.10/0109-staging-mt7621-pci-don-t-return-if-get-gpio-fails.patch @@ -0,0 +1,34 @@ +From 23a788c23ed10e0d79092fcb693dcf0e357e1f7e Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Thu, 19 Mar 2020 17:14:16 +0100 +Subject: [PATCH] staging: mt7621-pci: don't return if get gpio fails + +In some platforms gpio's are not used for reset but +for other purposes. Because of that when we try to +get them are valid gpio's but are already assigned +to do other function. To avoid those kind of problems +in those platforms just notice the fail in the kernel +but continue doing normal boot. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200319161416.19033-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -363,10 +363,8 @@ static int mt7621_pcie_parse_port(struct + + port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot, + GPIOD_OUT_LOW); +- if (IS_ERR(port->gpio_rst)) { +- dev_err(dev, "Failed to get GPIO for PCIe%d\n", slot); +- return PTR_ERR(port->gpio_rst); +- } ++ if (IS_ERR(port->gpio_rst)) ++ dev_notice(dev, "Failed to get GPIO for PCIe%d\n", slot); + + port->slot = slot; + port->pcie = pcie; diff --git a/target/linux/ramips/patches-5.10/0110-staging-mt7621-pci-phy-avoid-to-create-to-different-.patch b/target/linux/ramips/patches-5.10/0110-staging-mt7621-pci-phy-avoid-to-create-to-different-.patch new file mode 100644 index 0000000000..6d84bfdd5b --- /dev/null +++ b/target/linux/ramips/patches-5.10/0110-staging-mt7621-pci-phy-avoid-to-create-to-different-.patch @@ -0,0 +1,272 @@ +From 91eb47531421f0e8c9bc4594b4a7caa0e59dc83e Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Fri, 20 Mar 2020 12:01:19 +0100 +Subject: [PATCH] staging: mt7621-pci-phy: avoid to create to different phys + for a dual port one + +This soc has two phy's for the pcie one of them using just a different +register for settig it up but sharing all the rest of the config. Until +now we was presenting this schema as three different phy's in the device +tree using the 'phy-cells' node property to discriminate an index and +setting up a complete phy for the dual port index. This sometimes worked +properly but reconfiguring the same registers twice presents sometimes +some unstable pcie links and the ports was not properly being detected. +The problems only appears on hard resets and soft resets was properly +working. Instead of having this schema just set two phy's in the device +ree and use the 'phy-cells' property to say if the port has or not a dual +port. Doing this configuration and set up becomes easier, LOC is decreased +and the behaviour also gets deterministic with properly and stable pcie +links in both hard and soft resets. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200320110123.9907-2-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c | 144 ++++++++++-------------- + 1 file changed, 59 insertions(+), 85 deletions(-) + +--- a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c ++++ b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c +@@ -78,31 +78,21 @@ + #define MAX_PHYS 2 + + /** +- * struct mt7621_pci_phy_instance - Mt7621 Pcie PHY device +- * @phy: pointer to the kernel PHY device +- * @port_base: base register +- * @index: internal ID to identify the Mt7621 PCIe PHY +- */ +-struct mt7621_pci_phy_instance { +- struct phy *phy; +- void __iomem *port_base; +- u32 index; +-}; +- +-/** + * struct mt7621_pci_phy - Mt7621 Pcie PHY core + * @dev: pointer to device + * @regmap: kernel regmap pointer +- * @phys: pointer to Mt7621 PHY device +- * @nphys: number of PHY devices for this core ++ * @phy: pointer to the kernel PHY device ++ * @port_base: base register ++ * @has_dual_port: if the phy has dual ports. + * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst' + * needs to be executed. Depends on chip revision. + */ + struct mt7621_pci_phy { + struct device *dev; + struct regmap *regmap; +- struct mt7621_pci_phy_instance **phys; +- int nphys; ++ struct phy *phy; ++ void __iomem *port_base; ++ bool has_dual_port; + bool bypass_pipe_rst; + }; + +@@ -130,23 +120,23 @@ static inline void mt7621_phy_rmw(struct + phy_write(phy, val, reg); + } + +-static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy, +- struct mt7621_pci_phy_instance *instance) ++static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy) + { +- u32 offset = (instance->index != 1) ? +- RG_PE1_PIPE_REG : RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH; ++ mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_RST); ++ mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_CMD_FRC); + +- mt7621_phy_rmw(phy, offset, +- RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC, +- RG_PE1_PIPE_RST | RG_PE1_PIPE_CMD_FRC); ++ if (phy->has_dual_port) { ++ mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, ++ 0, RG_PE1_PIPE_RST); ++ mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, ++ 0, RG_PE1_PIPE_CMD_FRC); ++ } + } + +-static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy, +- struct mt7621_pci_phy_instance *instance) ++static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) + { + struct device *dev = phy->dev; + u32 reg = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0); +- u32 offset; + + reg = (reg >> 6) & 0x7; + /* Set PCIe Port PHY to disable SSC */ +@@ -156,10 +146,13 @@ static void mt7621_set_phy_for_ssc(struc + RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE_VAL(0x00)); + + /* disable port */ +- offset = (instance->index != 1) ? +- RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH; +- mt7621_phy_rmw(phy, offset, +- RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); ++ mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG, ++ RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); ++ ++ if (phy->has_dual_port) { ++ mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, ++ RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); ++ } + + if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */ + /* Set Pre-divider ratio (for host mode) */ +@@ -223,43 +216,44 @@ static void mt7621_set_phy_for_ssc(struc + + static int mt7621_pci_phy_init(struct phy *phy) + { +- struct mt7621_pci_phy_instance *instance = phy_get_drvdata(phy); +- struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent); ++ struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); + + if (mphy->bypass_pipe_rst) +- mt7621_bypass_pipe_rst(mphy, instance); ++ mt7621_bypass_pipe_rst(mphy); + +- mt7621_set_phy_for_ssc(mphy, instance); ++ mt7621_set_phy_for_ssc(mphy); + + return 0; + } + + static int mt7621_pci_phy_power_on(struct phy *phy) + { +- struct mt7621_pci_phy_instance *instance = phy_get_drvdata(phy); +- struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent); +- u32 offset = (instance->index != 1) ? +- RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH; ++ struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); + + /* Enable PHY and disable force mode */ +- mt7621_phy_rmw(mphy, offset, +- RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN, +- RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN); ++ mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, ++ RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); ++ ++ if (mphy->has_dual_port) { ++ mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, ++ RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); ++ } + + return 0; + } + + static int mt7621_pci_phy_power_off(struct phy *phy) + { +- struct mt7621_pci_phy_instance *instance = phy_get_drvdata(phy); +- struct mt7621_pci_phy *mphy = dev_get_drvdata(phy->dev.parent); +- u32 offset = (instance->index != 1) ? +- RG_PE1_FRC_PHY_REG : RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH; ++ struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); + + /* Disable PHY */ +- mt7621_phy_rmw(mphy, offset, +- RG_PE1_FRC_PHY_EN | RG_PE1_PHY_EN, +- RG_PE1_FRC_PHY_EN); ++ mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, ++ RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); ++ ++ if (mphy->has_dual_port) { ++ mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, ++ RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); ++ } + + return 0; + } +@@ -282,13 +276,15 @@ static struct phy *mt7621_pcie_phy_of_xl + { + struct mt7621_pci_phy *mt7621_phy = dev_get_drvdata(dev); + +- if (args->args_count == 0) +- return mt7621_phy->phys[0]->phy; +- + if (WARN_ON(args->args[0] >= MAX_PHYS)) + return ERR_PTR(-ENODEV); + +- return mt7621_phy->phys[args->args[0]]->phy; ++ mt7621_phy->has_dual_port = args->args[0]; ++ ++ dev_info(dev, "PHY for 0x%08x (dual port = %d)\n", ++ (unsigned int)mt7621_phy->port_base, mt7621_phy->has_dual_port); ++ ++ return mt7621_phy->phy; + } + + static const struct soc_device_attribute mt7621_pci_quirks_match[] = { +@@ -309,19 +305,11 @@ static int mt7621_pci_phy_probe(struct p + struct phy_provider *provider; + struct mt7621_pci_phy *phy; + struct resource *res; +- int port; +- void __iomem *port_base; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + +- phy->nphys = MAX_PHYS; +- phy->phys = devm_kcalloc(dev, phy->nphys, +- sizeof(*phy->phys), GFP_KERNEL); +- if (!phy->phys) +- return -ENOMEM; +- + attr = soc_device_match(mt7621_pci_quirks_match); + if (attr) + phy->bypass_pipe_rst = true; +@@ -335,39 +323,25 @@ static int mt7621_pci_phy_probe(struct p + return -ENXIO; + } + +- port_base = devm_ioremap_resource(dev, res); +- if (IS_ERR(port_base)) { ++ phy->port_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(phy->port_base)) { + dev_err(dev, "failed to remap phy regs\n"); +- return PTR_ERR(port_base); ++ return PTR_ERR(phy->port_base); + } + +- phy->regmap = devm_regmap_init_mmio(phy->dev, port_base, ++ phy->regmap = devm_regmap_init_mmio(phy->dev, phy->port_base, + &mt7621_pci_phy_regmap_config); + if (IS_ERR(phy->regmap)) + return PTR_ERR(phy->regmap); + +- for (port = 0; port < MAX_PHYS; port++) { +- struct mt7621_pci_phy_instance *instance; +- struct phy *pphy; +- +- instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL); +- if (!instance) +- return -ENOMEM; +- +- phy->phys[port] = instance; +- +- pphy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create phy\n"); +- return PTR_ERR(phy); +- } +- +- instance->port_base = port_base; +- instance->phy = pphy; +- instance->index = port; +- phy_set_drvdata(pphy, instance); ++ phy->phy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create phy\n"); ++ return PTR_ERR(phy); + } + ++ phy_set_drvdata(phy->phy, phy); ++ + provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate); + + return PTR_ERR_OR_ZERO(provider); diff --git a/target/linux/ramips/patches-5.10/0111-staging-mt7621-pci-use-only-two-phys-from-device-tre.patch b/target/linux/ramips/patches-5.10/0111-staging-mt7621-pci-use-only-two-phys-from-device-tre.patch new file mode 100644 index 0000000000..61aa80eb76 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0111-staging-mt7621-pci-use-only-two-phys-from-device-tre.patch @@ -0,0 +1,42 @@ +From c752b54bda4d772426c5eeb56978d2e41bd525b4 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Fri, 20 Mar 2020 12:01:21 +0100 +Subject: [PATCH] staging: mt7621-pci: use only two phys from device tree + +In order to align work with the mt7621-pci-phy part of +the driver and device tree which is now using only two +real phys one of them dual ported properly parse the +device tree and don't call phy initialization for the +slot 1 because is being taking into account when the +phy for the slot 0 is instantiated. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200320110123.9907-4-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -358,7 +358,7 @@ static int mt7621_pcie_parse_port(struct + + snprintf(name, sizeof(name), "pcie-phy%d", slot); + port->phy = devm_phy_get(dev, name); +- if (IS_ERR(port->phy)) ++ if (IS_ERR(port->phy) && slot != 1) + return PTR_ERR(port->phy); + + port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot, +@@ -495,6 +495,11 @@ static void mt7621_pcie_init_ports(struc + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + u32 slot = port->slot; + ++ if (slot == 1) { ++ port->enabled = true; ++ continue; ++ } ++ + err = mt7621_pcie_init_port(port); + if (err) { + dev_err(dev, "Initiating port %d failed\n", slot); diff --git a/target/linux/ramips/patches-5.10/0112-staging-mt7621-pci-change-variable-to-print-for-slot.patch b/target/linux/ramips/patches-5.10/0112-staging-mt7621-pci-change-variable-to-print-for-slot.patch new file mode 100644 index 0000000000..383c896a3e --- /dev/null +++ b/target/linux/ramips/patches-5.10/0112-staging-mt7621-pci-change-variable-to-print-for-slot.patch @@ -0,0 +1,26 @@ +From b59343b7de448c30e5b098484a7c7c5cb300df2f Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Fri, 20 Mar 2020 12:01:22 +0100 +Subject: [PATCH] staging: mt7621-pci: change variable to print for slot + +We are using the counter to print the slot which has been +enabled. Use the correct associated slot for the port instead. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200320110123.9907-5-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -562,7 +562,7 @@ static void mt7621_pcie_enable_ports(str + if (port->enabled) { + mt7621_pcie_port_clk_enable(port); + mt7621_pcie_enable_port(port); +- dev_info(dev, "PCIE%d enabled\n", num_slots_enabled); ++ dev_info(dev, "PCIE%d enabled\n", port->slot); + num_slots_enabled++; + } + } diff --git a/target/linux/ramips/patches-5.10/0113-staging-mt7621-pci-be-sure-gpio-descriptor-is-null-o.patch b/target/linux/ramips/patches-5.10/0113-staging-mt7621-pci-be-sure-gpio-descriptor-is-null-o.patch new file mode 100644 index 0000000000..bf6fdf1d49 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0113-staging-mt7621-pci-be-sure-gpio-descriptor-is-null-o.patch @@ -0,0 +1,33 @@ +From 87068309300c707d659ce79232eae827604804a4 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Fri, 20 Mar 2020 12:01:23 +0100 +Subject: [PATCH] staging: mt7621-pci: be sure gpio descriptor is null on fails + +Function 'devm_gpiod_get_index_optional' returns NULL if the +descriptor is invalid and the error associated for the error +pointer is ENOENT. Sometimes if the pin is just assigned the +error associated for the pointer might not be ENOENT but other. +In order to avoid weirds behaviours if this happen set descriptor +to NULL in the driver port structure. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200320110123.9907-6-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -363,8 +363,10 @@ static int mt7621_pcie_parse_port(struct + + port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot, + GPIOD_OUT_LOW); +- if (IS_ERR(port->gpio_rst)) ++ if (IS_ERR(port->gpio_rst)) { + dev_notice(dev, "Failed to get GPIO for PCIe%d\n", slot); ++ port->gpio_rst = NULL; ++ } + + port->slot = slot; + port->pcie = pcie; diff --git a/target/linux/ramips/patches-5.10/0114-staging-mt7621-pci-avoid-to-poweroff-the-phy-for-slo.patch b/target/linux/ramips/patches-5.10/0114-staging-mt7621-pci-avoid-to-poweroff-the-phy-for-slo.patch new file mode 100644 index 0000000000..d733e58537 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0114-staging-mt7621-pci-avoid-to-poweroff-the-phy-for-slo.patch @@ -0,0 +1,79 @@ +From d81fe3c13aa6f4ab1ec318212d2007175e6d05aa Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Fri, 20 Mar 2020 16:38:37 +0100 +Subject: [PATCH] staging: mt7621-pci: avoid to poweroff the phy for slot one + +Phy for slot 0 and 1 is shared and handled properly in slot 0. +If there is only one port in use,(slot 0) we shall not call the +'phy_power_off' function with an invalid slot because kernel +will crash with an unaligned access fault like the following: + +mt7621-pci 1e140000.pcie: Error applying setting, reverse things back +mt7621-pci-phy 1e149000.pcie-phy: PHY for 0xbe149000 (dual port = 1) +mt7621-pci-phy 1e14a000.pcie-phy: PHY for 0xbe14a000 (dual port = 0) +mt7621-pci-phy 1e149000.pcie-phy: Xtal is 40MHz +mt7621-pci-phy 1e14a000.pcie-phy: Xtal is 40MHz +mt7621-pci 1e140000.pcie: pcie1 no card, disable it (RST & CLK) +Unhandled kernel unaligned access[#1]: +CPU: 3 PID: 111 Comm: kworker/3:2 Not tainted 5.6.0-rc3-00347-g825c6f470c62-dirty #9 +Workqueue: events deferred_probe_work_func +$ 0 : 00000000 00000001 5f60d043 8fe1ba80 +$ 4 : 0000010d 01eb9000 00000000 00000000 +$ 8 : 294b4c00 80940000 00000008 000000ce +$12 : 2e303030 00000000 00000000 65696370 +$16 : ffffffed 0000010d 8e373cd0 8214c1e0 +$20 : 00000000 82144c80 82144680 8214c250 +$24 : 00000018 803ef8f4 +$28 : 8e372000 8e373c60 8214c080 803940e8 +Hi : 00000125 +Lo : 122f2000 +epc : 807b3328 mutex_lock+0x8/0x44 +ra : 803940e8 phy_power_off+0x28/0xb0 +Status: 1100fc03 KERNEL EXL IE +Cause : 00800010 (ExcCode 04) +BadVA : 0000010d +PrId : 0001992f (MIPS 1004Kc) +Modules linked in: +Process kworker/3:2 (pid: 111, threadinfo=(ptrval), task=(ptrval), tls=00000000) +Stack : 8e373cd0 803fe4f4 8e372000 8e373c90 8214c080 804fde1c 8e373c98 808d62f4 + 8e373c78 00000000 8214c254 804fe648 1e160000 804f27b8 00000001 808d62f4 + 00000000 00000001 8214c228 808d62f4 80930000 809a0000 8fd47e10 808d63d4 + 808d62d4 8fd47e10 808d0000 808d0000 8e373cd0 8e373cd0 809e2a74 809db510 + 809db510 00000006 00000001 00000000 00000000 00000000 01000000 1e1440ff + ... +Call Trace: +[<807b3328>] mutex_lock+0x8/0x44 +[<803940e8>] phy_power_off+0x28/0xb0 +[<804fe648>] mt7621_pci_probe+0xc20/0xd18 +[<80402ab8>] platform_drv_probe+0x40/0x94 +[<80400a74>] really_probe+0x104/0x364 +[<803feb74>] bus_for_each_drv+0x84/0xdc +[<80400924>] __device_attach+0xdc/0x120 +[<803ffb5c>] bus_probe_device+0xa0/0xbc +[<80400124>] deferred_probe_work_func+0x7c/0xbc +[<800420e8>] process_one_work+0x230/0x450 +[<80042638>] worker_thread+0x330/0x5fc +[<80048eb0>] kthread+0x12c/0x134 +[<80007438>] ret_from_kernel_thread+0x14/0x1c +Code: 24050002 27bdfff8 8f830000 <c0850000> 14a00005 00000000 00600825 e0810000 1020fffa + +Fixes: bf516f413f4e ("staging: mt7621-pci: use only two phys from device tree") +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200320153837.20415-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -517,7 +517,8 @@ static void mt7621_pcie_init_ports(struc + if (!mt7621_pcie_port_is_linkup(port)) { + dev_err(dev, "pcie%d no card, disable it (RST & CLK)\n", + slot); +- phy_power_off(port->phy); ++ if (slot != 1) ++ phy_power_off(port->phy); + mt7621_control_assert(port); + mt7621_pcie_port_clk_disable(port); + port->enabled = false; diff --git a/target/linux/ramips/patches-5.10/0115-staging-mt7621-pci-delete-release-gpios-related-code.patch b/target/linux/ramips/patches-5.10/0115-staging-mt7621-pci-delete-release-gpios-related-code.patch new file mode 100644 index 0000000000..5bfd205691 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0115-staging-mt7621-pci-delete-release-gpios-related-code.patch @@ -0,0 +1,91 @@ +From 9d789a7728c37e8730b6a9cca60cf155f18537ea Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Sat, 21 Mar 2020 08:26:50 +0100 +Subject: [PATCH] staging: mt7621-pci: delete release gpios related code + +Making gpio8 and gpio9 vendor specific and putting them +into the specific dts file makes not needed to release +gpios anymore because we are not occupying those pins +in the first place if it is not necessary. When the +device tree is parsed we can also check and return for +the error because we rely in the fact that the related +device for the board is correct. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200321072650.7784-3-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 27 +++++++-------------------- + 1 file changed, 7 insertions(+), 20 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -364,8 +364,8 @@ static int mt7621_pcie_parse_port(struct + port->gpio_rst = devm_gpiod_get_index_optional(dev, "reset", slot, + GPIOD_OUT_LOW); + if (IS_ERR(port->gpio_rst)) { +- dev_notice(dev, "Failed to get GPIO for PCIe%d\n", slot); +- port->gpio_rst = NULL; ++ dev_err(dev, "Failed to get GPIO for PCIe%d\n", slot); ++ return PTR_ERR(port->gpio_rst); + } + + port->slot = slot; +@@ -474,15 +474,6 @@ static void mt7621_pcie_reset_ep_deasser + mdelay(PERST_DELAY_MS); + } + +-static void mt7621_pcie_release_gpios(struct mt7621_pcie *pcie) +-{ +- struct mt7621_pcie_port *port; +- +- list_for_each_entry(port, &pcie->ports, list) +- if (port->gpio_rst) +- gpiod_put(port->gpio_rst); +-} +- + static void mt7621_pcie_init_ports(struct mt7621_pcie *pcie) + { + struct device *dev = pcie->dev; +@@ -684,7 +675,7 @@ static int mt7621_pci_probe(struct platf + err = mt7621_pci_parse_request_of_pci_ranges(pcie); + if (err) { + dev_err(dev, "Error requesting pci resources from ranges"); +- goto out_release_gpios; ++ return err; + } + + /* set resources limits */ +@@ -698,8 +689,7 @@ static int mt7621_pci_probe(struct platf + err = mt7621_pcie_init_virtual_bridges(pcie); + if (err) { + dev_err(dev, "Nothing is connected in virtual bridges. Exiting..."); +- err = 0; +- goto out_release_gpios; ++ return 0; + } + + mt7621_pcie_enable_ports(pcie); +@@ -709,19 +699,16 @@ static int mt7621_pci_probe(struct platf + err = mt7621_pcie_request_resources(pcie, &res); + if (err) { + dev_err(dev, "Error requesting resources\n"); +- goto out_release_gpios; ++ return err; + } + + err = mt7621_pcie_register_host(bridge, &res); + if (err) { + dev_err(dev, "Error registering host\n"); +- goto out_release_gpios; ++ return err; + } + +-out_release_gpios: +- mt7621_pcie_release_gpios(pcie); +- +- return err; ++ return 0; + } + + static const struct of_device_id mt7621_pci_ids[] = { diff --git a/target/linux/ramips/patches-5.10/0116-staging-mt7621-pci-use-builtin_platform_driver.patch b/target/linux/ramips/patches-5.10/0116-staging-mt7621-pci-use-builtin_platform_driver.patch new file mode 100644 index 0000000000..b333bb326b --- /dev/null +++ b/target/linux/ramips/patches-5.10/0116-staging-mt7621-pci-use-builtin_platform_driver.patch @@ -0,0 +1,29 @@ +From 60a15339ceab9fc2a6cdc85fd54b66b2c947ab4e Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Sat, 21 Mar 2020 14:36:21 +0100 +Subject: [PATCH] staging: mt7621-pci: use builtin_platform_driver() + +Macro builtin_platform_driver can be used for builtin drivers +that don't do anything in driver init. So, use the macro +builtin_platform_driver and remove some boilerplate code. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200321133624.31388-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -725,9 +725,4 @@ static struct platform_driver mt7621_pci + }, + }; + +-static int __init mt7621_pci_init(void) +-{ +- return platform_driver_register(&mt7621_pci_driver); +-} +- +-module_init(mt7621_pci_init); ++builtin_platform_driver(mt7621_pci_driver); diff --git a/target/linux/ramips/patches-5.10/0117-staging-mt7621-pci-phy-use-builtin_platform_driver.patch b/target/linux/ramips/patches-5.10/0117-staging-mt7621-pci-phy-use-builtin_platform_driver.patch new file mode 100644 index 0000000000..e55def40f5 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0117-staging-mt7621-pci-phy-use-builtin_platform_driver.patch @@ -0,0 +1,32 @@ +From ffe3dee4081055b4f58bc50dd3f5c97de42cf126 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Sat, 21 Mar 2020 14:36:23 +0100 +Subject: [PATCH] staging: mt7621-pci-phy: use builtin_platform_driver() + +Macro builtin_platform_driver can be used for builtin drivers +that don't do anything in driver init. So, use the macro +builtin_platform_driver and remove some boilerplate code. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200321133624.31388-3-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +--- a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c ++++ b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c +@@ -361,12 +361,7 @@ static struct platform_driver mt7621_pci + }, + }; + +-static int __init mt7621_pci_phy_drv_init(void) +-{ +- return platform_driver_register(&mt7621_pci_phy_driver); +-} +- +-module_init(mt7621_pci_phy_drv_init); ++builtin_platform_driver(mt7621_pci_phy_driver); + + MODULE_AUTHOR("Sergio Paracuellos <sergio.paracuellos@gmail.com>"); + MODULE_DESCRIPTION("MediaTek MT7621 PCIe PHY driver"); diff --git a/target/linux/ramips/patches-5.10/0118-staging-mt7621-pci-phy-re-do-xtal_mode-detection.patch b/target/linux/ramips/patches-5.10/0118-staging-mt7621-pci-phy-re-do-xtal_mode-detection.patch new file mode 100644 index 0000000000..9eb8345144 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0118-staging-mt7621-pci-phy-re-do-xtal_mode-detection.patch @@ -0,0 +1,68 @@ +From ff83e3023cb8fc3b5dfc12e0c91bf1eb9dc4c4c6 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Sat, 21 Mar 2020 14:36:24 +0100 +Subject: [PATCH] staging: mt7621-pci-phy: re-do 'xtal_mode' detection + +Detection of the Xtal mode is using magic numbers that +can be avoided using properly some definitions and a more +accurate variable name from 'reg' into 'xtal_mode'. This +increase readability. + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200321133624.31388-4-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +--- a/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c ++++ b/drivers/staging/mt7621-pci-phy/pci-mt7621-phy.c +@@ -75,6 +75,9 @@ + + #define RG_PE1_FRC_MSTCKDIV BIT(5) + ++#define XTAL_MODE_SEL_SHIFT 6 ++#define XTAL_MODE_SEL_MASK 0x7 ++ + #define MAX_PHYS 2 + + /** +@@ -136,9 +139,11 @@ static void mt7621_bypass_pipe_rst(struc + static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) + { + struct device *dev = phy->dev; +- u32 reg = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0); ++ u32 xtal_mode; ++ ++ xtal_mode = (rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0) ++ >> XTAL_MODE_SEL_SHIFT) & XTAL_MODE_SEL_MASK; + +- reg = (reg >> 6) & 0x7; + /* Set PCIe Port PHY to disable SSC */ + /* Debug Xtal Type */ + mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG, +@@ -154,13 +159,13 @@ static void mt7621_set_phy_for_ssc(struc + RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); + } + +- if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */ ++ if (xtal_mode <= 5 && xtal_mode >= 3) { /* 40MHz Xtal */ + /* Set Pre-divider ratio (for host mode) */ + mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, + RG_PE1_H_PLL_PREDIV, + RG_PE1_H_PLL_PREDIV_VAL(0x01)); + dev_info(dev, "Xtal is 40MHz\n"); +- } else if (reg >= 6) { /* 25MHz Xal */ ++ } else if (xtal_mode >= 6) { /* 25MHz Xal */ + mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, + RG_PE1_H_PLL_PREDIV, + RG_PE1_H_PLL_PREDIV_VAL(0x00)); +@@ -206,7 +211,7 @@ static void mt7621_set_phy_for_ssc(struc + mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG, + RG_PE1_H_PLL_BR, RG_PE1_H_PLL_BR_VAL(0x00)); + +- if (reg <= 5 && reg >= 3) { /* 40MHz Xtal */ ++ if (xtal_mode <= 5 && xtal_mode >= 3) { /* 40MHz Xtal */ + /* set force mode enable of da_pe1_mstckdiv */ + mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG, + RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV, diff --git a/target/linux/ramips/patches-5.10/0119-staging-mt7621-pci-avoid-to-set-iomem_resource-addre.patch b/target/linux/ramips/patches-5.10/0119-staging-mt7621-pci-avoid-to-set-iomem_resource-addre.patch new file mode 100644 index 0000000000..9af46f4196 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0119-staging-mt7621-pci-avoid-to-set-iomem_resource-addre.patch @@ -0,0 +1,35 @@ +From 4f0f36b67564311a4ce4441510ef94848febbab2 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Sun, 22 Mar 2020 08:21:28 +0100 +Subject: [PATCH] staging: mt7621-pci: avoid to set 'iomem_resource' addresses + +Setting up kernel resource 'iomem_resource' for PCI with +addresses parsed from device tree gots into a conflict within +the usb xhci driver: + +xhci-mtk 1e1c0000.xhci: can't request region for resource [mem 0x1e1c0000-0x1e1c0fff] +xhci-mtk: probe of 1e1c0000.xhci failed with error -16 + +Don't assign it and maintain the default addresses for this +resource seems to fix the problem. Checking legacy driver it +is being only setting the 'ioport_resource'. + +Fixes: 09dd629eeabb ("staging: mt7621-pci: fix io space and properly set resource limits") +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200322072128.4454-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -679,8 +679,6 @@ static int mt7621_pci_probe(struct platf + } + + /* set resources limits */ +- iomem_resource.start = pcie->mem.start; +- iomem_resource.end = pcie->mem.end; + ioport_resource.start = pcie->io.start; + ioport_resource.end = pcie->io.end; + diff --git a/target/linux/ramips/patches-5.10/0120-staging-mt7621-pci-properly-power-off-dual-ported-pc.patch b/target/linux/ramips/patches-5.10/0120-staging-mt7621-pci-properly-power-off-dual-ported-pc.patch new file mode 100644 index 0000000000..9efcb8011a --- /dev/null +++ b/target/linux/ramips/patches-5.10/0120-staging-mt7621-pci-properly-power-off-dual-ported-pc.patch @@ -0,0 +1,65 @@ +From 5fcded5e857cf66c9592e4be28c4dab4520c9177 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Thu, 9 Apr 2020 13:16:52 +0200 +Subject: [PATCH] staging: mt7621-pci: properly power off dual-ported pcie phy + +Pcie phy for pcie0 and pcie1 is shared using a dual ported +one. Current code was assuming that if nothing is connected +in pcie0 it won't be also nothing connected in pcie1. This +assumtion is wrong for some devices such us 'Mikrotik rbm33g' +and 'ZyXEL LTE3301-PLUS' where only connecting a card to the +second bus on the phy is possible. For such devices kernel +hangs in the same point because of the wrong poweroff of the +phy getting the following trace: + +mt7621-pci-phy 1e149000.pcie-phy: PHY for 0xbe149000 (dual port = 1) +mt7621-pci-phy 1e14a000.pcie-phy: PHY for 0xbe14a000 (dual port = 0) +mt7621-pci-phy 1e149000.pcie-phy: Xtal is 40MHz +mt7621-pci-phy 1e14a000.pcie-phy: Xtal is 40MHz +mt7621-pci 1e140000.pcie: pcie0 no card, disable it (RST & CLK) +[hangs] + +The wrong assumption is located in the 'mt7621_pcie_init_ports' +function where we are just making a power off of the phy for +slots 0 and 2 if nothing is connected in them. Hence, only +poweroff the phy if nothing is connected in both slot 0 and +slot 1 avoiding the kernel to hang. + +Fixes: 5737cfe87a9c ("staging: mt7621-pci: avoid to poweroff the phy for slot one") +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200409111652.30964-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-pci/pci-mt7621.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -502,17 +502,25 @@ static void mt7621_pcie_init_ports(struc + + mt7621_pcie_reset_ep_deassert(pcie); + ++ tmp = NULL; + list_for_each_entry(port, &pcie->ports, list) { + u32 slot = port->slot; + + if (!mt7621_pcie_port_is_linkup(port)) { + dev_err(dev, "pcie%d no card, disable it (RST & CLK)\n", + slot); +- if (slot != 1) +- phy_power_off(port->phy); + mt7621_control_assert(port); + mt7621_pcie_port_clk_disable(port); + port->enabled = false; ++ ++ if (slot == 0) { ++ tmp = port; ++ continue; ++ } ++ ++ if (slot == 1 && tmp && !tmp->enabled) ++ phy_power_off(tmp->phy); ++ + } + } + } diff --git a/target/linux/ramips/patches-5.10/0121-staging-mt7621-pci-fix-PCIe-interrupt-mapping.patch b/target/linux/ramips/patches-5.10/0121-staging-mt7621-pci-fix-PCIe-interrupt-mapping.patch new file mode 100644 index 0000000000..68de6df2db --- /dev/null +++ b/target/linux/ramips/patches-5.10/0121-staging-mt7621-pci-fix-PCIe-interrupt-mapping.patch @@ -0,0 +1,157 @@ +From fab6710e4c51f4eb622f95a08322ab5fdbe3f295 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Mon, 13 Apr 2020 07:59:42 +0200 +Subject: [PATCH] staging: mt7621-pci: fix PCIe interrupt mapping + +MT7621 has three assigned interrupts for the pcie. This +interrupts should properly being mapped taking into account +which devices are finally connected in which bus according +to link status. So the irq mappings should be as follows +according to link status (three bits indicating which devices +are link up): + +* For PCIe Bus 1 slot 0: + - status = 0x2 || status = 0x6 => IRQ = pcie1_irq (24). + - status = 0x4 => IRQ = pcie2_irq (25). + - default => IRQ = pcie0_irq (23). +* For PCIe Bus 2 slot 0: + - status = 0x5 || status = 0x6 => IRQ = pcie2_irq (25). + - default => IRQ = pcie1_irq (24). +* For PCIe Bus 2 slot 1: + - status = 0x5 || status = 0x6 => IRQ = pcie2_irq (25). + - default => IRQ = pcie1_irq (24). +* For PCIe Bus 3 any slot: + - default => IRQ = pcie2_irq (25). + +Because of this, the function 'of_irq_parse_and_map_pci' cannot +be used and we need to change device tree information from using +the 'interrupt-map' and 'interrupt-map-mask' properties into an +'interrupts' property to be able to get irq information from the +ports using the 'platform_get_irq' and storing an 'irq-map' into +the pcie driver data node to properly map correct irq using a +new 'mt7621_map_irq' function where this map will be read and the +correct irq returned. + +Fixes: 46d093124df4 ("staging: mt7621-pci: improve interrupt mapping") +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Link: https://lore.kernel.org/r/20200413055942.2714-1-sergio.paracuellos@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/staging/mt7621-dts/mt7621.dtsi | 9 +++---- + drivers/staging/mt7621-pci/pci-mt7621.c | 36 +++++++++++++++++++++++-- + 2 files changed, 38 insertions(+), 7 deletions(-) + +--- a/drivers/staging/mt7621-pci/pci-mt7621.c ++++ b/drivers/staging/mt7621-pci/pci-mt7621.c +@@ -97,6 +97,7 @@ + * @pcie_rst: pointer to port reset control + * @gpio_rst: gpio reset + * @slot: port slot ++ * @irq: GIC irq + * @enabled: indicates if port is enabled + */ + struct mt7621_pcie_port { +@@ -107,6 +108,7 @@ struct mt7621_pcie_port { + struct reset_control *pcie_rst; + struct gpio_desc *gpio_rst; + u32 slot; ++ int irq; + bool enabled; + }; + +@@ -120,6 +122,7 @@ struct mt7621_pcie_port { + * @dev: Pointer to PCIe device + * @io_map_base: virtual memory base address for io + * @ports: pointer to PCIe port information ++ * @irq_map: irq mapping info according pcie link status + * @resets_inverted: depends on chip revision + * reset lines are inverted. + */ +@@ -135,6 +138,7 @@ struct mt7621_pcie { + } offset; + unsigned long io_map_base; + struct list_head ports; ++ int irq_map[PCIE_P2P_MAX]; + bool resets_inverted; + }; + +@@ -279,6 +283,16 @@ static void setup_cm_memory_region(struc + } + } + ++static int mt7621_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) ++{ ++ struct mt7621_pcie *pcie = pdev->bus->sysdata; ++ struct device *dev = pcie->dev; ++ int irq = pcie->irq_map[slot]; ++ ++ dev_info(dev, "bus=%d slot=%d irq=%d\n", pdev->bus->number, slot, irq); ++ return irq; ++} ++ + static int mt7621_pci_parse_request_of_pci_ranges(struct mt7621_pcie *pcie) + { + struct device *dev = pcie->dev; +@@ -330,6 +344,7 @@ static int mt7621_pcie_parse_port(struct + { + struct mt7621_pcie_port *port; + struct device *dev = pcie->dev; ++ struct platform_device *pdev = to_platform_device(dev); + struct device_node *pnode = dev->of_node; + struct resource regs; + char name[10]; +@@ -371,6 +386,12 @@ static int mt7621_pcie_parse_port(struct + port->slot = slot; + port->pcie = pcie; + ++ port->irq = platform_get_irq(pdev, slot); ++ if (port->irq < 0) { ++ dev_err(dev, "Failed to get IRQ for PCIe%d\n", slot); ++ return -ENXIO; ++ } ++ + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &pcie->ports); + +@@ -585,13 +606,15 @@ static int mt7621_pcie_init_virtual_brid + { + u32 pcie_link_status = 0; + u32 n; +- int i; ++ int i = 0; + u32 p2p_br_devnum[PCIE_P2P_MAX]; ++ int irqs[PCIE_P2P_MAX]; + struct mt7621_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) { + u32 slot = port->slot; + ++ irqs[i++] = port->irq; + if (port->enabled) + pcie_link_status |= BIT(slot); + } +@@ -614,6 +637,15 @@ static int mt7621_pcie_init_virtual_brid + (p2p_br_devnum[1] << PCIE_P2P_BR_DEVNUM1_SHIFT) | + (p2p_br_devnum[2] << PCIE_P2P_BR_DEVNUM2_SHIFT)); + ++ /* Assign IRQs */ ++ n = 0; ++ for (i = 0; i < PCIE_P2P_MAX; i++) ++ if (pcie_link_status & BIT(i)) ++ pcie->irq_map[n++] = irqs[i]; ++ ++ for (i = n; i < PCIE_P2P_MAX; i++) ++ pcie->irq_map[i] = -1; ++ + return 0; + } + +@@ -638,7 +670,7 @@ static int mt7621_pcie_register_host(str + host->busnr = pcie->busn.start; + host->dev.parent = pcie->dev; + host->ops = &mt7621_pci_ops; +- host->map_irq = of_irq_parse_and_map_pci; ++ host->map_irq = mt7621_map_irq; + host->swizzle_irq = pci_common_swizzle; + host->sysdata = pcie; + diff --git a/target/linux/ramips/patches-5.10/0122-mips-ralink-enable-zboot-support.patch b/target/linux/ramips/patches-5.10/0122-mips-ralink-enable-zboot-support.patch new file mode 100644 index 0000000000..76615ed491 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0122-mips-ralink-enable-zboot-support.patch @@ -0,0 +1,26 @@ +From 1f0400d0e2c410b04f246aefb2e9b5155eb4b0bf Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Tue, 13 Oct 2020 10:05:47 +0800 +Subject: mips: ralink: enable zboot support + +Some of these ralink devices come with an ancient u-boot which can't +extract LZMA properly when image gets too big. +Enable zboot support to get a self-extracting kernel instead of relying +on broken u-boot support. + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> +--- + arch/mips/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -625,6 +625,7 @@ config RALINK + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SYS_SUPPORTS_MIPS16 ++ select SYS_SUPPORTS_ZBOOT + select SYS_HAS_EARLY_PRINTK + select CLKDEV_LOOKUP + select ARCH_HAS_RESET_CONTROLLER diff --git a/target/linux/ramips/patches-5.10/0123-mips-ralink-manage-low-reset-lines.patch b/target/linux/ramips/patches-5.10/0123-mips-ralink-manage-low-reset-lines.patch new file mode 100644 index 0000000000..bdf98f223c --- /dev/null +++ b/target/linux/ramips/patches-5.10/0123-mips-ralink-manage-low-reset-lines.patch @@ -0,0 +1,45 @@ +From 3f9ef7785a9cd69cb75f5e2ea4ca79a24752e496 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule <sander@svanheule.net> +Date: Wed, 3 Feb 2021 10:21:41 +0100 +Subject: MIPS: ralink: manage low reset lines + +Reset lines with indices smaller than 8 are currently considered invalid +by the rt2880-reset reset controller. + +The MT7621 SoC uses a number of these low reset lines. The DTS defines +reset lines "hsdma", "fe", and "mcm" with respective values 5, 6, and 2. +As a result of the above restriction, these resets cannot be asserted or +de-asserted by the reset controller. In cases where the bootloader does +not de-assert these lines, this results in e.g. the MT7621's internal +switch staying in reset. + +Change the reset controller to only ignore the system reset, so all +reset lines with index greater than 0 are considered valid. + +Signed-off-by: Sander Vanheule <sander@svanheule.net> +Acked-by: John Crispin <john@phrozen.org> +Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> +--- + arch/mips/ralink/reset.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/mips/ralink/reset.c ++++ b/arch/mips/ralink/reset.c +@@ -27,7 +27,7 @@ static int ralink_assert_device(struct r + { + u32 val; + +- if (id < 8) ++ if (id == 0) + return -1; + + val = rt_sysc_r32(SYSC_REG_RESET_CTRL); +@@ -42,7 +42,7 @@ static int ralink_deassert_device(struct + { + u32 val; + +- if (id < 8) ++ if (id == 0) + return -1; + + val = rt_sysc_r32(SYSC_REG_RESET_CTRL); diff --git a/target/linux/ramips/patches-5.10/0200-linkit_bootstrap.patch b/target/linux/ramips/patches-5.10/0200-linkit_bootstrap.patch new file mode 100644 index 0000000000..9142faaf43 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0200-linkit_bootstrap.patch @@ -0,0 +1,97 @@ +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -52,6 +52,7 @@ obj-$(CONFIG_ECHO) += echo/ + obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o + obj-$(CONFIG_CXL_BASE) += cxl/ + obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o ++obj-$(CONFIG_SOC_MT7620) += linkit.o + obj-$(CONFIG_OCXL) += ocxl/ + obj-y += cardreader/ + obj-$(CONFIG_PVPANIC) += pvpanic.o +--- /dev/null ++++ b/drivers/misc/linkit.c +@@ -0,0 +1,84 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * publishhed by the Free Software Foundation. ++ * ++ * Copyright (C) 2015 John Crispin <blogic@openwrt.org> ++ */ ++ ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/of.h> ++#include <linux/mtd/mtd.h> ++#include <linux/gpio.h> ++ ++#define LINKIT_LATCH_GPIO 11 ++ ++struct linkit_hw_data { ++ char board[16]; ++ char rev[16]; ++}; ++ ++static void sanify_string(char *s) ++{ ++ int i; ++ ++ for (i = 0; i < 15; i++) ++ if (s[i] <= 0x20) ++ s[i] = '\0'; ++ s[15] = '\0'; ++} ++ ++static int linkit_probe(struct platform_device *pdev) ++{ ++ struct linkit_hw_data hw; ++ struct mtd_info *mtd; ++ size_t retlen; ++ int ret; ++ ++ mtd = get_mtd_device_nm("factory"); ++ if (IS_ERR(mtd)) ++ return PTR_ERR(mtd); ++ ++ ret = mtd_read(mtd, 0x400, sizeof(hw), &retlen, (u_char *) &hw); ++ put_mtd_device(mtd); ++ ++ sanify_string(hw.board); ++ sanify_string(hw.rev); ++ ++ dev_info(&pdev->dev, "Version : %s\n", hw.board); ++ dev_info(&pdev->dev, "Revision : %s\n", hw.rev); ++ ++ if (!strcmp(hw.board, "LINKITS7688")) { ++ dev_info(&pdev->dev, "setting up bootstrap latch\n"); ++ ++ if (devm_gpio_request(&pdev->dev, LINKIT_LATCH_GPIO, "bootstrap")) { ++ dev_err(&pdev->dev, "failed to setup bootstrap gpio\n"); ++ return -1; ++ } ++ gpio_direction_output(LINKIT_LATCH_GPIO, 0); ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id linkit_match[] = { ++ { .compatible = "mediatek,linkit" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, linkit_match); ++ ++static struct platform_driver linkit_driver = { ++ .probe = linkit_probe, ++ .driver = { ++ .name = "mtk-linkit", ++ .owner = THIS_MODULE, ++ .of_match_table = linkit_match, ++ }, ++}; ++ ++int __init linkit_init(void) ++{ ++ return platform_driver_register(&linkit_driver); ++} ++late_initcall_sync(linkit_init); diff --git a/target/linux/ramips/patches-5.10/0300-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch b/target/linux/ramips/patches-5.10/0300-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch new file mode 100644 index 0000000000..ba844fed0f --- /dev/null +++ b/target/linux/ramips/patches-5.10/0300-mtd-rawnand-add-driver-support-for-MT7621-nand-flash.patch @@ -0,0 +1,1400 @@ +From e84e2430ee0e483842b4ff013ae8a6e7e2fa2734 Mon Sep 17 00:00:00 2001 +From: Weijie Gao <weijie.gao@mediatek.com> +Date: Wed, 1 Apr 2020 02:07:58 +0800 +Subject: [PATCH 1/2] mtd: rawnand: add driver support for MT7621 nand + flash controller + +This patch adds NAND flash controller driver for MediaTek MT7621 SoC. + +The NAND flash controller is similar with controllers described in +mtk_nand.c, except that the controller from MT7621 doesn't support DMA +transmission, and some registers' offset and fields are different. + +Signed-off-by: Weijie Gao <weijie.gao@mediatek.com> +--- + drivers/mtd/nand/raw/Kconfig | 8 + + drivers/mtd/nand/raw/Makefile | 1 + + drivers/mtd/nand/raw/mt7621_nand.c | 1348 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 1357 insertions(+) + create mode 100644 drivers/mtd/nand/raw/mt7621_nand.c + +--- a/drivers/mtd/nand/raw/Kconfig ++++ b/drivers/mtd/nand/raw/Kconfig +@@ -391,6 +391,14 @@ config MTD_NAND_QCOM + Enables support for NAND flash chips on SoCs containing the EBI2 NAND + controller. This controller is found on IPQ806x SoC. + ++config MTD_NAND_MT7621 ++ tristate "MT7621 NAND controller" ++ depends on SOC_MT7621 || COMPILE_TEST ++ depends on HAS_IOMEM ++ help ++ Enables support for NAND controller on MT7621 SoC. ++ This driver uses PIO mode for data transmission instead of DMA mode. ++ + config MTD_NAND_MTK + tristate "MTK NAND controller" + depends on ARCH_MEDIATEK || COMPILE_TEST +--- a/drivers/mtd/nand/raw/Makefile ++++ b/drivers/mtd/nand/raw/Makefile +@@ -52,6 +52,7 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_n + obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o + obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ + obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o ++obj-$(CONFIG_MTD_NAND_MT7621) += mt7621_nand.o + obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o + obj-$(CONFIG_MTD_NAND_MXIC) += mxic_nand.o + obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o +--- /dev/null ++++ b/drivers/mtd/nand/raw/mt7621_nand.c +@@ -0,0 +1,1350 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * MediaTek MT7621 NAND Flash Controller driver ++ * ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao <weijie.gao@mediatek.com> ++ */ ++ ++#include <linux/io.h> ++#include <linux/clk.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/sizes.h> ++#include <linux/iopoll.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/rawnand.h> ++#include <linux/mtd/partitions.h> ++#include <linux/platform_device.h> ++#include <asm/addrspace.h> ++ ++/* NFI core registers */ ++#define NFI_CNFG 0x000 ++#define CNFG_OP_MODE_S 12 ++#define CNFG_OP_MODE_M GENMASK(14, 12) ++#define CNFG_OP_CUSTOM 6 ++#define CNFG_AUTO_FMT_EN BIT(9) ++#define CNFG_HW_ECC_EN BIT(8) ++#define CNFG_BYTE_RW BIT(6) ++#define CNFG_READ_MODE BIT(1) ++ ++#define NFI_PAGEFMT 0x004 ++#define PAGEFMT_FDM_ECC_S 12 ++#define PAGEFMT_FDM_ECC_M GENMASK(15, 12) ++#define PAGEFMT_FDM_S 8 ++#define PAGEFMT_FDM_M GENMASK(11, 8) ++#define PAGEFMT_SPARE_S 4 ++#define PAGEFMT_SPARE_M GENMASK(5, 4) ++#define PAGEFMT_PAGE_S 0 ++#define PAGEFMT_PAGE_M GENMASK(1, 0) ++ ++#define NFI_CON 0x008 ++#define CON_NFI_SEC_S 12 ++#define CON_NFI_SEC_M GENMASK(15, 12) ++#define CON_NFI_BWR BIT(9) ++#define CON_NFI_BRD BIT(8) ++#define CON_NFI_RST BIT(1) ++#define CON_FIFO_FLUSH BIT(0) ++ ++#define NFI_ACCCON 0x00c ++#define ACCCON_POECS_S 28 ++#define ACCCON_POECS_MAX 0x0f ++#define ACCCON_POECS_DEF 3 ++#define ACCCON_PRECS_S 22 ++#define ACCCON_PRECS_MAX 0x3f ++#define ACCCON_PRECS_DEF 3 ++#define ACCCON_C2R_S 16 ++#define ACCCON_C2R_MAX 0x3f ++#define ACCCON_C2R_DEF 7 ++#define ACCCON_W2R_S 12 ++#define ACCCON_W2R_MAX 0x0f ++#define ACCCON_W2R_DEF 7 ++#define ACCCON_WH_S 8 ++#define ACCCON_WH_MAX 0x0f ++#define ACCCON_WH_DEF 15 ++#define ACCCON_WST_S 4 ++#define ACCCON_WST_MAX 0x0f ++#define ACCCON_WST_DEF 15 ++#define ACCCON_WST_MIN 3 ++#define ACCCON_RLT_S 0 ++#define ACCCON_RLT_MAX 0x0f ++#define ACCCON_RLT_DEF 15 ++#define ACCCON_RLT_MIN 3 ++ ++#define NFI_CMD 0x020 ++ ++#define NFI_ADDRNOB 0x030 ++#define ADDR_ROW_NOB_S 4 ++#define ADDR_ROW_NOB_M GENMASK(6, 4) ++#define ADDR_COL_NOB_S 0 ++#define ADDR_COL_NOB_M GENMASK(2, 0) ++ ++#define NFI_COLADDR 0x034 ++#define NFI_ROWADDR 0x038 ++ ++#define NFI_STRDATA 0x040 ++#define STR_DATA BIT(0) ++ ++#define NFI_CNRNB 0x044 ++#define CB2R_TIME_S 4 ++#define CB2R_TIME_M GENMASK(7, 4) ++#define STR_CNRNB BIT(0) ++ ++#define NFI_DATAW 0x050 ++#define NFI_DATAR 0x054 ++ ++#define NFI_PIO_DIRDY 0x058 ++#define PIO_DIRDY BIT(0) ++ ++#define NFI_STA 0x060 ++#define STA_NFI_FSM_S 16 ++#define STA_NFI_FSM_M GENMASK(19, 16) ++#define STA_FSM_CUSTOM_DATA 14 ++#define STA_BUSY BIT(8) ++#define STA_ADDR BIT(1) ++#define STA_CMD BIT(0) ++ ++#define NFI_ADDRCNTR 0x070 ++#define SEC_CNTR_S 12 ++#define SEC_CNTR_M GENMASK(15, 12) ++#define SEC_ADDR_S 0 ++#define SEC_ADDR_M GENMASK(9, 0) ++ ++#define NFI_CSEL 0x090 ++#define CSEL_S 0 ++#define CSEL_M GENMASK(1, 0) ++ ++#define NFI_FDM0L 0x0a0 ++#define NFI_FDML(n) (0x0a0 + ((n) << 3)) ++ ++#define NFI_FDM0M 0x0a4 ++#define NFI_FDMM(n) (0x0a4 + ((n) << 3)) ++ ++#define NFI_MASTER_STA 0x210 ++#define MAS_ADDR GENMASK(11, 9) ++#define MAS_RD GENMASK(8, 6) ++#define MAS_WR GENMASK(5, 3) ++#define MAS_RDDLY GENMASK(2, 0) ++ ++/* ECC engine registers */ ++#define ECC_ENCCON 0x000 ++#define ENC_EN BIT(0) ++ ++#define ECC_ENCCNFG 0x004 ++#define ENC_CNFG_MSG_S 16 ++#define ENC_CNFG_MSG_M GENMASK(28, 16) ++#define ENC_MODE_S 4 ++#define ENC_MODE_M GENMASK(5, 4) ++#define ENC_MODE_NFI 1 ++#define ENC_TNUM_S 0 ++#define ENC_TNUM_M GENMASK(2, 0) ++ ++#define ECC_ENCIDLE 0x00c ++#define ENC_IDLE BIT(0) ++ ++#define ECC_DECCON 0x100 ++#define DEC_EN BIT(0) ++ ++#define ECC_DECCNFG 0x104 ++#define DEC_EMPTY_EN BIT(31) ++#define DEC_CS_S 16 ++#define DEC_CS_M GENMASK(28, 16) ++#define DEC_CON_S 12 ++#define DEC_CON_M GENMASK(13, 12) ++#define DEC_CON_EL 2 ++#define DEC_MODE_S 4 ++#define DEC_MODE_M GENMASK(5, 4) ++#define DEC_MODE_NFI 1 ++#define DEC_TNUM_S 0 ++#define DEC_TNUM_M GENMASK(2, 0) ++ ++#define ECC_DECIDLE 0x10c ++#define DEC_IDLE BIT(1) ++ ++#define ECC_DECENUM 0x114 ++#define ERRNUM_S 2 ++#define ERRNUM_M GENMASK(3, 0) ++ ++#define ECC_DECDONE 0x118 ++#define DEC_DONE7 BIT(7) ++#define DEC_DONE6 BIT(6) ++#define DEC_DONE5 BIT(5) ++#define DEC_DONE4 BIT(4) ++#define DEC_DONE3 BIT(3) ++#define DEC_DONE2 BIT(2) ++#define DEC_DONE1 BIT(1) ++#define DEC_DONE0 BIT(0) ++ ++#define ECC_DECEL(n) (0x11c + (n) * 4) ++#define DEC_EL_ODD_S 16 ++#define DEC_EL_EVEN_S 0 ++#define DEC_EL_M 0x1fff ++#define DEC_EL_BYTE_POS_S 3 ++#define DEC_EL_BIT_POS_M GENMASK(3, 0) ++ ++#define ECC_FDMADDR 0x13c ++ ++/* ENCIDLE and DECIDLE */ ++#define ECC_IDLE BIT(0) ++ ++#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \ ++ ((tpoecs) << ACCCON_POECS_S | (tprecs) << ACCCON_PRECS_S | \ ++ (tc2r) << ACCCON_C2R_S | (tw2r) << ACCCON_W2R_S | \ ++ (twh) << ACCCON_WH_S | (twst) << ACCCON_WST_S | (trlt)) ++ ++#define MASTER_STA_MASK (MAS_ADDR | MAS_RD | MAS_WR | \ ++ MAS_RDDLY) ++#define NFI_RESET_TIMEOUT 1000000 ++#define NFI_CORE_TIMEOUT 500000 ++#define ECC_ENGINE_TIMEOUT 500000 ++ ++#define ECC_SECTOR_SIZE 512 ++#define ECC_PARITY_BITS 13 ++ ++#define NFI_FDM_SIZE 8 ++ ++#define MT7621_NFC_NAME "mt7621-nand" ++ ++struct mt7621_nfc { ++ struct nand_controller controller; ++ struct nand_chip nand; ++ struct clk *nfi_clk; ++ struct device *dev; ++ ++ void __iomem *nfi_regs; ++ void __iomem *ecc_regs; ++ ++ u32 spare_per_sector; ++}; ++ ++static const u16 mt7621_nfi_page_size[] = { SZ_512, SZ_2K, SZ_4K }; ++static const u8 mt7621_nfi_spare_size[] = { 16, 26, 27, 28 }; ++static const u8 mt7621_ecc_strength[] = { 4, 6, 8, 10, 12 }; ++ ++static inline u32 nfi_read32(struct mt7621_nfc *nfc, u32 reg) ++{ ++ return readl(nfc->nfi_regs + reg); ++} ++ ++static inline void nfi_write32(struct mt7621_nfc *nfc, u32 reg, u32 val) ++{ ++ writel(val, nfc->nfi_regs + reg); ++} ++ ++static inline u16 nfi_read16(struct mt7621_nfc *nfc, u32 reg) ++{ ++ return readw(nfc->nfi_regs + reg); ++} ++ ++static inline void nfi_write16(struct mt7621_nfc *nfc, u32 reg, u16 val) ++{ ++ writew(val, nfc->nfi_regs + reg); ++} ++ ++static inline void ecc_write16(struct mt7621_nfc *nfc, u32 reg, u16 val) ++{ ++ writew(val, nfc->ecc_regs + reg); ++} ++ ++static inline u32 ecc_read32(struct mt7621_nfc *nfc, u32 reg) ++{ ++ return readl(nfc->ecc_regs + reg); ++} ++ ++static inline void ecc_write32(struct mt7621_nfc *nfc, u32 reg, u32 val) ++{ ++ return writel(val, nfc->ecc_regs + reg); ++} ++ ++static inline u8 *oob_fdm_ptr(struct nand_chip *nand, int sect) ++{ ++ return nand->oob_poi + sect * NFI_FDM_SIZE; ++} ++ ++static inline u8 *oob_ecc_ptr(struct mt7621_nfc *nfc, int sect) ++{ ++ struct nand_chip *nand = &nfc->nand; ++ ++ return nand->oob_poi + nand->ecc.steps * NFI_FDM_SIZE + ++ sect * (nfc->spare_per_sector - NFI_FDM_SIZE); ++} ++ ++static inline u8 *page_data_ptr(struct nand_chip *nand, const u8 *buf, ++ int sect) ++{ ++ return (u8 *)buf + sect * nand->ecc.size; ++} ++ ++static int mt7621_ecc_wait_idle(struct mt7621_nfc *nfc, u32 reg) ++{ ++ struct device *dev = nfc->dev; ++ u32 val; ++ int ret; ++ ++ ret = readw_poll_timeout_atomic(nfc->ecc_regs + reg, val, ++ val & ECC_IDLE, 10, ++ ECC_ENGINE_TIMEOUT); ++ if (ret) { ++ dev_warn(dev, "ECC engine timed out entering idle mode\n"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int mt7621_ecc_decoder_wait_done(struct mt7621_nfc *nfc, u32 sect) ++{ ++ struct device *dev = nfc->dev; ++ u32 val; ++ int ret; ++ ++ ret = readw_poll_timeout_atomic(nfc->ecc_regs + ECC_DECDONE, val, ++ val & (1 << sect), 10, ++ ECC_ENGINE_TIMEOUT); ++ ++ if (ret) { ++ dev_warn(dev, "ECC decoder for sector %d timed out\n", ++ sect); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static void mt7621_ecc_encoder_op(struct mt7621_nfc *nfc, bool enable) ++{ ++ mt7621_ecc_wait_idle(nfc, ECC_ENCIDLE); ++ ecc_write16(nfc, ECC_ENCCON, enable ? ENC_EN : 0); ++} ++ ++static void mt7621_ecc_decoder_op(struct mt7621_nfc *nfc, bool enable) ++{ ++ mt7621_ecc_wait_idle(nfc, ECC_DECIDLE); ++ ecc_write16(nfc, ECC_DECCON, enable ? DEC_EN : 0); ++} ++ ++static int mt7621_ecc_correct_check(struct mt7621_nfc *nfc, u8 *sector_buf, ++ u8 *fdm_buf, u32 sect) ++{ ++ struct nand_chip *nand = &nfc->nand; ++ u32 decnum, num_error_bits, fdm_end_bits; ++ u32 error_locations, error_bit_loc; ++ u32 error_byte_pos, error_bit_pos; ++ int bitflips = 0; ++ u32 i; ++ ++ decnum = ecc_read32(nfc, ECC_DECENUM); ++ num_error_bits = (decnum >> (sect << ERRNUM_S)) & ERRNUM_M; ++ fdm_end_bits = (nand->ecc.size + NFI_FDM_SIZE) << 3; ++ ++ if (!num_error_bits) ++ return 0; ++ ++ if (num_error_bits == ERRNUM_M) ++ return -1; ++ ++ for (i = 0; i < num_error_bits; i++) { ++ error_locations = ecc_read32(nfc, ECC_DECEL(i / 2)); ++ error_bit_loc = (error_locations >> ((i % 2) * DEC_EL_ODD_S)) & ++ DEC_EL_M; ++ error_byte_pos = error_bit_loc >> DEC_EL_BYTE_POS_S; ++ error_bit_pos = error_bit_loc & DEC_EL_BIT_POS_M; ++ ++ if (error_bit_loc < (nand->ecc.size << 3)) { ++ if (sector_buf) { ++ sector_buf[error_byte_pos] ^= ++ (1 << error_bit_pos); ++ } ++ } else if (error_bit_loc < fdm_end_bits) { ++ if (fdm_buf) { ++ fdm_buf[error_byte_pos - nand->ecc.size] ^= ++ (1 << error_bit_pos); ++ } ++ } ++ ++ bitflips++; ++ } ++ ++ return bitflips; ++} ++ ++static int mt7621_nfc_wait_write_completion(struct mt7621_nfc *nfc, ++ struct nand_chip *nand) ++{ ++ struct device *dev = nfc->dev; ++ u16 val; ++ int ret; ++ ++ ret = readw_poll_timeout_atomic(nfc->nfi_regs + NFI_ADDRCNTR, val, ++ ((val & SEC_CNTR_M) >> SEC_CNTR_S) >= nand->ecc.steps, 10, ++ NFI_CORE_TIMEOUT); ++ ++ if (ret) { ++ dev_warn(dev, "NFI core write operation timed out\n"); ++ return -ETIMEDOUT; ++ } ++ ++ return ret; ++} ++ ++static void mt7621_nfc_hw_reset(struct mt7621_nfc *nfc) ++{ ++ u32 val; ++ int ret; ++ ++ /* reset all registers and force the NFI master to terminate */ ++ nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST); ++ ++ /* wait for the master to finish the last transaction */ ++ ret = readw_poll_timeout(nfc->nfi_regs + NFI_MASTER_STA, val, ++ !(val & MASTER_STA_MASK), 50, ++ NFI_RESET_TIMEOUT); ++ if (ret) { ++ dev_warn(nfc->dev, "Failed to reset NFI master in %dms\n", ++ NFI_RESET_TIMEOUT); ++ } ++ ++ /* ensure any status register affected by the NFI master is reset */ ++ nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST); ++ nfi_write16(nfc, NFI_STRDATA, 0); ++} ++ ++static inline void mt7621_nfc_hw_init(struct mt7621_nfc *nfc) ++{ ++ u32 acccon; ++ ++ /* ++ * CNRNB: nand ready/busy register ++ * ------------------------------- ++ * 7:4: timeout register for polling the NAND busy/ready signal ++ * 0 : poll the status of the busy/ready signal after [7:4]*16 cycles. ++ */ ++ nfi_write16(nfc, NFI_CNRNB, CB2R_TIME_M | STR_CNRNB); ++ ++ mt7621_nfc_hw_reset(nfc); ++ ++ /* Apply default access timing */ ++ acccon = ACCTIMING(ACCCON_POECS_DEF, ACCCON_PRECS_DEF, ACCCON_C2R_DEF, ++ ACCCON_W2R_DEF, ACCCON_WH_DEF, ACCCON_WST_DEF, ++ ACCCON_RLT_DEF); ++ ++ nfi_write32(nfc, NFI_ACCCON, acccon); ++} ++ ++static int mt7621_nfc_send_command(struct mt7621_nfc *nfc, u8 command) ++{ ++ struct device *dev = nfc->dev; ++ u32 val; ++ int ret; ++ ++ nfi_write32(nfc, NFI_CMD, command); ++ ++ ret = readl_poll_timeout_atomic(nfc->nfi_regs + NFI_STA, val, ++ !(val & STA_CMD), 10, ++ NFI_CORE_TIMEOUT); ++ if (ret) { ++ dev_warn(dev, "NFI core timed out entering command mode\n"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int mt7621_nfc_send_address_byte(struct mt7621_nfc *nfc, int addr) ++{ ++ struct device *dev = nfc->dev; ++ u32 val; ++ int ret; ++ ++ nfi_write32(nfc, NFI_COLADDR, addr); ++ nfi_write32(nfc, NFI_ROWADDR, 0); ++ nfi_write16(nfc, NFI_ADDRNOB, 1); ++ ++ ret = readl_poll_timeout_atomic(nfc->nfi_regs + NFI_STA, val, ++ !(val & STA_ADDR), 10, ++ NFI_CORE_TIMEOUT); ++ if (ret) { ++ dev_warn(dev, "NFI core timed out entering address mode\n"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int mt7621_nfc_send_address(struct mt7621_nfc *nfc, const u8 *addr, ++ unsigned int naddrs) ++{ ++ int ret; ++ ++ while (naddrs) { ++ ret = mt7621_nfc_send_address_byte(nfc, *addr); ++ if (ret) ++ return ret; ++ ++ addr++; ++ naddrs--; ++ } ++ ++ return 0; ++} ++ ++static void mt7621_nfc_wait_pio_ready(struct mt7621_nfc *nfc) ++{ ++ struct device *dev = nfc->dev; ++ int ret; ++ u16 val; ++ ++ ret = readw_poll_timeout_atomic(nfc->nfi_regs + NFI_PIO_DIRDY, val, ++ val & PIO_DIRDY, 10, ++ NFI_CORE_TIMEOUT); ++ if (ret < 0) ++ dev_err(dev, "NFI core PIO mode not ready\n"); ++} ++ ++static u32 mt7621_nfc_pio_read(struct mt7621_nfc *nfc, bool br) ++{ ++ u32 reg; ++ ++ /* after each byte read, the NFI_STA reg is reset by the hardware */ ++ reg = (nfi_read32(nfc, NFI_STA) & STA_NFI_FSM_M) >> STA_NFI_FSM_S; ++ if (reg != STA_FSM_CUSTOM_DATA) { ++ reg = nfi_read16(nfc, NFI_CNFG); ++ reg |= CNFG_READ_MODE | CNFG_BYTE_RW; ++ if (!br) ++ reg &= ~CNFG_BYTE_RW; ++ nfi_write16(nfc, NFI_CNFG, reg); ++ ++ /* ++ * set to max sector to allow the HW to continue reading over ++ * unaligned accesses ++ */ ++ nfi_write16(nfc, NFI_CON, CON_NFI_SEC_M | CON_NFI_BRD); ++ ++ /* trigger to fetch data */ ++ nfi_write16(nfc, NFI_STRDATA, STR_DATA); ++ } ++ ++ mt7621_nfc_wait_pio_ready(nfc); ++ ++ return nfi_read32(nfc, NFI_DATAR); ++} ++ ++static void mt7621_nfc_read_data(struct mt7621_nfc *nfc, u8 *buf, u32 len) ++{ ++ while (((uintptr_t)buf & 3) && len) { ++ *buf = mt7621_nfc_pio_read(nfc, true); ++ buf++; ++ len--; ++ } ++ ++ while (len >= 4) { ++ *(u32 *)buf = mt7621_nfc_pio_read(nfc, false); ++ buf += 4; ++ len -= 4; ++ } ++ ++ while (len) { ++ *buf = mt7621_nfc_pio_read(nfc, true); ++ buf++; ++ len--; ++ } ++} ++ ++static void mt7621_nfc_read_data_discard(struct mt7621_nfc *nfc, u32 len) ++{ ++ while (len >= 4) { ++ mt7621_nfc_pio_read(nfc, false); ++ len -= 4; ++ } ++ ++ while (len) { ++ mt7621_nfc_pio_read(nfc, true); ++ len--; ++ } ++} ++ ++static void mt7621_nfc_pio_write(struct mt7621_nfc *nfc, u32 val, bool bw) ++{ ++ u32 reg; ++ ++ reg = (nfi_read32(nfc, NFI_STA) & STA_NFI_FSM_M) >> STA_NFI_FSM_S; ++ if (reg != STA_FSM_CUSTOM_DATA) { ++ reg = nfi_read16(nfc, NFI_CNFG); ++ reg &= ~(CNFG_READ_MODE | CNFG_BYTE_RW); ++ if (bw) ++ reg |= CNFG_BYTE_RW; ++ nfi_write16(nfc, NFI_CNFG, reg); ++ ++ nfi_write16(nfc, NFI_CON, CON_NFI_SEC_M | CON_NFI_BWR); ++ nfi_write16(nfc, NFI_STRDATA, STR_DATA); ++ } ++ ++ mt7621_nfc_wait_pio_ready(nfc); ++ nfi_write32(nfc, NFI_DATAW, val); ++} ++ ++static void mt7621_nfc_write_data(struct mt7621_nfc *nfc, const u8 *buf, ++ u32 len) ++{ ++ while (((uintptr_t)buf & 3) && len) { ++ mt7621_nfc_pio_write(nfc, *buf, true); ++ buf++; ++ len--; ++ } ++ ++ while (len >= 4) { ++ mt7621_nfc_pio_write(nfc, *(const u32 *)buf, false); ++ buf += 4; ++ len -= 4; ++ } ++ ++ while (len) { ++ mt7621_nfc_pio_write(nfc, *buf, true); ++ buf++; ++ len--; ++ } ++} ++ ++static void mt7621_nfc_write_data_empty(struct mt7621_nfc *nfc, u32 len) ++{ ++ while (len >= 4) { ++ mt7621_nfc_pio_write(nfc, 0xffffffff, false); ++ len -= 4; ++ } ++ ++ while (len) { ++ mt7621_nfc_pio_write(nfc, 0xff, true); ++ len--; ++ } ++} ++ ++static int mt7621_nfc_dev_ready(struct mt7621_nfc *nfc, ++ unsigned int timeout_ms) ++{ ++ u32 val; ++ ++ return readl_poll_timeout_atomic(nfc->nfi_regs + NFI_STA, val, ++ !(val & STA_BUSY), 10, ++ timeout_ms * 1000); ++} ++ ++static int mt7621_nfc_exec_instr(struct nand_chip *nand, ++ const struct nand_op_instr *instr) ++{ ++ struct mt7621_nfc *nfc = nand_get_controller_data(nand); ++ ++ switch (instr->type) { ++ case NAND_OP_CMD_INSTR: ++ mt7621_nfc_hw_reset(nfc); ++ nfi_write16(nfc, NFI_CNFG, CNFG_OP_CUSTOM << CNFG_OP_MODE_S); ++ return mt7621_nfc_send_command(nfc, instr->ctx.cmd.opcode); ++ case NAND_OP_ADDR_INSTR: ++ return mt7621_nfc_send_address(nfc, instr->ctx.addr.addrs, ++ instr->ctx.addr.naddrs); ++ case NAND_OP_DATA_IN_INSTR: ++ mt7621_nfc_read_data(nfc, instr->ctx.data.buf.in, ++ instr->ctx.data.len); ++ return 0; ++ case NAND_OP_DATA_OUT_INSTR: ++ mt7621_nfc_write_data(nfc, instr->ctx.data.buf.out, ++ instr->ctx.data.len); ++ return 0; ++ case NAND_OP_WAITRDY_INSTR: ++ return mt7621_nfc_dev_ready(nfc, ++ instr->ctx.waitrdy.timeout_ms); ++ default: ++ WARN_ONCE(1, "unsupported NAND instruction type: %d\n", ++ instr->type); ++ ++ return -EINVAL; ++ } ++} ++ ++static int mt7621_nfc_exec_op(struct nand_chip *nand, ++ const struct nand_operation *op, bool check_only) ++{ ++ struct mt7621_nfc *nfc = nand_get_controller_data(nand); ++ int i, ret; ++ ++ if (check_only) ++ return 0; ++ ++ /* Only CS0 available */ ++ nfi_write16(nfc, NFI_CSEL, 0); ++ ++ for (i = 0; i < op->ninstrs; i++) { ++ ret = mt7621_nfc_exec_instr(nand, &op->instrs[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int mt7621_nfc_setup_data_interface(struct nand_chip *nand, int csline, ++ const struct nand_data_interface *conf) ++{ ++ struct mt7621_nfc *nfc = nand_get_controller_data(nand); ++ const struct nand_sdr_timings *timings; ++ u32 acccon, temp, rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt; ++ ++ if (!nfc->nfi_clk) ++ return -ENOTSUPP; ++ ++ timings = nand_get_sdr_timings(conf); ++ if (IS_ERR(timings)) ++ return -ENOTSUPP; ++ ++ rate = clk_get_rate(nfc->nfi_clk); ++ ++ /* turn clock rate into KHZ */ ++ rate /= 1000; ++ ++ tpoecs = max(timings->tALH_min, timings->tCLH_min) / 1000; ++ tpoecs = DIV_ROUND_UP(tpoecs * rate, 1000000); ++ tpoecs = min_t(u32, tpoecs, ACCCON_POECS_MAX); ++ ++ tprecs = max(timings->tCLS_min, timings->tALS_min) / 1000; ++ tprecs = DIV_ROUND_UP(tprecs * rate, 1000000); ++ tprecs = min_t(u32, tprecs, ACCCON_PRECS_MAX); ++ ++ /* sdr interface has no tCR which means CE# low to RE# low */ ++ tc2r = 0; ++ ++ tw2r = timings->tWHR_min / 1000; ++ tw2r = DIV_ROUND_UP(tw2r * rate, 1000000); ++ tw2r = DIV_ROUND_UP(tw2r - 1, 2); ++ tw2r = min_t(u32, tw2r, ACCCON_W2R_MAX); ++ ++ twh = max(timings->tREH_min, timings->tWH_min) / 1000; ++ twh = DIV_ROUND_UP(twh * rate, 1000000) - 1; ++ twh = min_t(u32, twh, ACCCON_WH_MAX); ++ ++ /* Calculate real WE#/RE# hold time in nanosecond */ ++ temp = (twh + 1) * 1000000 / rate; ++ /* nanosecond to picosecond */ ++ temp *= 1000; ++ ++ /* ++ * WE# low level time should be expaned to meet WE# pulse time ++ * and WE# cycle time at the same time. ++ */ ++ if (temp < timings->tWC_min) ++ twst = timings->tWC_min - temp; ++ else ++ twst = 0; ++ twst = max(timings->tWP_min, twst) / 1000; ++ twst = DIV_ROUND_UP(twst * rate, 1000000) - 1; ++ twst = min_t(u32, twst, ACCCON_WST_MAX); ++ ++ /* ++ * RE# low level time should be expaned to meet RE# pulse time ++ * and RE# cycle time at the same time. ++ */ ++ if (temp < timings->tRC_min) ++ trlt = timings->tRC_min - temp; ++ else ++ trlt = 0; ++ trlt = max(trlt, timings->tRP_min) / 1000; ++ trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1; ++ trlt = min_t(u32, trlt, ACCCON_RLT_MAX); ++ ++ if (csline == NAND_DATA_IFACE_CHECK_ONLY) { ++ if (twst < ACCCON_WST_MIN || trlt < ACCCON_RLT_MIN) ++ return -ENOTSUPP; ++ } ++ ++ acccon = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt); ++ ++ dev_info(nfc->dev, "Using programmed access timing: %08x\n", acccon); ++ ++ nfi_write32(nfc, NFI_ACCCON, acccon); ++ ++ return 0; ++} ++ ++static int mt7621_nfc_calc_ecc_strength(struct mt7621_nfc *nfc, ++ u32 avail_ecc_bytes) ++{ ++ struct nand_chip *nand = &nfc->nand; ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ u32 strength; ++ int i; ++ ++ strength = avail_ecc_bytes * 8 / ECC_PARITY_BITS; ++ ++ /* Find the closest supported ecc strength */ ++ for (i = ARRAY_SIZE(mt7621_ecc_strength) - 1; i >= 0; i--) { ++ if (mt7621_ecc_strength[i] <= strength) ++ break; ++ } ++ ++ if (unlikely(i < 0)) { ++ dev_err(nfc->dev, "OOB size (%u) is not supported\n", ++ mtd->oobsize); ++ return -EINVAL; ++ } ++ ++ nand->ecc.strength = mt7621_ecc_strength[i]; ++ nand->ecc.bytes = ++ DIV_ROUND_UP(nand->ecc.strength * ECC_PARITY_BITS, 8); ++ ++ dev_info(nfc->dev, "ECC strength adjusted to %u bits\n", ++ nand->ecc.strength); ++ ++ return i; ++} ++ ++static int mt7621_nfc_set_spare_per_sector(struct mt7621_nfc *nfc) ++{ ++ struct nand_chip *nand = &nfc->nand; ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ u32 size; ++ int i; ++ ++ size = nand->ecc.bytes + NFI_FDM_SIZE; ++ ++ /* Find the closest supported spare size */ ++ for (i = 0; i < ARRAY_SIZE(mt7621_nfi_spare_size); i++) { ++ if (mt7621_nfi_spare_size[i] >= size) ++ break; ++ } ++ ++ if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_spare_size))) { ++ dev_err(nfc->dev, "OOB size (%u) is not supported\n", ++ mtd->oobsize); ++ return -EINVAL; ++ } ++ ++ nfc->spare_per_sector = mt7621_nfi_spare_size[i]; ++ ++ return i; ++} ++ ++static int mt7621_nfc_ecc_init(struct mt7621_nfc *nfc) ++{ ++ struct nand_chip *nand = &nfc->nand; ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ u32 spare_per_sector, encode_block_size, decode_block_size; ++ u32 ecc_enccfg, ecc_deccfg; ++ int ecc_cap; ++ ++ /* Only hardware ECC mode is supported */ ++ if (nand->ecc.mode != NAND_ECC_HW_SYNDROME) { ++ dev_err(nfc->dev, "Only hardware ECC mode is supported\n"); ++ return -EINVAL; ++ } ++ ++ nand->ecc.size = ECC_SECTOR_SIZE; ++ nand->ecc.steps = mtd->writesize / nand->ecc.size; ++ ++ spare_per_sector = mtd->oobsize / nand->ecc.steps; ++ ++ ecc_cap = mt7621_nfc_calc_ecc_strength(nfc, ++ spare_per_sector - NFI_FDM_SIZE); ++ if (ecc_cap < 0) ++ return ecc_cap; ++ ++ /* Sector + FDM */ ++ encode_block_size = (nand->ecc.size + NFI_FDM_SIZE) * 8; ++ ecc_enccfg = ecc_cap | (ENC_MODE_NFI << ENC_MODE_S) | ++ (encode_block_size << ENC_CNFG_MSG_S); ++ ++ /* Sector + FDM + ECC parity bits */ ++ decode_block_size = ((nand->ecc.size + NFI_FDM_SIZE) * 8) + ++ nand->ecc.strength * ECC_PARITY_BITS; ++ ecc_deccfg = ecc_cap | (DEC_MODE_NFI << DEC_MODE_S) | ++ (decode_block_size << DEC_CS_S) | ++ (DEC_CON_EL << DEC_CON_S) | DEC_EMPTY_EN; ++ ++ mt7621_ecc_encoder_op(nfc, false); ++ ecc_write32(nfc, ECC_ENCCNFG, ecc_enccfg); ++ ++ mt7621_ecc_decoder_op(nfc, false); ++ ecc_write32(nfc, ECC_DECCNFG, ecc_deccfg); ++ ++ return 0; ++} ++ ++static int mt7621_nfc_set_page_format(struct mt7621_nfc *nfc) ++{ ++ struct nand_chip *nand = &nfc->nand; ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ int i, spare_size; ++ u32 pagefmt; ++ ++ spare_size = mt7621_nfc_set_spare_per_sector(nfc); ++ if (spare_size < 0) ++ return spare_size; ++ ++ for (i = 0; i < ARRAY_SIZE(mt7621_nfi_page_size); i++) { ++ if (mt7621_nfi_page_size[i] == mtd->writesize) ++ break; ++ } ++ ++ if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_page_size))) { ++ dev_err(nfc->dev, "Page size (%u) is not supported\n", ++ mtd->writesize); ++ return -EINVAL; ++ } ++ ++ pagefmt = i | (spare_size << PAGEFMT_SPARE_S) | ++ (NFI_FDM_SIZE << PAGEFMT_FDM_S) | ++ (NFI_FDM_SIZE << PAGEFMT_FDM_ECC_S); ++ ++ nfi_write16(nfc, NFI_PAGEFMT, pagefmt); ++ ++ return 0; ++} ++ ++static int mt7621_nfc_attach_chip(struct nand_chip *nand) ++{ ++ struct mt7621_nfc *nfc = nand_get_controller_data(nand); ++ int ret; ++ ++ if (nand->options & NAND_BUSWIDTH_16) { ++ dev_err(nfc->dev, "16-bit buswidth is not supported"); ++ return -EINVAL; ++ } ++ ++ ret = mt7621_nfc_ecc_init(nfc); ++ if (ret) ++ return ret; ++ ++ return mt7621_nfc_set_page_format(nfc); ++} ++ ++static const struct nand_controller_ops mt7621_nfc_controller_ops = { ++ .attach_chip = mt7621_nfc_attach_chip, ++ .exec_op = mt7621_nfc_exec_op, ++ .setup_data_interface = mt7621_nfc_setup_data_interface, ++}; ++ ++static int mt7621_nfc_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oob_region) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ ++ if (section >= nand->ecc.steps) ++ return -ERANGE; ++ ++ oob_region->length = NFI_FDM_SIZE - 1; ++ oob_region->offset = section * NFI_FDM_SIZE + 1; ++ ++ return 0; ++} ++ ++static int mt7621_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oob_region) ++{ ++ struct nand_chip *nand = mtd_to_nand(mtd); ++ ++ if (section) ++ return -ERANGE; ++ ++ oob_region->offset = NFI_FDM_SIZE * nand->ecc.steps; ++ oob_region->length = mtd->oobsize - oob_region->offset; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops mt7621_nfc_ooblayout_ops = { ++ .free = mt7621_nfc_ooblayout_free, ++ .ecc = mt7621_nfc_ooblayout_ecc, ++}; ++ ++static void mt7621_nfc_write_fdm(struct mt7621_nfc *nfc) ++{ ++ struct nand_chip *nand = &nfc->nand; ++ u32 vall, valm; ++ u8 *oobptr; ++ int i, j; ++ ++ for (i = 0; i < nand->ecc.steps; i++) { ++ vall = 0; ++ valm = 0; ++ oobptr = oob_fdm_ptr(nand, i); ++ ++ for (j = 0; j < 4; j++) ++ vall |= (u32)oobptr[j] << (j * 8); ++ ++ for (j = 0; j < 4; j++) ++ valm |= (u32)oobptr[j + 4] << ((j - 4) * 8); ++ ++ nfi_write32(nfc, NFI_FDML(i), vall); ++ nfi_write32(nfc, NFI_FDMM(i), valm); ++ } ++} ++ ++static void mt7621_nfc_read_sector_fdm(struct mt7621_nfc *nfc, u32 sect) ++{ ++ struct nand_chip *nand = &nfc->nand; ++ u32 vall, valm; ++ u8 *oobptr; ++ int i; ++ ++ vall = nfi_read32(nfc, NFI_FDML(sect)); ++ valm = nfi_read32(nfc, NFI_FDMM(sect)); ++ oobptr = oob_fdm_ptr(nand, sect); ++ ++ for (i = 0; i < 4; i++) ++ oobptr[i] = (vall >> (i * 8)) & 0xff; ++ ++ for (i = 0; i < 4; i++) ++ oobptr[i + 4] = (valm >> (i * 8)) & 0xff; ++} ++ ++static int mt7621_nfc_read_page_hwecc(struct nand_chip *nand, uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct mt7621_nfc *nfc = nand_get_controller_data(nand); ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ int bitflips = 0; ++ int rc, i; ++ ++ nand_read_page_op(nand, page, 0, NULL, 0); ++ ++ nfi_write16(nfc, NFI_CNFG, (CNFG_OP_CUSTOM << CNFG_OP_MODE_S) | ++ CNFG_READ_MODE | CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); ++ ++ mt7621_ecc_decoder_op(nfc, true); ++ ++ nfi_write16(nfc, NFI_CON, ++ CON_NFI_BRD | (nand->ecc.steps << CON_NFI_SEC_S)); ++ ++ for (i = 0; i < nand->ecc.steps; i++) { ++ if (buf) ++ mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i), ++ nand->ecc.size); ++ else ++ mt7621_nfc_read_data_discard(nfc, nand->ecc.size); ++ ++ rc = mt7621_ecc_decoder_wait_done(nfc, i); ++ ++ mt7621_nfc_read_sector_fdm(nfc, i); ++ ++ if (rc < 0) { ++ bitflips = -EIO; ++ continue; ++ } ++ ++ rc = mt7621_ecc_correct_check(nfc, ++ buf ? page_data_ptr(nand, buf, i) : NULL, ++ oob_fdm_ptr(nand, i), i); ++ ++ if (rc < 0) { ++ dev_warn(nfc->dev, ++ "Uncorrectable ECC error at page %d.%d\n", ++ page, i); ++ bitflips = -EBADMSG; ++ mtd->ecc_stats.failed++; ++ } else if (bitflips >= 0) { ++ bitflips += rc; ++ mtd->ecc_stats.corrected += rc; ++ } ++ } ++ ++ mt7621_ecc_decoder_op(nfc, false); ++ ++ nfi_write16(nfc, NFI_CON, 0); ++ ++ return bitflips; ++} ++ ++static int mt7621_nfc_read_page_raw(struct nand_chip *nand, uint8_t *buf, ++ int oob_required, int page) ++{ ++ struct mt7621_nfc *nfc = nand_get_controller_data(nand); ++ int i; ++ ++ nand_read_page_op(nand, page, 0, NULL, 0); ++ ++ nfi_write16(nfc, NFI_CNFG, (CNFG_OP_CUSTOM << CNFG_OP_MODE_S) | ++ CNFG_READ_MODE); ++ ++ nfi_write16(nfc, NFI_CON, ++ CON_NFI_BRD | (nand->ecc.steps << CON_NFI_SEC_S)); ++ ++ for (i = 0; i < nand->ecc.steps; i++) { ++ /* Read data */ ++ if (buf) ++ mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i), ++ nand->ecc.size); ++ else ++ mt7621_nfc_read_data_discard(nfc, nand->ecc.size); ++ ++ /* Read FDM */ ++ mt7621_nfc_read_data(nfc, oob_fdm_ptr(nand, i), NFI_FDM_SIZE); ++ ++ /* Read ECC parity data */ ++ mt7621_nfc_read_data(nfc, oob_ecc_ptr(nfc, i), ++ nfc->spare_per_sector - NFI_FDM_SIZE); ++ } ++ ++ nfi_write16(nfc, NFI_CON, 0); ++ ++ return 0; ++} ++ ++static int mt7621_nfc_read_oob_hwecc(struct nand_chip *nand, int page) ++{ ++ return mt7621_nfc_read_page_hwecc(nand, NULL, 1, page); ++} ++ ++static int mt7621_nfc_read_oob_raw(struct nand_chip *nand, int page) ++{ ++ return mt7621_nfc_read_page_raw(nand, NULL, 1, page); ++} ++ ++static int mt7621_nfc_check_empty_page(struct nand_chip *nand, const u8 *buf) ++{ ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ uint32_t i, j; ++ u8 *oobptr; ++ ++ if (buf) { ++ for (i = 0; i < mtd->writesize; i++) ++ if (buf[i] != 0xff) ++ return 0; ++ } ++ ++ for (i = 0; i < nand->ecc.steps; i++) { ++ oobptr = oob_fdm_ptr(nand, i); ++ for (j = 0; j < NFI_FDM_SIZE; j++) ++ if (oobptr[j] != 0xff) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static int mt7621_nfc_write_page_hwecc(struct nand_chip *nand, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ struct mt7621_nfc *nfc = nand_get_controller_data(nand); ++ struct mtd_info *mtd = nand_to_mtd(nand); ++ ++ if (mt7621_nfc_check_empty_page(nand, buf)) { ++ /* ++ * MT7621 ECC engine always generates parity code for input ++ * pages, even for empty pages. Doing so will write back ECC ++ * parity code to the oob region, which means such pages will ++ * no longer be empty pages. ++ * ++ * To avoid this, stop write operation if current page is an ++ * empty page. ++ */ ++ return 0; ++ } ++ ++ nand_prog_page_begin_op(nand, page, 0, NULL, 0); ++ ++ nfi_write16(nfc, NFI_CNFG, (CNFG_OP_CUSTOM << CNFG_OP_MODE_S) | ++ CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); ++ ++ mt7621_ecc_encoder_op(nfc, true); ++ ++ mt7621_nfc_write_fdm(nfc); ++ ++ nfi_write16(nfc, NFI_CON, ++ CON_NFI_BWR | (nand->ecc.steps << CON_NFI_SEC_S)); ++ ++ if (buf) ++ mt7621_nfc_write_data(nfc, buf, mtd->writesize); ++ else ++ mt7621_nfc_write_data_empty(nfc, mtd->writesize); ++ ++ mt7621_nfc_wait_write_completion(nfc, nand); ++ ++ mt7621_ecc_encoder_op(nfc, false); ++ ++ nfi_write16(nfc, NFI_CON, 0); ++ ++ return nand_prog_page_end_op(nand); ++} ++ ++static int mt7621_nfc_write_page_raw(struct nand_chip *nand, ++ const uint8_t *buf, int oob_required, ++ int page) ++{ ++ struct mt7621_nfc *nfc = nand_get_controller_data(nand); ++ int i; ++ ++ nand_prog_page_begin_op(nand, page, 0, NULL, 0); ++ ++ nfi_write16(nfc, NFI_CNFG, (CNFG_OP_CUSTOM << CNFG_OP_MODE_S)); ++ ++ nfi_write16(nfc, NFI_CON, ++ CON_NFI_BWR | (nand->ecc.steps << CON_NFI_SEC_S)); ++ ++ for (i = 0; i < nand->ecc.steps; i++) { ++ /* Write data */ ++ if (buf) ++ mt7621_nfc_write_data(nfc, page_data_ptr(nand, buf, i), ++ nand->ecc.size); ++ else ++ mt7621_nfc_write_data_empty(nfc, nand->ecc.size); ++ ++ /* Write FDM */ ++ mt7621_nfc_write_data(nfc, oob_fdm_ptr(nand, i), ++ NFI_FDM_SIZE); ++ ++ /* Write dummy ECC parity data */ ++ mt7621_nfc_write_data_empty(nfc, nfc->spare_per_sector - ++ NFI_FDM_SIZE); ++ } ++ ++ mt7621_nfc_wait_write_completion(nfc, nand); ++ ++ nfi_write16(nfc, NFI_CON, 0); ++ ++ return nand_prog_page_end_op(nand); ++} ++ ++static int mt7621_nfc_write_oob_hwecc(struct nand_chip *nand, int page) ++{ ++ return mt7621_nfc_write_page_hwecc(nand, NULL, 1, page); ++} ++ ++static int mt7621_nfc_write_oob_raw(struct nand_chip *nand, int page) ++{ ++ return mt7621_nfc_write_page_raw(nand, NULL, 1, page); ++} ++ ++static int mt7621_nfc_init_chip(struct mt7621_nfc *nfc) ++{ ++ struct nand_chip *nand = &nfc->nand; ++ struct mtd_info *mtd; ++ int ret; ++ ++ nand->controller = &nfc->controller; ++ nand_set_controller_data(nand, (void *)nfc); ++ nand_set_flash_node(nand, nfc->dev->of_node); ++ ++ nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_NO_SUBPAGE_WRITE; ++ if (!nfc->nfi_clk) ++ nand->options |= NAND_KEEP_TIMINGS; ++ ++ nand->ecc.mode = NAND_ECC_HW_SYNDROME; ++ nand->ecc.read_page = mt7621_nfc_read_page_hwecc; ++ nand->ecc.read_page_raw = mt7621_nfc_read_page_raw; ++ nand->ecc.write_page = mt7621_nfc_write_page_hwecc; ++ nand->ecc.write_page_raw = mt7621_nfc_write_page_raw; ++ nand->ecc.read_oob = mt7621_nfc_read_oob_hwecc; ++ nand->ecc.read_oob_raw = mt7621_nfc_read_oob_raw; ++ nand->ecc.write_oob = mt7621_nfc_write_oob_hwecc; ++ nand->ecc.write_oob_raw = mt7621_nfc_write_oob_raw; ++ ++ mtd = nand_to_mtd(nand); ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = nfc->dev; ++ mtd->name = MT7621_NFC_NAME; ++ mtd_set_ooblayout(mtd, &mt7621_nfc_ooblayout_ops); ++ ++ mt7621_nfc_hw_init(nfc); ++ ++ ret = nand_scan(nand, 1); ++ if (ret) ++ return ret; ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) { ++ dev_err(nfc->dev, "Failed to register MTD: %d\n", ret); ++ nand_release(nand); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int mt7621_nfc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mt7621_nfc *nfc; ++ struct resource *res; ++ int ret; ++ ++ nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); ++ if (!nfc) ++ return -ENOMEM; ++ ++ nand_controller_init(&nfc->controller); ++ nfc->controller.ops = &mt7621_nfc_controller_ops; ++ nfc->dev = dev; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nfi"); ++ nfc->nfi_regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(nfc->nfi_regs)) { ++ ret = PTR_ERR(nfc->nfi_regs); ++ return ret; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecc"); ++ nfc->ecc_regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(nfc->ecc_regs)) { ++ ret = PTR_ERR(nfc->ecc_regs); ++ return ret; ++ } ++ ++ nfc->nfi_clk = devm_clk_get(dev, "nfi_clk"); ++ if (IS_ERR(nfc->nfi_clk)) { ++ dev_warn(dev, "nfi clk not provided\n"); ++ nfc->nfi_clk = NULL; ++ } else { ++ ret = clk_prepare_enable(nfc->nfi_clk); ++ if (ret) { ++ dev_err(dev, "Failed to enable nfi core clock\n"); ++ return ret; ++ } ++ } ++ ++ platform_set_drvdata(pdev, nfc); ++ ++ ret = mt7621_nfc_init_chip(nfc); ++ if (ret) { ++ dev_err(dev, "Failed to initialize nand chip\n"); ++ goto clk_disable; ++ } ++ ++ return 0; ++ ++clk_disable: ++ clk_disable_unprepare(nfc->nfi_clk); ++ ++ return ret; ++} ++ ++static int mt7621_nfc_remove(struct platform_device *pdev) ++{ ++ struct mt7621_nfc *nfc = platform_get_drvdata(pdev); ++ ++ nand_release(&nfc->nand); ++ clk_disable_unprepare(nfc->nfi_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id mt7621_nfc_id_table[] = { ++ { .compatible = "mediatek,mt7621-nfc" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, match); ++ ++static struct platform_driver mt7621_nfc_driver = { ++ .probe = mt7621_nfc_probe, ++ .remove = mt7621_nfc_remove, ++ .driver = { ++ .name = MT7621_NFC_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = mt7621_nfc_id_table, ++ }, ++}; ++module_platform_driver(mt7621_nfc_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Weijie Gao <weijie.gao@mediatek.com>"); ++MODULE_DESCRIPTION("MediaTek MT7621 NAND Flash Controller driver"); diff --git a/target/linux/ramips/patches-5.10/0301-dt-bindings-add-documentation-for-mt7621-nand-driver.patch b/target/linux/ramips/patches-5.10/0301-dt-bindings-add-documentation-for-mt7621-nand-driver.patch new file mode 100644 index 0000000000..3d122c10c0 --- /dev/null +++ b/target/linux/ramips/patches-5.10/0301-dt-bindings-add-documentation-for-mt7621-nand-driver.patch @@ -0,0 +1,85 @@ +From 3d5f4da8296b23eb3abf8b13122b0d06a215e79c Mon Sep 17 00:00:00 2001 +From: Weijie Gao <weijie.gao@mediatek.com> +Date: Wed, 1 Apr 2020 02:07:59 +0800 +Subject: [PATCH 2/2] dt-bindings: add documentation for mt7621-nand driver + +This patch adds documentation for MediaTek MT7621 NAND flash controller +driver. + +Signed-off-by: Weijie Gao <weijie.gao@mediatek.com> +--- + .../bindings/mtd/mediatek,mt7621-nfc.yaml | 68 ++++++++++++++++++++++ + 1 file changed, 68 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/mediatek,mt7621-nfc.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/mediatek,mt7621-nfc.yaml +@@ -0,0 +1,68 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mtd/mediatek,mt7621-nfc.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: MediaTek MT7621 SoC NAND Flash Controller (NFC) DT binding ++ ++maintainers: ++ - Weijie Gao <weijie.gao@mediatek.com> ++ ++description: | ++ This driver uses a single node to describe both NAND Flash controller ++ interface (NFI) and ECC engine for MT7621 SoC. ++ MT7621 supports only one chip select. ++ ++properties: ++ "#address-cells": false ++ "#size-cells": false ++ ++ compatible: ++ enum: ++ - mediatek,mt7621-nfc ++ ++ reg: ++ items: ++ - description: Register base of NFI core ++ - description: Register base of ECC engine ++ ++ reg-names: ++ items: ++ - const: nfi ++ - const: ecc ++ ++ clocks: ++ items: ++ - description: Source clock for NFI core, fixed 125MHz ++ ++ clock-names: ++ items: ++ - const: nfi_clk ++ ++required: ++ - compatible ++ - reg ++ - reg-names ++ - clocks ++ - clock-names ++ ++examples: ++ - | ++ nficlock: nficlock { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ ++ clock-frequency = <125000000>; ++ }; ++ ++ nand@1e003000 { ++ compatible = "mediatek,mt7621-nfc"; ++ ++ reg = <0x1e003000 0x800 ++ 0x1e003800 0x800>; ++ reg-names = "nfi", "ecc"; ++ ++ clocks = <&nficlock>; ++ clock-names = "nfi_clk"; ++ }; diff --git a/target/linux/ramips/patches-5.10/100-mt7621-core-detect-hack.patch b/target/linux/ramips/patches-5.10/100-mt7621-core-detect-hack.patch new file mode 100644 index 0000000000..1c8b61f8ef --- /dev/null +++ b/target/linux/ramips/patches-5.10/100-mt7621-core-detect-hack.patch @@ -0,0 +1,61 @@ +There is a variant of MT7621 which contains only one CPU core instead of 2. +This is not reflected in the config register, so the kernel detects more +physical cores, which leads to a hang on SMP bringup. +Add a hack to detect missing cores. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> + +--- a/arch/mips/kernel/smp-cps.c ++++ b/arch/mips/kernel/smp-cps.c +@@ -43,6 +43,11 @@ static unsigned core_vpe_count(unsigned + return mips_cps_numvps(cluster, core); + } + ++bool __weak plat_cpu_core_present(int core) ++{ ++ return true; ++} ++ + static void __init cps_smp_setup(void) + { + unsigned int nclusters, ncores, nvpes, core_vpes; +@@ -60,6 +65,8 @@ static void __init cps_smp_setup(void) + + ncores = mips_cps_numcores(cl); + for (c = 0; c < ncores; c++) { ++ if (!plat_cpu_core_present(c)) ++ continue; + core_vpes = core_vpe_count(cl, c); + + if (c > 0) +--- a/arch/mips/ralink/mt7621.c ++++ b/arch/mips/ralink/mt7621.c +@@ -13,6 +13,7 @@ + #include <asm/mips-cps.h> + #include <asm/mach-ralink/ralink_regs.h> + #include <asm/mach-ralink/mt7621.h> ++#include <asm/mips-boards/launch.h> + + #include <pinmux.h> + +@@ -160,6 +161,20 @@ void __init ralink_of_remap(void) + panic("Failed to remap core resources"); + } + ++bool plat_cpu_core_present(int core) ++{ ++ struct cpulaunch *launch = (struct cpulaunch *)CKSEG0ADDR(CPULAUNCH); ++ ++ if (!core) ++ return true; ++ launch += core * 2; /* 2 VPEs per core */ ++ if (!(launch->flags & LAUNCH_FREADY)) ++ return false; ++ if (launch->flags & (LAUNCH_FGO | LAUNCH_FGONE)) ++ return false; ++ return true; ++} ++ + void prom_soc_init(struct ralink_soc_info *soc_info) + { + void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7621_SYSC_BASE); diff --git a/target/linux/ramips/patches-5.10/101-mt7621-timer.patch b/target/linux/ramips/patches-5.10/101-mt7621-timer.patch new file mode 100644 index 0000000000..8528b71c88 --- /dev/null +++ b/target/linux/ramips/patches-5.10/101-mt7621-timer.patch @@ -0,0 +1,87 @@ +--- a/arch/mips/ralink/mt7621.c ++++ b/arch/mips/ralink/mt7621.c +@@ -7,6 +7,7 @@ + + #include <linux/kernel.h> + #include <linux/init.h> ++#include <linux/jiffies.h> + + #include <asm/mipsregs.h> + #include <asm/smp-ops.h> +@@ -14,6 +15,7 @@ + #include <asm/mach-ralink/ralink_regs.h> + #include <asm/mach-ralink/mt7621.h> + #include <asm/mips-boards/launch.h> ++#include <asm/delay.h> + + #include <pinmux.h> + +@@ -175,6 +177,58 @@ bool plat_cpu_core_present(int core) + return true; + } + ++#define LPS_PREC 8 ++/* ++* Re-calibration lpj(loop-per-jiffy). ++* (derived from kernel/calibrate.c) ++*/ ++static int udelay_recal(void) ++{ ++ unsigned int i, lpj = 0; ++ unsigned long ticks, loopbit; ++ int lps_precision = LPS_PREC; ++ ++ lpj = (1<<12); ++ ++ while ((lpj <<= 1) != 0) { ++ /* wait for "start of" clock tick */ ++ ticks = jiffies; ++ while (ticks == jiffies) ++ /* nothing */; ++ ++ /* Go .. */ ++ ticks = jiffies; ++ __delay(lpj); ++ ticks = jiffies - ticks; ++ if (ticks) ++ break; ++ } ++ ++ /* ++ * Do a binary approximation to get lpj set to ++ * equal one clock (up to lps_precision bits) ++ */ ++ lpj >>= 1; ++ loopbit = lpj; ++ while (lps_precision-- && (loopbit >>= 1)) { ++ lpj |= loopbit; ++ ticks = jiffies; ++ while (ticks == jiffies) ++ /* nothing */; ++ ticks = jiffies; ++ __delay(lpj); ++ if (jiffies != ticks) /* longer than 1 tick */ ++ lpj &= ~loopbit; ++ } ++ printk(KERN_INFO "%d CPUs re-calibrate udelay(lpj = %d)\n", NR_CPUS, lpj); ++ ++ for(i=0; i< NR_CPUS; i++) ++ cpu_data[i].udelay_val = lpj; ++ ++ return 0; ++} ++device_initcall(udelay_recal); ++ + void prom_soc_init(struct ralink_soc_info *soc_info) + { + void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7621_SYSC_BASE); +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -58,6 +58,7 @@ choice + select CLKSRC_MIPS_GIC + select HAVE_PCI if PCI_MT7621 + select WEAK_REORDERING_BEYOND_LLSC ++ select GENERIC_CLOCKEVENTS_BROADCAST + endchoice + + choice diff --git a/target/linux/ramips/patches-5.10/102-mt7621-fix-cpu-clk-add-clkdev.patch b/target/linux/ramips/patches-5.10/102-mt7621-fix-cpu-clk-add-clkdev.patch new file mode 100644 index 0000000000..0c997a3f28 --- /dev/null +++ b/target/linux/ramips/patches-5.10/102-mt7621-fix-cpu-clk-add-clkdev.patch @@ -0,0 +1,224 @@ +--- a/arch/mips/include/asm/mach-ralink/mt7621.h ++++ b/arch/mips/include/asm/mach-ralink/mt7621.h +@@ -17,6 +17,10 @@ + #define SYSC_REG_CHIP_REV 0x0c + #define SYSC_REG_SYSTEM_CONFIG0 0x10 + #define SYSC_REG_SYSTEM_CONFIG1 0x14 ++#define SYSC_REG_CLKCFG0 0x2c ++#define SYSC_REG_CUR_CLK_STS 0x44 ++ ++#define MEMC_REG_CPU_PLL 0x648 + + #define CHIP_REV_PKG_MASK 0x1 + #define CHIP_REV_PKG_SHIFT 16 +@@ -24,6 +28,22 @@ + #define CHIP_REV_VER_SHIFT 8 + #define CHIP_REV_ECO_MASK 0xf + ++#define XTAL_MODE_SEL_MASK 0x7 ++#define XTAL_MODE_SEL_SHIFT 6 ++ ++#define CPU_CLK_SEL_MASK 0x3 ++#define CPU_CLK_SEL_SHIFT 30 ++ ++#define CUR_CPU_FDIV_MASK 0x1f ++#define CUR_CPU_FDIV_SHIFT 8 ++#define CUR_CPU_FFRAC_MASK 0x1f ++#define CUR_CPU_FFRAC_SHIFT 0 ++ ++#define CPU_PLL_PREDIV_MASK 0x3 ++#define CPU_PLL_PREDIV_SHIFT 12 ++#define CPU_PLL_FBDIV_MASK 0x7f ++#define CPU_PLL_FBDIV_SHIFT 4 ++ + #define MT7621_DRAM_BASE 0x0 + #define MT7621_DDR2_SIZE_MIN 32 + #define MT7621_DDR2_SIZE_MAX 256 +--- a/arch/mips/ralink/mt7621.c ++++ b/arch/mips/ralink/mt7621.c +@@ -8,6 +8,10 @@ + #include <linux/kernel.h> + #include <linux/init.h> + #include <linux/jiffies.h> ++#include <linux/clk.h> ++#include <linux/clkdev.h> ++#include <linux/clk-provider.h> ++#include <dt-bindings/clock/mt7621-clk.h> + + #include <asm/mipsregs.h> + #include <asm/smp-ops.h> +@@ -16,16 +20,12 @@ + #include <asm/mach-ralink/mt7621.h> + #include <asm/mips-boards/launch.h> + #include <asm/delay.h> ++#include <asm/time.h> + + #include <pinmux.h> + + #include "common.h" + +-#define SYSC_REG_SYSCFG 0x10 +-#define SYSC_REG_CPLL_CLKCFG0 0x2c +-#define SYSC_REG_CUR_CLK_STS 0x44 +-#define CPU_CLK_SEL (BIT(30) | BIT(31)) +- + #define MT7621_GPIO_MODE_UART1 1 + #define MT7621_GPIO_MODE_I2C 2 + #define MT7621_GPIO_MODE_UART3_MASK 0x3 +@@ -111,49 +111,89 @@ static struct rt2880_pmx_group mt7621_pi + { 0 } + }; + ++static struct clk *clks[MT7621_CLK_MAX]; ++static struct clk_onecell_data clk_data = { ++ .clks = clks, ++ .clk_num = ARRAY_SIZE(clks), ++}; ++ + phys_addr_t mips_cpc_default_phys_base(void) + { + panic("Cannot detect cpc address"); + } + +-void __init ralink_clk_init(void) ++static struct clk *__init mt7621_add_sys_clkdev( ++ const char *id, unsigned long rate) + { +- int cpu_fdiv = 0; +- int cpu_ffrac = 0; +- int fbdiv = 0; +- u32 clk_sts, syscfg; +- u8 clk_sel = 0, xtal_mode; +- u32 cpu_clk; ++ struct clk *clk; ++ int err; ++ ++ clk = clk_register_fixed_rate(NULL, id, NULL, 0, rate); ++ if (IS_ERR(clk)) ++ panic("failed to allocate %s clock structure", id); ++ ++ err = clk_register_clkdev(clk, id, NULL); ++ if (err) ++ panic("unable to register %s clock device", id); + +- if ((rt_sysc_r32(SYSC_REG_CPLL_CLKCFG0) & CPU_CLK_SEL) != 0) +- clk_sel = 1; ++ return clk; ++} ++ ++void __init ralink_clk_init(void) ++{ ++ u32 syscfg, xtal_sel, clkcfg, clk_sel, curclk, ffiv, ffrac; ++ u32 pll, prediv, fbdiv; ++ u32 xtal_clk, cpu_clk, bus_clk; ++ const static u32 prediv_tbl[] = {0, 1, 2, 2}; ++ ++ syscfg = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0); ++ xtal_sel = (syscfg >> XTAL_MODE_SEL_SHIFT) & XTAL_MODE_SEL_MASK; ++ ++ clkcfg = rt_sysc_r32(SYSC_REG_CLKCFG0); ++ clk_sel = (clkcfg >> CPU_CLK_SEL_SHIFT) & CPU_CLK_SEL_MASK; ++ ++ curclk = rt_sysc_r32(SYSC_REG_CUR_CLK_STS); ++ ffiv = (curclk >> CUR_CPU_FDIV_SHIFT) & CUR_CPU_FDIV_MASK; ++ ffrac = (curclk >> CUR_CPU_FFRAC_SHIFT) & CUR_CPU_FFRAC_MASK; ++ ++ if (xtal_sel <= 2) ++ xtal_clk = 20 * 1000 * 1000; ++ else if (xtal_sel <= 5) ++ xtal_clk = 40 * 1000 * 1000; ++ else ++ xtal_clk = 25 * 1000 * 1000; + + switch (clk_sel) { + case 0: +- clk_sts = rt_sysc_r32(SYSC_REG_CUR_CLK_STS); +- cpu_fdiv = ((clk_sts >> 8) & 0x1F); +- cpu_ffrac = (clk_sts & 0x1F); +- cpu_clk = (500 * cpu_ffrac / cpu_fdiv) * 1000 * 1000; ++ cpu_clk = 500 * 1000 * 1000; + break; +- + case 1: +- fbdiv = ((rt_sysc_r32(0x648) >> 4) & 0x7F) + 1; +- syscfg = rt_sysc_r32(SYSC_REG_SYSCFG); +- xtal_mode = (syscfg >> 6) & 0x7; +- if (xtal_mode >= 6) { +- /* 25Mhz Xtal */ +- cpu_clk = 25 * fbdiv * 1000 * 1000; +- } else if (xtal_mode >= 3) { +- /* 40Mhz Xtal */ +- cpu_clk = 40 * fbdiv * 1000 * 1000; +- } else { +- /* 20Mhz Xtal */ +- cpu_clk = 20 * fbdiv * 1000 * 1000; +- } ++ pll = rt_memc_r32(MEMC_REG_CPU_PLL); ++ fbdiv = (pll >> CPU_PLL_FBDIV_SHIFT) & CPU_PLL_FBDIV_MASK; ++ prediv = (pll >> CPU_PLL_PREDIV_SHIFT) & CPU_PLL_PREDIV_MASK; ++ cpu_clk = ((fbdiv + 1) * xtal_clk) >> prediv_tbl[prediv]; + break; ++ default: ++ cpu_clk = xtal_clk; + } ++ ++ cpu_clk = cpu_clk / ffiv * ffrac; ++ bus_clk = cpu_clk / 4; ++ ++ clks[MT7621_CLK_CPU] = mt7621_add_sys_clkdev("cpu", cpu_clk); ++ clks[MT7621_CLK_BUS] = mt7621_add_sys_clkdev("bus", bus_clk); ++ ++ pr_info("CPU Clock: %dMHz\n", cpu_clk / 1000000); ++ mips_hpt_frequency = cpu_clk / 2; + } + ++static void __init mt7621_clocks_init_dt(struct device_node *np) ++{ ++ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); ++} ++ ++CLK_OF_DECLARE(ar7100, "mediatek,mt7621-pll", mt7621_clocks_init_dt); ++ + void __init ralink_of_remap(void) + { + rt_sysc_membase = plat_of_remap_node("mtk,mt7621-sysc"); +--- a/arch/mips/ralink/timer-gic.c ++++ b/arch/mips/ralink/timer-gic.c +@@ -9,14 +9,14 @@ + + #include <linux/of.h> + #include <linux/clk-provider.h> +-#include <linux/clocksource.h> ++#include <asm/time.h> + + #include "common.h" + + void __init plat_time_init(void) + { + ralink_of_remap(); +- ++ ralink_clk_init(); + of_clk_init(NULL); + timer_probe(); + } +--- /dev/null ++++ b/include/dt-bindings/clock/mt7621-clk.h +@@ -0,0 +1,18 @@ ++/* ++ * Copyright (C) 2018 Weijie Gao <hackpascal@gmail.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef __DT_BINDINGS_MT7621_CLK_H ++#define __DT_BINDINGS_MT7621_CLK_H ++ ++#define MT7621_CLK_CPU 0 ++#define MT7621_CLK_BUS 1 ++ ++#define MT7621_CLK_MAX 2 ++ ++#endif /* __DT_BINDINGS_MT7621_CLK_H */ diff --git a/target/linux/ramips/patches-5.10/105-mt7621-memory-detect.patch b/target/linux/ramips/patches-5.10/105-mt7621-memory-detect.patch new file mode 100644 index 0000000000..08b4b0de29 --- /dev/null +++ b/target/linux/ramips/patches-5.10/105-mt7621-memory-detect.patch @@ -0,0 +1,125 @@ +From b5a52351a66f3c2a7a207548aa87d78ff2d336c0 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Wed, 10 Jul 2019 00:24:48 +0800 +Subject: [PATCH] MIPS: ralink: mt7621: add memory detection support + +mt7621 has the following memory map: +0x0-0x1c000000: lower 448m memory +0x1c000000-0x2000000: peripheral registers +0x20000000-0x2400000: higher 64m memory + +detect_memory_region in arch/mips/kernel/setup.c only add the first +memory region and isn't suitable for 512m memory detection because +it may accidentally read the memory area for peripheral registers. + +This commit adds memory detection capability for mt7621: +1. add the highmem area when 512m is detected. +2. guard memcmp from accessing peripheral registers: + This only happens when some weird user decided to change + kernel load address to 256m or higher address. Since this + is a quite unusual case, we just skip 512m testing and return + 256m as memory size. + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +--- + arch/mips/include/asm/mach-ralink/mt7621.h | 7 ++--- + arch/mips/ralink/mt7621.c | 30 +++++++++++++++++++--- + 2 files changed, 30 insertions(+), 7 deletions(-) + +--- a/arch/mips/include/asm/mach-ralink/mt7621.h ++++ b/arch/mips/include/asm/mach-ralink/mt7621.h +@@ -44,9 +44,10 @@ + #define CPU_PLL_FBDIV_MASK 0x7f + #define CPU_PLL_FBDIV_SHIFT 4 + +-#define MT7621_DRAM_BASE 0x0 +-#define MT7621_DDR2_SIZE_MIN 32 +-#define MT7621_DDR2_SIZE_MAX 256 ++#define MT7621_LOWMEM_BASE 0x0 ++#define MT7621_LOWMEM_MAX_SIZE 0x1C000000 ++#define MT7621_HIGHMEM_BASE 0x20000000 ++#define MT7621_HIGHMEM_SIZE 0x4000000 + + #define MT7621_CHIP_NAME0 0x3637544D + #define MT7621_CHIP_NAME1 0x20203132 +--- a/arch/mips/ralink/mt7621.c ++++ b/arch/mips/ralink/mt7621.c +@@ -13,6 +13,7 @@ + #include <linux/clk-provider.h> + #include <dt-bindings/clock/mt7621-clk.h> + ++#include <asm/bootinfo.h> + #include <asm/mipsregs.h> + #include <asm/smp-ops.h> + #include <asm/mips-cps.h> +@@ -55,6 +56,8 @@ + #define MT7621_GPIO_MODE_SDHCI_SHIFT 18 + #define MT7621_GPIO_MODE_SDHCI_GPIO 1 + ++static void *detect_magic __initdata = detect_memory_region; ++ + static struct rt2880_pmx_func uart1_grp[] = { FUNC("uart1", 0, 1, 2) }; + static struct rt2880_pmx_func i2c_grp[] = { FUNC("i2c", 0, 3, 2) }; + static struct rt2880_pmx_func uart3_grp[] = { +@@ -139,6 +142,28 @@ static struct clk *__init mt7621_add_sys + return clk; + } + ++void __init mt7621_memory_detect(void) ++{ ++ void *dm = &detect_magic; ++ phys_addr_t size; ++ ++ for (size = 32 * SZ_1M; size < 256 * SZ_1M; size <<= 1) { ++ if (!memcmp(dm, dm + size, sizeof(detect_magic))) ++ break; ++ } ++ ++ if ((size == 256 * SZ_1M) && ++ (CPHYSADDR(dm + size) < MT7621_LOWMEM_MAX_SIZE) && ++ memcmp(dm, dm + size, sizeof(detect_magic))) { ++ add_memory_region(MT7621_LOWMEM_BASE, MT7621_LOWMEM_MAX_SIZE, ++ BOOT_MEM_RAM); ++ add_memory_region(MT7621_HIGHMEM_BASE, MT7621_HIGHMEM_SIZE, ++ BOOT_MEM_RAM); ++ } else { ++ add_memory_region(MT7621_LOWMEM_BASE, size, BOOT_MEM_RAM); ++ } ++} ++ + void __init ralink_clk_init(void) + { + u32 syscfg, xtal_sel, clkcfg, clk_sel, curclk, ffiv, ffrac; +@@ -317,10 +342,7 @@ void prom_soc_init(struct ralink_soc_inf + (rev >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK, + (rev & CHIP_REV_ECO_MASK)); + +- soc_info->mem_size_min = MT7621_DDR2_SIZE_MIN; +- soc_info->mem_size_max = MT7621_DDR2_SIZE_MAX; +- soc_info->mem_base = MT7621_DRAM_BASE; +- ++ soc_info->mem_detect = mt7621_memory_detect; + rt2880_pinmux_data = mt7621_pinmux_data; + + +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -17,6 +17,7 @@ struct ralink_soc_info { + unsigned long mem_size; + unsigned long mem_size_min; + unsigned long mem_size_max; ++ void (*mem_detect)(void); + }; + extern struct ralink_soc_info soc_info; + +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -87,6 +87,8 @@ void __init plat_mem_setup(void) + of_scan_flat_dt(early_init_dt_find_memory, NULL); + if (memory_dtb) + of_scan_flat_dt(early_init_dt_scan_memory, NULL); ++ else if (soc_info.mem_detect) ++ soc_info.mem_detect(); + else if (soc_info.mem_size) + add_memory_region(soc_info.mem_base, soc_info.mem_size * SZ_1M, + BOOT_MEM_RAM); diff --git a/target/linux/ramips/patches-5.10/110-mt7621-perfctr-fix.patch b/target/linux/ramips/patches-5.10/110-mt7621-perfctr-fix.patch new file mode 100644 index 0000000000..dfeac7eb99 --- /dev/null +++ b/target/linux/ramips/patches-5.10/110-mt7621-perfctr-fix.patch @@ -0,0 +1,15 @@ +--- a/arch/mips/ralink/irq-gic.c ++++ b/arch/mips/ralink/irq-gic.c +@@ -13,6 +13,12 @@ + + int get_c0_perfcount_int(void) + { ++ /* ++ * Performance counter events are routed through GIC. ++ * Prevent them from firing on CPU IRQ7 as well ++ */ ++ clear_c0_status(IE_SW0 << 7); ++ + return gic_get_c0_perfcount_int(); + } + EXPORT_SYMBOL_GPL(get_c0_perfcount_int); diff --git a/target/linux/ramips/patches-5.10/111-gpio-mmio-introduce-BGPIOF_NO_SET_ON_INPUT.patch b/target/linux/ramips/patches-5.10/111-gpio-mmio-introduce-BGPIOF_NO_SET_ON_INPUT.patch new file mode 100644 index 0000000000..fdb89d0902 --- /dev/null +++ b/target/linux/ramips/patches-5.10/111-gpio-mmio-introduce-BGPIOF_NO_SET_ON_INPUT.patch @@ -0,0 +1,85 @@ +From 5d7b644aad721ecca20bd8976b38fb243fdc84f9 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Sun, 15 Mar 2020 20:13:37 +0800 +Subject: [PATCH] gpio: mmio: introduce BGPIOF_NO_SET_ON_INPUT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some gpio controllers ignores pin value writing when that pin is +configured as input mode. As a result, bgpio_dir_out should set +pin to output before configuring pin values or gpio pin values +can't be set up properly. +Introduce two variants of bgpio_dir_out: bgpio_dir_out_val_first +and bgpio_dir_out_dir_first, and assign direction_output according +to a new flag: BGPIOF_NO_SET_ON_INPUT. + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +Tested-by: René van Dorst <opensource@vdorst.com> +Reviewed-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com> +--- + drivers/gpio/gpio-mmio.c | 23 +++++++++++++++++++---- + include/linux/gpio/driver.h | 1 + + 2 files changed, 20 insertions(+), 4 deletions(-) + +--- a/drivers/gpio/gpio-mmio.c ++++ b/drivers/gpio/gpio-mmio.c +@@ -381,12 +381,10 @@ static int bgpio_get_dir(struct gpio_chi + return 1; + } + +-static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) ++static void bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) + { + unsigned long flags; + +- gc->set(gc, gpio, val); +- + spin_lock_irqsave(&gc->bgpio_lock, flags); + + gc->bgpio_dir |= bgpio_line2mask(gc, gpio); +@@ -397,7 +395,21 @@ static int bgpio_dir_out(struct gpio_chi + gc->write_reg(gc->reg_dir_out, gc->bgpio_dir); + + spin_unlock_irqrestore(&gc->bgpio_lock, flags); ++} + ++static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, ++ int val) ++{ ++ bgpio_dir_out(gc, gpio, val); ++ gc->set(gc, gpio, val); ++ return 0; ++} ++ ++static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, ++ int val) ++{ ++ gc->set(gc, gpio, val); ++ bgpio_dir_out(gc, gpio, val); + return 0; + } + +@@ -530,7 +542,10 @@ static int bgpio_setup_direction(struct + if (dirout || dirin) { + gc->reg_dir_out = dirout; + gc->reg_dir_in = dirin; +- gc->direction_output = bgpio_dir_out; ++ if (flags & BGPIOF_NO_SET_ON_INPUT) ++ gc->direction_output = bgpio_dir_out_dir_first; ++ else ++ gc->direction_output = bgpio_dir_out_val_first; + gc->direction_input = bgpio_dir_in; + gc->get_direction = bgpio_get_dir; + } else { +--- a/include/linux/gpio/driver.h ++++ b/include/linux/gpio/driver.h +@@ -567,6 +567,7 @@ int bgpio_init(struct gpio_chip *gc, str + #define BGPIOF_BIG_ENDIAN_BYTE_ORDER BIT(3) + #define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */ + #define BGPIOF_NO_OUTPUT BIT(5) /* only input */ ++#define BGPIOF_NO_SET_ON_INPUT BIT(6) + + int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq); diff --git a/target/linux/ramips/patches-5.10/112-gpio-mt7621-add-BGPIOF_NO_SET_ON_INPUT-flag.patch b/target/linux/ramips/patches-5.10/112-gpio-mt7621-add-BGPIOF_NO_SET_ON_INPUT-flag.patch new file mode 100644 index 0000000000..862f9adb47 --- /dev/null +++ b/target/linux/ramips/patches-5.10/112-gpio-mt7621-add-BGPIOF_NO_SET_ON_INPUT-flag.patch @@ -0,0 +1,33 @@ +From ad65f02fd73e9a700f1693a4513ae923ca07beb0 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Sun, 15 Mar 2020 20:13:38 +0800 +Subject: [PATCH] gpio: mt7621: add BGPIOF_NO_SET_ON_INPUT flag +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +DSET/DCLR registers only works on output pins. Add corresponding +BGPIOF_NO_SET_ON_INPUT flag to bgpio_init call to fix direction_out +behavior. + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +Tested-by: René van Dorst <opensource@vdorst.com> +Reviewed-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com> +--- + drivers/gpio/gpio-mt7621.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/gpio/gpio-mt7621.c ++++ b/drivers/gpio/gpio-mt7621.c +@@ -227,8 +227,8 @@ mediatek_gpio_bank_probe(struct device * + ctrl = mtk->base + GPIO_REG_DCLR + (rg->bank * GPIO_BANK_STRIDE); + diro = mtk->base + GPIO_REG_CTRL + (rg->bank * GPIO_BANK_STRIDE); + +- ret = bgpio_init(&rg->chip, dev, 4, +- dat, set, ctrl, diro, NULL, 0); ++ ret = bgpio_init(&rg->chip, dev, 4, dat, set, ctrl, diro, NULL, ++ BGPIOF_NO_SET_ON_INPUT); + if (ret) { + dev_err(dev, "bgpio_init() failed\n"); + return ret; diff --git a/target/linux/ramips/patches-5.10/200-add-ralink-eth.patch b/target/linux/ramips/patches-5.10/200-add-ralink-eth.patch new file mode 100644 index 0000000000..b8fd8e511d --- /dev/null +++ b/target/linux/ramips/patches-5.10/200-add-ralink-eth.patch @@ -0,0 +1,20 @@ +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -159,6 +159,7 @@ source "drivers/net/ethernet/pasemi/Kcon + source "drivers/net/ethernet/pensando/Kconfig" + source "drivers/net/ethernet/qlogic/Kconfig" + source "drivers/net/ethernet/qualcomm/Kconfig" ++source "drivers/net/ethernet/ralink/Kconfig" + source "drivers/net/ethernet/rdc/Kconfig" + source "drivers/net/ethernet/realtek/Kconfig" + source "drivers/net/ethernet/renesas/Kconfig" +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -72,6 +72,7 @@ obj-$(CONFIG_NET_VENDOR_PACKET_ENGINES) + obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ + obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ + obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/ ++obj-$(CONFIG_NET_VENDOR_RALINK) += ralink/ + obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ + obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/ + obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ diff --git a/target/linux/ramips/patches-5.10/300-mt7620-export-chip-version-and-pkg.patch b/target/linux/ramips/patches-5.10/300-mt7620-export-chip-version-and-pkg.patch new file mode 100644 index 0000000000..8b4335eb03 --- /dev/null +++ b/target/linux/ramips/patches-5.10/300-mt7620-export-chip-version-and-pkg.patch @@ -0,0 +1,19 @@ +--- a/arch/mips/include/asm/mach-ralink/mt7620.h ++++ b/arch/mips/include/asm/mach-ralink/mt7620.h +@@ -135,4 +135,16 @@ static inline int mt7620_get_eco(void) + return rt_sysc_r32(SYSC_REG_CHIP_REV) & CHIP_REV_ECO_MASK; + } + ++static inline int mt7620_get_chipver(void) ++{ ++ return (rt_sysc_r32(SYSC_REG_CHIP_REV) >> CHIP_REV_VER_SHIFT) & ++ CHIP_REV_VER_MASK; ++} ++ ++static inline int mt7620_get_pkg(void) ++{ ++ return (rt_sysc_r32(SYSC_REG_CHIP_REV) >> CHIP_REV_PKG_SHIFT) & ++ CHIP_REV_PKG_MASK; ++} ++ + #endif diff --git a/target/linux/ramips/patches-5.10/301-MIPS-ralink-mt7621-introduce-soc_device-initializati.patch b/target/linux/ramips/patches-5.10/301-MIPS-ralink-mt7621-introduce-soc_device-initializati.patch new file mode 100644 index 0000000000..dc3dd0d1ec --- /dev/null +++ b/target/linux/ramips/patches-5.10/301-MIPS-ralink-mt7621-introduce-soc_device-initializati.patch @@ -0,0 +1,89 @@ +From f798b7588bd7397bbab958281ca6c88d08714941 Mon Sep 17 00:00:00 2001 +From: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Date: Thu, 12 Mar 2020 12:29:15 +0100 +Subject: [PATCH] MIPS: ralink: mt7621: introduce 'soc_device' initialization + +mt7621 SoC has its own 'ralink_soc_info' structure with some +information about the soc itself. Pcie controller and pcie phy +drivers for this soc which are still in staging git tree make uses +of 'soc_device_attribute' looking for revision 'E2' in order to +know if reset lines are or not inverted. This way of doing things +seems to be necessary in order to make things clean and properly. +Hence, introduce this 'soc_device' to be able to properly use those +attributes in drivers. Also set 'data' pointer points to the struct +'ralink_soc_info' to be able to export also current soc information +using this mechanism. + +Cc: Paul Burton <paul.burton@mips.com> +Cc: ralf@linux-mips.org +Cc: jhogan@kernel.org +Cc: john@phrozen.org +Cc: NeilBrown <neil@brown.name> +Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Cc: linux-mips@vger.kernel.org + +Signed-off-by: Sergio Paracuellos <sergio.paracuellos@gmail.com> +Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> +--- + arch/mips/ralink/mt7621.c | 32 +++++++++++++++++++++++++++++++- + 1 file changed, 31 insertions(+), 1 deletion(-) + +--- a/arch/mips/ralink/mt7621.c ++++ b/arch/mips/ralink/mt7621.c +@@ -7,6 +7,8 @@ + + #include <linux/kernel.h> + #include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/sys_soc.h> + #include <linux/jiffies.h> + #include <linux/clk.h> + #include <linux/clkdev.h> +@@ -294,6 +296,33 @@ static int udelay_recal(void) + } + device_initcall(udelay_recal); + ++static void soc_dev_init(struct ralink_soc_info *soc_info, u32 rev) ++{ ++ struct soc_device *soc_dev; ++ struct soc_device_attribute *soc_dev_attr; ++ ++ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); ++ if (!soc_dev_attr) ++ return; ++ ++ soc_dev_attr->soc_id = "mt7621"; ++ soc_dev_attr->family = "Ralink"; ++ ++ if (((rev >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK) == 1 && ++ (rev & CHIP_REV_ECO_MASK) == 1) ++ soc_dev_attr->revision = "E2"; ++ else ++ soc_dev_attr->revision = "E1"; ++ ++ soc_dev_attr->data = soc_info; ++ ++ soc_dev = soc_device_register(soc_dev_attr); ++ if (IS_ERR(soc_dev)) { ++ kfree(soc_dev_attr); ++ return; ++ } ++} ++ + void prom_soc_init(struct ralink_soc_info *soc_info) + { + void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7621_SYSC_BASE); +@@ -345,11 +374,12 @@ void prom_soc_init(struct ralink_soc_inf + soc_info->mem_detect = mt7621_memory_detect; + rt2880_pinmux_data = mt7621_pinmux_data; + +- + if (!register_cps_smp_ops()) + return; + if (!register_cmp_smp_ops()) + return; + if (!register_vsmp_smp_ops()) + return; ++ ++ soc_dev_init(soc_info, rev); + } diff --git a/target/linux/ramips/patches-5.10/302-spi-nor-add-gd25q512.patch b/target/linux/ramips/patches-5.10/302-spi-nor-add-gd25q512.patch new file mode 100644 index 0000000000..7fcf0c54b6 --- /dev/null +++ b/target/linux/ramips/patches-5.10/302-spi-nor-add-gd25q512.patch @@ -0,0 +1,14 @@ +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -2303,6 +2303,11 @@ static const struct flash_info spi_nor_i + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + .fixups = &gd25q256_fixups, + }, ++ { ++ "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4B_OPCODES) ++ }, + + /* Intel/Numonyx -- xxxs33b */ + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, diff --git a/target/linux/ramips/patches-5.10/401-net-ethernet-mediatek-support-net-labels.patch b/target/linux/ramips/patches-5.10/401-net-ethernet-mediatek-support-net-labels.patch new file mode 100644 index 0000000000..6583fca60b --- /dev/null +++ b/target/linux/ramips/patches-5.10/401-net-ethernet-mediatek-support-net-labels.patch @@ -0,0 +1,34 @@ +From bd0f89de5476ca25e73fae829ba3e1dafae1d90d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= <opensource@vdorst.com> +Date: Fri, 21 Jun 2019 10:04:05 +0200 +Subject: [PATCH] net: ethernet: mediatek: support net-labels + +With this patch, device name can be set within dts file in the same way as dsa +port can. +Add: label = "wan"; to GMAC node. + +Signed-off-by: René van Dorst <opensource@vdorst.com> +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -2922,6 +2922,7 @@ static const struct net_device_ops mtk_n + + static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) + { ++ const char *name = of_get_property(np, "label", NULL); + const __be32 *_id = of_get_property(np, "reg", NULL); + struct phylink *phylink; + int phy_mode, id, err; +@@ -3014,6 +3015,9 @@ static int mtk_add_mac(struct mtk_eth *e + + eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN; + ++ if (name) ++ strlcpy(eth->netdev[id]->name, name, IFNAMSIZ); ++ + return 0; + + free_netdev: diff --git a/target/linux/ramips/patches-5.10/990-NET-no-auto-carrier-off-support.patch b/target/linux/ramips/patches-5.10/990-NET-no-auto-carrier-off-support.patch new file mode 100644 index 0000000000..a31d1ac3a6 --- /dev/null +++ b/target/linux/ramips/patches-5.10/990-NET-no-auto-carrier-off-support.patch @@ -0,0 +1,47 @@ +From 0b6eb1e68290243d439ee330ea8d0b239a5aec69 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Sun, 27 Jul 2014 09:38:50 +0100 +Subject: [PATCH 34/53] NET: multi phy support + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/net/phy/phy.c | 9 ++++++--- + include/linux/phy.h | 1 + + 2 files changed, 7 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -546,7 +546,10 @@ static int phy_check_link_status(struct + phy_link_up(phydev); + } else if (!phydev->link && phydev->state != PHY_NOLINK) { + phydev->state = PHY_NOLINK; +- phy_link_down(phydev, true); ++ if (!phydev->no_auto_carrier_off) ++ phy_link_down(phydev, true); ++ else ++ phy_link_down(phydev, false); + } + + return 0; +@@ -926,7 +929,10 @@ void phy_state_machine(struct work_struc + case PHY_HALTED: + if (phydev->link) { + phydev->link = 0; +- phy_link_down(phydev, true); ++ if (!phydev->no_auto_carrier_off) ++ phy_link_down(phydev, true); ++ else ++ phy_link_down(phydev, false); + } + do_suspend = true; + break; +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -380,6 +380,7 @@ struct phy_device { + unsigned suspended_by_mdio_bus:1; + unsigned sysfs_links:1; + unsigned loopback_enabled:1; ++ unsigned no_auto_carrier_off:1; + + unsigned autoneg:1; + /* The most recently read link state */ diff --git a/target/linux/ramips/patches-5.10/991-at803x.patch b/target/linux/ramips/patches-5.10/991-at803x.patch new file mode 100644 index 0000000000..af0132f8f7 --- /dev/null +++ b/target/linux/ramips/patches-5.10/991-at803x.patch @@ -0,0 +1,156 @@ +From 924453aa9d2324e5611f8e2b71df746d8f0c79f1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= <opensource@vdorst.com> +Date: Fri, 13 Nov 2020 16:11:32 +0100 +Subject: [PATCH] net: phy: at803x: add support for SFP module in + RGMII-to-x-base mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: René van Dorst <opensource@vdorst.com> +--- + drivers/net/phy/at803x.c | 91 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 91 insertions(+) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -14,6 +14,8 @@ + #include <linux/etherdevice.h> + #include <linux/of_gpio.h> + #include <linux/gpio/consumer.h> ++#include <linux/sfp.h> ++#include <linux/phylink.h> + + #define AT803X_SPECIFIC_STATUS 0x11 + #define AT803X_SS_SPEED_MASK (3 << 14) +@@ -53,9 +55,18 @@ + + #define AT803X_MODE_CFG_MASK 0x0F + #define AT803X_MODE_CFG_SGMII 0x01 ++#define AT803X_MODE_CFG_BX1000_RGMII_50 0x02 ++#define AT803X_MODE_CFG_BX1000_RGMII_75 0x03 ++#define AT803X_MODE_FIBER 0x01 ++#define AT803X_MODE_COPPER 0x00 + + #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ + #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 ++#define PSSR_LINK BIT(10) ++#define PSSR_SYNC_STATUS BIT(8) ++#define PSSR_DUPLEX BIT(13) ++#define PSSR_SPEED_1000 BIT(15) ++#define PSSR_SPEED_100 BIT(14) + + #define AT803X_DEBUG_REG_0 0x00 + #define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) +@@ -243,10 +254,72 @@ static int at803x_resume(struct phy_devi + return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); + } + ++static int at803x_mode(struct phy_device *phydev) ++{ ++ int mode; ++ ++ mode = phy_read(phydev, AT803X_REG_CHIP_CONFIG) & AT803X_MODE_CFG_MASK; ++ ++ if (mode == AT803X_MODE_CFG_BX1000_RGMII_50 || ++ mode == AT803X_MODE_CFG_BX1000_RGMII_75) ++ return AT803X_MODE_FIBER; ++ return AT803X_MODE_COPPER; ++} ++ ++static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(at803x_support) = { 0, }; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; ++ struct phy_device *phydev = upstream; ++ phy_interface_t iface; ++ ++ phylink_set(at803x_support, 1000baseX_Full); ++ /* AT803x only support 1000baseX but SGMII works fine when module runs ++ * at 1Gbit. ++ */ ++ phylink_set(at803x_support, 1000baseT_Full); ++ ++ sfp_parse_support(phydev->sfp_bus, id, support); ++ ++ // Limit to interfaces that both sides support ++ linkmode_and(support, support, at803x_support); ++ ++ if (linkmode_empty(support)) ++ goto unsupported_mode; ++ ++ iface = sfp_select_interface(phydev->sfp_bus, support); ++ ++ if (iface != PHY_INTERFACE_MODE_SGMII && ++ iface != PHY_INTERFACE_MODE_1000BASEX) ++ goto unsupported_mode; ++ ++ dev_info(&phydev->mdio.dev, "SFP interface %s", phy_modes(iface)); ++ ++ return 0; ++ ++unsupported_mode: ++ dev_info(&phydev->mdio.dev, "incompatible SFP module inserted;" ++ "Only SGMII at 1Gbit/1000BASEX are supported!\n"); ++ return -EINVAL; ++} ++ ++static const struct sfp_upstream_ops at803x_sfp_ops = { ++ .attach = phy_sfp_attach, ++ .detach = phy_sfp_detach, ++ .module_insert = at803x_sfp_insert, ++}; ++ + static int at803x_probe(struct phy_device *phydev) + { + struct device *dev = &phydev->mdio.dev; + struct at803x_priv *priv; ++ int ret; ++ ++ if (at803x_mode(phydev) == AT803X_MODE_FIBER) { ++ ret = phy_sfp_probe(phydev, &at803x_sfp_ops); ++ if (ret < 0) ++ return ret; ++ } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -394,6 +467,10 @@ static int at803x_read_status(struct phy + { + int ss, err, old_link = phydev->link; + ++ /* Handle (Fiber) SGMII to RGMII mode */ ++ if (at803x_mode(phydev) == AT803X_MODE_FIBER) ++ return genphy_c37_read_status(phydev); ++ + /* Update the link, but return if there was an error */ + err = genphy_update_link(phydev); + if (err) +@@ -448,6 +525,19 @@ static int at803x_read_status(struct phy + return 0; + } + ++static int at803x_config_aneg(struct phy_device *phydev) ++{ ++ /* Handle (Fiber) SerDes to RGMII mode */ ++ if (at803x_mode(phydev) == AT803X_MODE_FIBER) { ++ pr_warn("%s: fiber\n", __func__); ++ return genphy_c37_config_aneg(phydev); ++ } ++ ++ pr_warn("%s: enter\n", __func__); ++ ++ return genphy_config_aneg(phydev); ++} ++ + static struct phy_driver at803x_driver[] = { + { + /* ATHEROS 8035 */ +@@ -491,6 +581,7 @@ static struct phy_driver at803x_driver[] + .suspend = at803x_suspend, + .resume = at803x_resume, + /* PHY_GBIT_FEATURES */ ++ .config_aneg = at803x_config_aneg, + .read_status = at803x_read_status, + .aneg_done = at803x_aneg_done, + .ack_interrupt = &at803x_ack_interrupt, diff --git a/target/linux/ramips/patches-5.10/999-fix-pci-init-mt7620.patch b/target/linux/ramips/patches-5.10/999-fix-pci-init-mt7620.patch new file mode 100644 index 0000000000..7c00d4c9ae --- /dev/null +++ b/target/linux/ramips/patches-5.10/999-fix-pci-init-mt7620.patch @@ -0,0 +1,21 @@ +--- a/arch/mips/pci/pci-mt7620.c ++++ b/arch/mips/pci/pci-mt7620.c +@@ -32,6 +32,7 @@ + #define PPLL_CFG1 0x9c + + #define PPLL_DRV 0xa0 ++#define PPLL_LD BIT(23) + #define PDRV_SW_SET BIT(31) + #define LC_CKDRVPD BIT(19) + #define LC_CKDRVOHZ BIT(18) +@@ -239,8 +240,8 @@ static int mt7620_pci_hw_init(struct pla + rt_sysc_m32(0, RALINK_PCIE0_CLK_EN, RALINK_CLKCFG1); + mdelay(100); + +- if (!(rt_sysc_r32(PPLL_CFG1) & PDRV_SW_SET)) { +- dev_err(&pdev->dev, "MT7620 PPLL unlock\n"); ++ if (!(rt_sysc_r32(PPLL_CFG1) & PPLL_LD)) { ++ dev_err(&pdev->dev, "MT7620 PPLL is unlocked, aborting init\n"); + reset_control_assert(rstpcie0); + rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1); + return -1; |