From 5dd763680e874b0082e4cfbbc729fa75ee056601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Wed, 5 Feb 2025 08:49:20 +0100 Subject: [PATCH 01/17] kernel: r8168: update to v8.055.00 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changelog: https://github.com/openwrt/rtl8168/compare/8.054.00...8.055.00 Signed-off-by: Álvaro Fernández Rojas --- package/kernel/r8168/Makefile | 4 ++-- .../001-r8168_n-fix-proc_dump_rx_desc_2-on-32-bits.patch | 2 +- .../200-r8168-print-link-speed-and-duplex-mode.patch | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/package/kernel/r8168/Makefile b/package/kernel/r8168/Makefile index 2604425dea..0b846113df 100644 --- a/package/kernel/r8168/Makefile +++ b/package/kernel/r8168/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=r8168 -PKG_VERSION:=8.054.00 +PKG_VERSION:=8.055.00 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:=https://github.com/openwrt/rtl8168/releases/download/$(PKG_VERSION) -PKG_HASH:=5480120cf823e991e8cbd325118c1ec0c57d8f42760ba1a7334bd07d291d235d +PKG_HASH:=61deb2a9cb7d6b08748ad51734b108da95d629712b64b204e2e6bd3f16d0a48f PKG_BUILD_PARALLEL:=1 PKG_LICENSE:=GPLv2 diff --git a/package/kernel/r8168/patches/001-r8168_n-fix-proc_dump_rx_desc_2-on-32-bits.patch b/package/kernel/r8168/patches/001-r8168_n-fix-proc_dump_rx_desc_2-on-32-bits.patch index b53819a0cb..bb00411e06 100644 --- a/package/kernel/r8168/patches/001-r8168_n-fix-proc_dump_rx_desc_2-on-32-bits.patch +++ b/package/kernel/r8168/patches/001-r8168_n-fix-proc_dump_rx_desc_2-on-32-bits.patch @@ -13,7 +13,7 @@ Signed-off-by: Álvaro Fernández Rojas --- a/src/r8168_n.c +++ b/src/r8168_n.c -@@ -1655,9 +1655,9 @@ static int proc_dump_rx_desc_2(struct se +@@ -1668,9 +1668,9 @@ static int proc_dump_rx_desc_2(struct se j, k); for (i=0; i<(tp->RxDescLength/4); i++) { if (!(i % 4)) diff --git a/package/kernel/r8168/patches/200-r8168-print-link-speed-and-duplex-mode.patch b/package/kernel/r8168/patches/200-r8168-print-link-speed-and-duplex-mode.patch index 41162b4d9b..150c41d632 100644 --- a/package/kernel/r8168/patches/200-r8168-print-link-speed-and-duplex-mode.patch +++ b/package/kernel/r8168/patches/200-r8168-print-link-speed-and-duplex-mode.patch @@ -18,7 +18,7 @@ Signed-off-by: Chukun Pan --- a/src/r8168.h +++ b/src/r8168.h -@@ -1468,6 +1468,8 @@ enum RTL8168_register_content { +@@ -1480,6 +1480,8 @@ enum RTL8168_register_content { LinkStatus = 0x02, FullDup = 0x01, @@ -37,7 +37,7 @@ Signed-off-by: Chukun Pan #include #include #include -@@ -5369,6 +5370,36 @@ rtl8168_link_down_patch(struct net_devic +@@ -5396,6 +5397,38 @@ rtl8168_link_down_patch(struct net_devic #endif } @@ -66,6 +66,8 @@ Signed-off-by: Chukun Pan + speed = SPEED_100; + else if (status & _10bps) + speed = SPEED_10; ++ else if (eee_giga_lite) ++ speed = SPEED_1000; + } + + return speed; @@ -74,7 +76,7 @@ Signed-off-by: Chukun Pan static void rtl8168_check_link_status(struct net_device *dev) { -@@ -5388,11 +5419,18 @@ rtl8168_check_link_status(struct net_dev +@@ -5415,11 +5448,18 @@ rtl8168_check_link_status(struct net_dev if (link_status_on) { rtl8168_link_on_patch(dev); From b410f2216c7a4e6c5646f999d18b952b63c85817 Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Tue, 28 Jan 2025 08:14:03 +0100 Subject: [PATCH 02/17] realtek: drop old RTL8231 driver The old RTL8231 driver integrated the MDIO bus access with the GPIO control ops, making this driver not very portable to newer platforms. It depended on the SoC ID instead of the compatible to determine the MDIO access register, further complicating portability. A new MFD driver is now available, which offers proper pin config as well as optional LED support, which can work on any (bitbanged) MDIO bus. Now that all devices have been migrated, we can drop the old code. Signed-off-by: Sander Vanheule --- .../files-6.6/drivers/gpio/gpio-rtl8231.c | 364 ------------------ .../301-gpio-add-rtl8231-driver.patch | 50 --- target/linux/realtek/rtl838x/config-6.6 | 1 - target/linux/realtek/rtl839x/config-6.6 | 1 - target/linux/realtek/rtl930x/config-6.6 | 1 - target/linux/realtek/rtl931x/config-6.6 | 1 - 6 files changed, 418 deletions(-) delete mode 100644 target/linux/realtek/files-6.6/drivers/gpio/gpio-rtl8231.c delete mode 100644 target/linux/realtek/patches-6.6/301-gpio-add-rtl8231-driver.patch diff --git a/target/linux/realtek/files-6.6/drivers/gpio/gpio-rtl8231.c b/target/linux/realtek/files-6.6/drivers/gpio/gpio-rtl8231.c deleted file mode 100644 index 2821591a97..0000000000 --- a/target/linux/realtek/files-6.6/drivers/gpio/gpio-rtl8231.c +++ /dev/null @@ -1,364 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include -#include -#include -#include -#include - -/* RTL8231 registers for LED control */ -#define RTL8231_LED_FUNC0 0x0000 -#define RTL8231_LED_FUNC1 0x0001 -#define RTL8231_READY_MASK 0x03f0 -#define RTL8231_READY_VALUE 0x0370 -#define RTL8231_GPIO_PIN_SEL(gpio) ((0x0002) + ((gpio) >> 4)) -#define RTL8231_GPIO_DIR(gpio) ((0x0005) + ((gpio) >> 4)) -#define RTL8231_GPIO_DATA(gpio) ((0x001C) + ((gpio) >> 4)) - -#define USEC_TIMEOUT 5000 - -#define RTL8231_SMI_BUS_ID_MAX 0x1F - -struct rtl8231_gpios { - struct gpio_chip gc; - struct device *dev; - u32 id; - u32 smi_bus_id; - u16 reg_shadow[0x20]; - u32 reg_cached; - int ext_gpio_indrt_access; -}; - -extern struct rtl83xx_soc_info soc_info; - -DEFINE_MUTEX(miim_lock); - -static u32 rtl8231_read(struct rtl8231_gpios *gpios, u32 reg) -{ - u32 t = 0, n = 0; - - reg &= 0x1f; - - /* Calculate read register address */ - t = (gpios->smi_bus_id << 2) | (reg << 7); - - /* Set execution bit: cleared when operation completed */ - t |= 1; - - /* Start execution */ - sw_w32(t, gpios->ext_gpio_indrt_access); - do { - udelay(1); - t = sw_r32(gpios->ext_gpio_indrt_access); - n++; - } while ((t & 1) && (n < USEC_TIMEOUT)); - - if (n >= USEC_TIMEOUT) - return 0x80000000; - - pr_debug("%s: %x, %x, %x\n", __func__, gpios->smi_bus_id, - reg, (t & 0xffff0000) >> 16); - - return (t & 0xffff0000) >> 16; -} - -static int rtl8231_write(struct rtl8231_gpios *gpios, u32 reg, u32 data) -{ - u32 t = 0, n = 0; - - pr_debug("%s: %x, %x, %x\n", __func__, gpios->smi_bus_id, reg, data); - reg &= 0x1f; - - t = (gpios->smi_bus_id << 2) | (reg << 7) | (data << 16); - /* Set write bit */ - t |= 2; - - /* Set execution bit: cleared when operation completed */ - t |= 1; - - /* Start execution */ - sw_w32(t, gpios->ext_gpio_indrt_access); - do { - udelay(1); - t = sw_r32(gpios->ext_gpio_indrt_access); - } while ((t & 1) && (n < USEC_TIMEOUT)); - - if (n >= USEC_TIMEOUT) - return -1; - - return 0; -} - -static u32 rtl8231_read_cached(struct rtl8231_gpios *gpios, u32 reg) -{ - if (reg > 0x1f) - return 0; - - if (gpios->reg_cached & (1 << reg)) - return gpios->reg_shadow[reg]; - - return rtl8231_read(gpios, reg); -} - -/* Set Direction of the RTL8231 pin: - * dir 1: input - * dir 0: output - */ -static int rtl8231_pin_dir(struct rtl8231_gpios *gpios, u32 gpio, u32 dir) -{ - u32 v; - int pin_sel_addr = RTL8231_GPIO_PIN_SEL(gpio); - int pin_dir_addr = RTL8231_GPIO_DIR(gpio); - int dpin = gpio % 16; - - if (gpio > 31) { - pr_debug("WARNING: HIGH pin\n"); - dpin += 5; - pin_dir_addr = pin_sel_addr; - } - - v = rtl8231_read_cached(gpios, pin_dir_addr); - if (v & 0x80000000) { - pr_err("Error reading RTL8231\n"); - return -1; - } - - v = (v & ~(1 << dpin)) | (dir << dpin); - rtl8231_write(gpios, pin_dir_addr, v); - gpios->reg_shadow[pin_dir_addr] = v; - gpios->reg_cached |= 1 << pin_dir_addr; - - return 0; -} - -static int rtl8231_pin_dir_get(struct rtl8231_gpios *gpios, u32 gpio, u32 *dir) -{ - /* dir 1: input - * dir 0: output - */ - - u32 v; - int pin_dir_addr = RTL8231_GPIO_DIR(gpio); - int pin = gpio % 16; - - if (gpio > 31) { - pin_dir_addr = RTL8231_GPIO_PIN_SEL(gpio); - pin += 5; - } - - v = rtl8231_read(gpios, pin_dir_addr); - if (v & (1 << pin)) - *dir = 1; - else - *dir = 0; - - return 0; -} - -static int rtl8231_pin_set(struct rtl8231_gpios *gpios, u32 gpio, u32 data) -{ - u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio)); - - pr_debug("%s: %d to %d\n", __func__, gpio, data); - if (v & 0x80000000) { - pr_err("Error reading RTL8231\n"); - return -1; - } - v = (v & ~(1 << (gpio % 16))) | (data << (gpio % 16)); - rtl8231_write(gpios, RTL8231_GPIO_DATA(gpio), v); - gpios->reg_shadow[RTL8231_GPIO_DATA(gpio)] = v; - gpios->reg_cached |= 1 << RTL8231_GPIO_DATA(gpio); - - return 0; -} - -static int rtl8231_pin_get(struct rtl8231_gpios *gpios, u32 gpio, u16 *state) -{ - u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio)); - - if (v & 0x80000000) { - pr_err("Error reading RTL8231\n"); - return -1; - } - - *state = v & 0xffff; - - return 0; -} - -static int rtl8231_direction_input(struct gpio_chip *gc, unsigned int offset) -{ - int err; - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - pr_debug("%s: %d\n", __func__, offset); - mutex_lock(&miim_lock); - err = rtl8231_pin_dir(gpios, offset, 1); - mutex_unlock(&miim_lock); - - return err; -} - -static int rtl8231_direction_output(struct gpio_chip *gc, unsigned int offset, int value) -{ - int err; - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - pr_debug("%s: %d\n", __func__, offset); - mutex_lock(&miim_lock); - err = rtl8231_pin_dir(gpios, offset, 0); - mutex_unlock(&miim_lock); - - if (!err) - err = rtl8231_pin_set(gpios, offset, value); - - return err; -} - -static int rtl8231_get_direction(struct gpio_chip *gc, unsigned int offset) -{ - u32 v = 0; - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - pr_debug("%s: %d\n", __func__, offset); - mutex_lock(&miim_lock); - rtl8231_pin_dir_get(gpios, offset, &v); - mutex_unlock(&miim_lock); - - return v; -} - -static int rtl8231_gpio_get(struct gpio_chip *gc, unsigned int offset) -{ - u16 state = 0; - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - mutex_lock(&miim_lock); - rtl8231_pin_get(gpios, offset, &state); - mutex_unlock(&miim_lock); - - if (state & (1 << (offset % 16))) - return 1; - - return 0; -} - -void rtl8231_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) -{ - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - rtl8231_pin_set(gpios, offset, value); -} - -int rtl8231_init(struct rtl8231_gpios *gpios) -{ - u32 ret; - - pr_info("%s called, MDIO bus ID: %d\n", __func__, gpios->smi_bus_id); - - gpios->reg_cached = 0; - - if (soc_info.family == RTL8390_FAMILY_ID) { - /* RTL8390: Enable external gpio in global led control register */ - sw_w32_mask(0x7 << 18, 0x4 << 18, RTL839X_LED_GLB_CTRL); - } else if (soc_info.family == RTL8380_FAMILY_ID) { - /* RTL8380: Enable RTL8231 indirect access mode */ - sw_w32_mask(0, 1, RTL838X_EXTRA_GPIO_CTRL); - sw_w32_mask(3, 1, RTL838X_DMY_REG5); - } - - ret = rtl8231_read(gpios, RTL8231_LED_FUNC1); - if ((ret & 0x80000000) || ((ret & RTL8231_READY_MASK) != RTL8231_READY_VALUE)) - return -ENXIO; - - /* Select GPIO functionality and force input direction for pins 0-36 */ - rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(0), 0xffff); - rtl8231_write(gpios, RTL8231_GPIO_DIR(0), 0xffff); - rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(16), 0xffff); - rtl8231_write(gpios, RTL8231_GPIO_DIR(16), 0xffff); - rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(32), 0x03ff); - - /* Set LED_Start to enable drivers for output mode */ - rtl8231_write(gpios, RTL8231_LED_FUNC0, 1 << 1); - - return 0; -} - -static const struct of_device_id rtl8231_gpio_of_match[] = { - { .compatible = "realtek,rtl8231-gpio" }, - {}, -}; - -MODULE_DEVICE_TABLE(of, rtl8231_gpio_of_match); - -static int rtl8231_gpio_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct rtl8231_gpios *gpios; - int err; - - pr_info("Probing RTL8231 GPIOs\n"); - - if (!np) { - dev_err(&pdev->dev, "No DT found\n"); - return -EINVAL; - } - - gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); - if (!gpios) - return -ENOMEM; - - gpios->id = soc_info.id; - if (soc_info.family == RTL8380_FAMILY_ID) { - gpios->ext_gpio_indrt_access = RTL838X_EXT_GPIO_INDRT_ACCESS; - } - - if (soc_info.family == RTL8390_FAMILY_ID) { - gpios->ext_gpio_indrt_access = RTL839X_EXT_GPIO_INDRT_ACCESS; - } - - err = of_property_read_u32(np, "indirect-access-bus-id", &gpios->smi_bus_id); - if (!err && gpios->smi_bus_id > RTL8231_SMI_BUS_ID_MAX) - err = -EINVAL; - - if (err) { - dev_err(dev, "invalid or missing indirect-access-bus-id\n"); - return err; - } - - err = rtl8231_init(gpios); - if (err) { - dev_err(dev, "no device found at bus address %d\n", gpios->smi_bus_id); - return err; - } - - gpios->dev = dev; - gpios->gc.base = -1; - gpios->gc.ngpio = 37; - gpios->gc.label = "rtl8231"; - gpios->gc.parent = dev; - gpios->gc.owner = THIS_MODULE; - gpios->gc.can_sleep = true; - - gpios->gc.direction_input = rtl8231_direction_input; - gpios->gc.direction_output = rtl8231_direction_output; - gpios->gc.set = rtl8231_gpio_set; - gpios->gc.get = rtl8231_gpio_get; - gpios->gc.get_direction = rtl8231_get_direction; - - return devm_gpiochip_add_data(dev, &gpios->gc, gpios); -} - -static struct platform_driver rtl8231_gpio_driver = { - .driver = { - .name = "rtl8231-gpio", - .of_match_table = rtl8231_gpio_of_match, - }, - .probe = rtl8231_gpio_probe, -}; - -module_platform_driver(rtl8231_gpio_driver); - -MODULE_DESCRIPTION("Realtek RTL8231 GPIO expansion chip support"); -MODULE_LICENSE("GPL v2"); diff --git a/target/linux/realtek/patches-6.6/301-gpio-add-rtl8231-driver.patch b/target/linux/realtek/patches-6.6/301-gpio-add-rtl8231-driver.patch deleted file mode 100644 index f64e2cf94e..0000000000 --- a/target/linux/realtek/patches-6.6/301-gpio-add-rtl8231-driver.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 26 Nov 2020 12:02:21 +0100 -Subject: [PATCH] realtek: update the tree to the latest refactored version -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -* rename the target to realtek -* add refactored DSA driver -* add latest gpio driver -* lots of arch cleanups -* new irq driver -* additional boards - -Submitted-by: Bert Vermeulen -Submitted-by: Birger Koblitz -Submitted-by: Sander Vanheule -Submitted-by: Bjørn Mork -Submitted-by: John Crispin ---- - drivers/gpio/Kconfig | 6 ++++++ - drivers/gpio/Makefile | 1 + - 2 files changed, 7 insertions(+) - ---- a/drivers/gpio/Kconfig -+++ b/drivers/gpio/Kconfig -@@ -553,6 +553,12 @@ config GPIO_ROCKCHIP - help - Say yes here to support GPIO on Rockchip SoCs. - -+config GPIO_RTL8231 -+ tristate "RTL8231 GPIO" -+ depends on MACH_REALTEK_RTL -+ help -+ Say yes here to support Realtek RTL8231 GPIO expansion chips. -+ - config GPIO_SAMA5D2_PIOBU - tristate "SAMA5D2 PIOBU GPIO support" - depends on MFD_SYSCON ---- a/drivers/gpio/Makefile -+++ b/drivers/gpio/Makefile -@@ -138,6 +138,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc3 - obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o - obj-$(CONFIG_GPIO_REG) += gpio-reg.o - obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o -+obj-$(CONFIG_GPIO_RTL8231) += gpio-rtl8231.o - obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o - obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o - obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o diff --git a/target/linux/realtek/rtl838x/config-6.6 b/target/linux/realtek/rtl838x/config-6.6 index 3a2dbae8c1..3020e58a68 100644 --- a/target/linux/realtek/rtl838x/config-6.6 +++ b/target/linux/realtek/rtl838x/config-6.6 @@ -96,7 +96,6 @@ CONFIG_GPIO_PCA953X=y CONFIG_GPIO_PCA953X_IRQ=y CONFIG_GPIO_REALTEK_OTTO=y CONFIG_GPIO_REGMAP=y -CONFIG_GPIO_RTL8231=y CONFIG_GPIO_WATCHDOG=y # CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set CONFIG_GRO_CELLS=y diff --git a/target/linux/realtek/rtl839x/config-6.6 b/target/linux/realtek/rtl839x/config-6.6 index b64c46b613..9b9bcae6df 100644 --- a/target/linux/realtek/rtl839x/config-6.6 +++ b/target/linux/realtek/rtl839x/config-6.6 @@ -97,7 +97,6 @@ CONFIG_GPIO_PCA953X=y CONFIG_GPIO_PCA953X_IRQ=y CONFIG_GPIO_REALTEK_OTTO=y CONFIG_GPIO_REGMAP=y -CONFIG_GPIO_RTL8231=y CONFIG_GPIO_WATCHDOG=y # CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set CONFIG_GRO_CELLS=y diff --git a/target/linux/realtek/rtl930x/config-6.6 b/target/linux/realtek/rtl930x/config-6.6 index 4c53682b1b..a381e88824 100644 --- a/target/linux/realtek/rtl930x/config-6.6 +++ b/target/linux/realtek/rtl930x/config-6.6 @@ -78,7 +78,6 @@ CONFIG_GPIO_CDEV=y CONFIG_GPIO_GENERIC=y CONFIG_GPIO_PCA953X=y CONFIG_GPIO_REALTEK_OTTO=y -CONFIG_GPIO_RTL8231=y CONFIG_GRO_CELLS=y CONFIG_HARDWARE_WATCHPOINTS=y CONFIG_HAS_DMA=y diff --git a/target/linux/realtek/rtl931x/config-6.6 b/target/linux/realtek/rtl931x/config-6.6 index 716d805e51..c9cd3ddfb4 100644 --- a/target/linux/realtek/rtl931x/config-6.6 +++ b/target/linux/realtek/rtl931x/config-6.6 @@ -83,7 +83,6 @@ CONFIG_GPIOLIB_IRQCHIP=y CONFIG_GPIO_CDEV=y CONFIG_GPIO_GENERIC=y CONFIG_GPIO_REALTEK_OTTO=y -CONFIG_GPIO_RTL8231=y CONFIG_GRO_CELLS=y CONFIG_HARDWARE_WATCHPOINTS=y CONFIG_HAS_DMA=y From 77a1a5ef8e74e8c64ae02b846f827252c978451c Mon Sep 17 00:00:00 2001 From: Michael Heimpold Date: Sun, 15 Sep 2024 10:39:40 +0200 Subject: [PATCH 03/17] mxs: fix image generation for I2SE Duckbills The standard U-Boot boot scripts for Duckbills expect the Linux kernel and device tree files installed below /boot within the (ext4) root filesystem. Also a raw zImage is expected instead of uImage. Extend the SD card generation accordingly and while at, install all possible Duckbill DT blobs there. Signed-off-by: Michael Heimpold --- target/linux/mxs/image/Makefile | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/target/linux/mxs/image/Makefile b/target/linux/mxs/image/Makefile index b95409741a..6fd832a9f4 100644 --- a/target/linux/mxs/image/Makefile +++ b/target/linux/mxs/image/Makefile @@ -10,11 +10,33 @@ FAT32_BLOCKS=$(shell echo $$(($(CONFIG_MXS_SD_BOOT_PARTSIZE)*1024*1024/$(FAT32_B KERNEL_LOADADDR:=0x40008000 +define Build/mxs-ext4-rootfs-with-boot + rm -rf $(call mkfs_target_dir,$(1))/boot + mkdir -p $(call mkfs_target_dir,$(1))/boot + $(CP) --no-preserve=mode $(KDIR)/$(KERNEL_NAME) $(call mkfs_target_dir,$(1))/boot/ + $(foreach dts,$(DEVICE_DTS), \ + $(CP) \ + $(DTS_DIR)/$(dts).dtb \ + $(call mkfs_target_dir,$(1))/boot/; \ + ) + + rm -rf $@.rootfs + $(STAGING_DIR_HOST)/bin/make_ext4fs -L rootfs \ + -l $(ROOTFS_PARTSIZE) -b $(CONFIG_TARGET_EXT4_BLOCKSIZE) \ + $(if $(CONFIG_TARGET_EXT4_RESERVED_PCT),-m $(CONFIG_TARGET_EXT4_RESERVED_PCT)) \ + $(if $(CONFIG_TARGET_EXT4_JOURNAL),,-J) \ + $(if $(SOURCE_DATE_EPOCH),-T $(SOURCE_DATE_EPOCH)) \ + $@.rootfs $(call mkfs_target_dir,$(1))/ + mv $@.rootfs $@ +endef + define Build/mxs-sdcard-ext4-ext4 + mv $@ $@.rootfs + ./gen_sdcard_ext4_ext4.sh \ $@ \ $(STAGING_DIR_IMAGE)/$(DEVICE_NAME)/u-boot.sb \ - $(IMAGE_ROOTFS) \ + $@.rootfs \ $(CONFIG_TARGET_ROOTFS_PARTSIZE) endef @@ -50,8 +72,9 @@ define Device/i2se_duckbill uboot-envtools kmod-leds-gpio -kmod-nf-nathelper SUPPORTED_DEVICES:=i2se,duckbill SOC:=imx28 - DEVICE_DTS:=imx28-duckbill - IMAGE/sdcard.img.gz = mxs-sdcard-ext4-ext4 | append-metadata | gzip + KERNEL := kernel-bin + DEVICE_DTS:=imx28-duckbill imx28-duckbill-2 imx28-duckbill-2-485 imx28-duckbill-2-spi + IMAGE/sdcard.img.gz = mxs-ext4-rootfs-with-boot | mxs-sdcard-ext4-ext4 | append-metadata | gzip endef TARGET_DEVICES += i2se_duckbill From da33d7928cab62599d36bbbd15937918a5a296f9 Mon Sep 17 00:00:00 2001 From: Michael Heimpold Date: Mon, 14 Oct 2024 08:51:38 +0200 Subject: [PATCH 04/17] mxs: image: slightly adjust whitespace (no functional change) Seems that the common style is to have whitespace around the operators and the indent is single tab. Signed-off-by: Michael Heimpold --- target/linux/mxs/image/Makefile | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/target/linux/mxs/image/Makefile b/target/linux/mxs/image/Makefile index 6fd832a9f4..0281600da0 100644 --- a/target/linux/mxs/image/Makefile +++ b/target/linux/mxs/image/Makefile @@ -5,10 +5,10 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/image.mk -FAT32_BLOCK_SIZE=1024 -FAT32_BLOCKS=$(shell echo $$(($(CONFIG_MXS_SD_BOOT_PARTSIZE)*1024*1024/$(FAT32_BLOCK_SIZE)))) +FAT32_BLOCK_SIZE = 1024 +FAT32_BLOCKS = $(shell echo $$(($(CONFIG_MXS_SD_BOOT_PARTSIZE)*1024*1024/$(FAT32_BLOCK_SIZE)))) -KERNEL_LOADADDR:=0x40008000 +KERNEL_LOADADDR := 0x40008000 define Build/mxs-ext4-rootfs-with-boot rm -rf $(call mkfs_target_dir,$(1))/boot @@ -69,11 +69,11 @@ define Device/i2se_duckbill DEVICE_VENDOR := I2SE DEVICE_MODEL := Duckbill DEVICE_PACKAGES := -dnsmasq -ppp -ip6tables -iptables -mtd \ - uboot-envtools kmod-leds-gpio -kmod-nf-nathelper - SUPPORTED_DEVICES:=i2se,duckbill - SOC:=imx28 + uboot-envtools kmod-leds-gpio -kmod-nf-nathelper + SUPPORTED_DEVICES := i2se,duckbill + SOC := imx28 KERNEL := kernel-bin - DEVICE_DTS:=imx28-duckbill imx28-duckbill-2 imx28-duckbill-2-485 imx28-duckbill-2-spi + DEVICE_DTS := imx28-duckbill imx28-duckbill-2 imx28-duckbill-2-485 imx28-duckbill-2-spi IMAGE/sdcard.img.gz = mxs-ext4-rootfs-with-boot | mxs-sdcard-ext4-ext4 | append-metadata | gzip endef TARGET_DEVICES += i2se_duckbill @@ -82,10 +82,10 @@ define Device/olinuxino_maxi DEVICE_VENDOR := Olimex DEVICE_MODEL := OLinuXino Maxi DEVICE_PACKAGES := kmod-usb-net-smsc95xx kmod-pinctrl-mcp23s08-i2c \ - kmod-pinctrl-mcp23s08-spi kmod-leds-gpio kmod-sound-core - SUPPORTED_DEVICES:=olimex,imx23-olinuxino - SOC:=imx23 - DEVICE_DTS:=imx23-olinuxino + kmod-pinctrl-mcp23s08-spi kmod-leds-gpio kmod-sound-core + SUPPORTED_DEVICES := olimex,imx23-olinuxino + SOC := imx23 + DEVICE_DTS := imx23-olinuxino IMAGE/sdcard.img.gz = mxs-sdcard-vfat-ext4 | append-metadata | gzip endef TARGET_DEVICES += olinuxino_maxi @@ -94,10 +94,10 @@ define Device/olinuxino_micro DEVICE_VENDOR := Olimex DEVICE_MODEL := OLinuXino Micro DEVICE_PACKAGES := kmod-pinctrl-mcp23s08-spi kmod-pinctrl-mcp23s08-i2c \ - kmod-leds-gpio - SUPPORTED_DEVICES:=olimex,imx23-olinuxino - SOC:=imx23 - DEVICE_DTS:=imx23-olinuxino + kmod-leds-gpio + SUPPORTED_DEVICES := olimex,imx23-olinuxino + SOC := imx23 + DEVICE_DTS := imx23-olinuxino IMAGE/sdcard.img.gz = mxs-sdcard-vfat-ext4 | append-metadata | gzip endef TARGET_DEVICES += olinuxino_micro From 6f63eb71f3b0794d1367cc9262b7d7631410f644 Mon Sep 17 00:00:00 2001 From: Michael Heimpold Date: Thu, 28 Nov 2024 08:00:36 +0100 Subject: [PATCH 05/17] mxs: adapt default package list for Duckbill devices Replace outdated package names, order the remaining. firewall4 is added again, since it is enabled by default. The device is not a router by default, so this package makes no sense in the default config. User can enable it by theirself, or it will be automatically pulled via dependency, e.g. luci-app-firewall. Signed-off-by: Michael Heimpold --- target/linux/mxs/image/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/linux/mxs/image/Makefile b/target/linux/mxs/image/Makefile index 0281600da0..12e7136144 100644 --- a/target/linux/mxs/image/Makefile +++ b/target/linux/mxs/image/Makefile @@ -68,8 +68,8 @@ endef define Device/i2se_duckbill DEVICE_VENDOR := I2SE DEVICE_MODEL := Duckbill - DEVICE_PACKAGES := -dnsmasq -ppp -ip6tables -iptables -mtd \ - uboot-envtools kmod-leds-gpio -kmod-nf-nathelper + DEVICE_PACKAGES := -dnsmasq -firewall4 -mtd -nftables -odhcpd-ipv6only -ppp -kmod-nft-offload \ + uboot-envtools kmod-leds-gpio SUPPORTED_DEVICES := i2se,duckbill SOC := imx28 KERNEL := kernel-bin From c332a7d5a05671f62327088d1db7bd6658ab2354 Mon Sep 17 00:00:00 2001 From: Zoltan HERPAI Date: Wed, 5 Feb 2025 16:41:08 +0100 Subject: [PATCH 06/17] imx-bootlets: bump to 10.12.01 - refresh patches - add patch to fix compilation with GCC13 (referring to ARM-8933/1). Link: https://lore.kernel.org/lkml/20220630133231.272605764@linuxfoundation.org/ Signed-off-by: Zoltan HERPAI --- package/boot/imx-bootlets/Makefile | 6 ++--- .../patches/001-skip_sb_generation.patch | 22 +++++++++++-------- .../patches/002-set_elftosb_config.patch | 8 +++---- .../patches/003-add-olinuxino.patch | 13 +---------- .../patches/004-fix-ARM-8933_1.patch | 11 ++++++++++ 5 files changed, 32 insertions(+), 28 deletions(-) create mode 100644 package/boot/imx-bootlets/patches/004-fix-ARM-8933_1.patch diff --git a/package/boot/imx-bootlets/Makefile b/package/boot/imx-bootlets/Makefile index de976c249a..0ecb0f2dfd 100644 --- a/package/boot/imx-bootlets/Makefile +++ b/package/boot/imx-bootlets/Makefile @@ -7,11 +7,11 @@ include $(TOPDIR)/rules.mk PKG_NAME:=imx-bootlets -PKG_VERSION:=10.05.02 +PKG_VERSION:=10.12.01 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=http://trabant.uid0.hu/openwrt/ -PKG_HASH:=09ecd81a64db5166a235932146faf08d0689bfc7ac04ac9fcc3a5bd809fba74a +PKG_HASH:=f7c98cbc41e15184cad61c56115e840e34ac3ebb4a162fadeea905e5038fd65b PKG_FLAGS:=nonshared @@ -37,7 +37,7 @@ define Package/imx-bootlets/install $(INSTALL_BIN) $(PKG_BUILD_DIR)/boot_prep/boot_prep $(STAGING_DIR)/boot_prep $(INSTALL_BIN) $(PKG_BUILD_DIR)/linux_prep/output-target/linux_prep $(STAGING_DIR)/linux_prep $(INSTALL_BIN) $(PKG_BUILD_DIR)/power_prep/power_prep $(STAGING_DIR)/power_prep - $(INSTALL_BIN) $(PKG_BUILD_DIR)/linux_prebuilt.db $(STAGING_DIR)/linux_prebuilt.db + $(INSTALL_BIN) $(PKG_BUILD_DIR)/linux_ivt.bd $(STAGING_DIR)/linux_ivt.bd endef $(eval $(call BuildPackage,imx-bootlets)) diff --git a/package/boot/imx-bootlets/patches/001-skip_sb_generation.patch b/package/boot/imx-bootlets/patches/001-skip_sb_generation.patch index ad65265850..d1829aa16c 100644 --- a/package/boot/imx-bootlets/patches/001-skip_sb_generation.patch +++ b/package/boot/imx-bootlets/patches/001-skip_sb_generation.patch @@ -1,18 +1,22 @@ --- a/Makefile +++ b/Makefile -@@ -32,10 +32,11 @@ ifeq "$(DFT_IMAGE)" "$(wildcard $(DFT_IM - sed -i 's,[^ *]image.*;,\timage="$(DFT_UBOOT)";,' uboot.db - elftosb2 -z -c ./uboot.db -o i$(ARCH)_uboot.sb +@@ -37,13 +37,13 @@ ifeq "$(DFT_IMAGE)" "$(wildcard $(DFT_IM + elftosb -z -c ./uboot.bd -o i$(ARCH)_uboot.sb + elftosb -z -f imx28 -c ./uboot_ivt.bd -o i$(ARCH)_ivt_uboot.sb else - @echo "by using the pre-built kernel" -- elftosb2 -z -c ./linux_prebuilt.db -o i$(ARCH)_linux.sb -- @echo "generating U-Boot boot stream image" -- elftosb2 -z -c ./uboot_prebuilt.db -o i$(ARCH)_uboot.sb +- elftosb -z -c ./linux.bd -o i$(ARCH)_linux.sb +- elftosb -z -f imx28 -c ./linux_ivt.bd -o i$(ARCH)_ivt_linux.sb + @echo "... not generating any image for now." -+ #@echo "by using the pre-built kernel" -+ #elftosb2 -z -c ./linux_prebuilt.db -o i$(ARCH)_linux.sb ++ #elftosb -z -c ./linux.bd -o i$(ARCH)_linux.sb ++ #elftosb -z -f imx28 -c ./linux_ivt.bd -o i$(ARCH)_ivt_linux.sb + +- @echo "generating U-Boot boot stream image" +- elftosb -z -c ./uboot.bd -o i$(ARCH)_uboot.sb +- elftosb -z -f imx28 -c ./uboot_ivt.bd -o i$(ARCH)_ivt_uboot.sb + #@echo "generating U-Boot boot stream image" -+ #elftosb2 -z -c ./uboot_prebuilt.db -o i$(ARCH)_uboot.sb ++ #elftosb -z -c ./uboot.bd -o i$(ARCH)_uboot.sb ++ #elftosb -z -f imx28 -c ./uboot_ivt.bd -o i$(ARCH)_ivt_uboot.sb endif #@echo "generating kernel bootstream file sd_mmc_bootstream.raw" #Please use cfimager to burn xxx_linux.sb. The below way will no diff --git a/package/boot/imx-bootlets/patches/002-set_elftosb_config.patch b/package/boot/imx-bootlets/patches/002-set_elftosb_config.patch index 5cf48fd6fc..afd4150248 100644 --- a/package/boot/imx-bootlets/patches/002-set_elftosb_config.patch +++ b/package/boot/imx-bootlets/patches/002-set_elftosb_config.patch @@ -1,5 +1,5 @@ ---- a/linux_prebuilt.db -+++ b/linux_prebuilt.db +--- a/linux_ivt.bd ++++ b/linux_ivt.bd @@ -4,10 +4,10 @@ options { flags = 0x01; } @@ -7,11 +7,11 @@ - power_prep="./power_prep/power_prep"; - sdram_prep="./boot_prep/boot_prep"; - linux_prep="./linux_prep/output-target/linux_prep"; -- zImage = "./zImage"; +- zImage="./zImage"; + power_prep="./power_prep"; + sdram_prep="./boot_prep"; + linux_prep="./linux_prep"; -+ zImage = "./zImage_dtb"; ++ zImage="./zImage_dtb"; } section (0) { diff --git a/package/boot/imx-bootlets/patches/003-add-olinuxino.patch b/package/boot/imx-bootlets/patches/003-add-olinuxino.patch index 6ed5786344..dd0a5ce478 100644 --- a/package/boot/imx-bootlets/patches/003-add-olinuxino.patch +++ b/package/boot/imx-bootlets/patches/003-add-olinuxino.patch @@ -121,7 +121,7 @@ all: build_prep gen_bootstream -@@ -94,6 +97,8 @@ distclean: clean +@@ -101,6 +104,8 @@ distclean: clean clean: -rm -rf *.sb rm -f sd_mmc_bootstream.raw @@ -130,14 +130,3 @@ $(MAKE) -C linux_prep clean ARCH=$(ARCH) $(MAKE) -C boot_prep clean ARCH=$(ARCH) $(MAKE) -C power_prep clean ARCH=$(ARCH) ---- a/uboot.db -+++ b/uboot.db -@@ -3,7 +3,7 @@ - sources { - power_prep="./power_prep/power_prep"; - sdram_prep="./boot_prep/boot_prep"; -- image="/home/b18647/repos/ltib_latest/rootfs/boot/u-boot"; -+ image="../boot/u-boot"; - } - - section (0) { diff --git a/package/boot/imx-bootlets/patches/004-fix-ARM-8933_1.patch b/package/boot/imx-bootlets/patches/004-fix-ARM-8933_1.patch new file mode 100644 index 0000000000..3b896b2889 --- /dev/null +++ b/package/boot/imx-bootlets/patches/004-fix-ARM-8933_1.patch @@ -0,0 +1,11 @@ +--- a/linux_prep/core/cmdlines.S ++++ b/linux_prep/core/cmdlines.S +@@ -14,7 +14,7 @@ + #define CMDLINES_FILE "output-target/command_lines_stripped.txt" + #endif + +- .section .cmdlines, #alloc ++ .section .cmdlines, "a" + .globl cmdlines_start + cmdlines_start: + .incbin CMDLINES_FILE From 62a5280b8b68656700534331f4b8c738418c06a3 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Tue, 24 Oct 2023 15:56:03 -0300 Subject: [PATCH 07/17] kernel: modules: netdevices: add realtek DSA modules Uses upstream DSA switch modules (rtl8365mb, rtl8366), similar to RTL8367C and rtl8366rb swconfig drivers. The package dependencies exclude targets built without kernel CONFIG_OF. It also fixes the rtl8366rb LED support. Signed-off-by: Luiz Angelo Daros de Luca Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- package/kernel/linux/modules/netdevices.mk | 59 ++ ...-Convert-to-platform-remove-callback.patch | 66 ++ ...ealtek-drop-cleanup-from-realtek_ops.patch | 30 + ...ltek-introduce-REALTEK_DSA-namespace.patch | 146 +++ ...k-convert-variants-into-real-drivers.patch | 539 +++++++++++ ...keep-variant-reference-in-realtek_pr.patch | 91 ++ ...et-dsa-realtek-common-rtl83xx-module.patch | 875 ++++++++++++++++++ ...merge-rtl83xx-and-interface-modules-.patch | 93 ++ ...altek-get-internal-MDIO-node-by-name.patch | 34 + ...dsa-realtek-clean-user_mii_bus-setup.patch | 83 ++ ...migrate-user_mii_bus-setup-to-realte.patch | 198 ++++ ...use-the-same-mii-bus-driver-for-both.patch | 261 ++++++ ...k-embed-dsa_switch-into-realtek_priv.patch | 180 ++++ ...fix-digital-interface-select-macro-f.patch | 38 + ...dsa-realtek-reset-gpios-is-not-requi.patch | 34 + ...net-dsa-realtek-add-reset-controller.patch | 33 + ...dsa-realtek-support-reset-controller.patch | 123 +++ ...ealtek-do-not-assert-reset-on-remove.patch | 45 + ...ealtek-add-LED-drivers-for-rtl8366rb.patch | 396 ++++++++ 19 files changed, 3324 insertions(+) create mode 100644 target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch create mode 100644 target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch create mode 100644 target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch create mode 100644 target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch create mode 100644 target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch create mode 100644 target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch create mode 100644 target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch create mode 100644 target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk index 24a41c6b3f..d418701555 100644 --- a/package/kernel/linux/modules/netdevices.mk +++ b/package/kernel/linux/modules/netdevices.mk @@ -637,6 +637,65 @@ endef $(eval $(call KernelPackage,dsa-qca8k)) + +define KernelPackage/dsa-realtek + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Realtek common module RTL83xx DSA switch family + DEPENDS:=+kmod-dsa +kmod-phy-realtek +kmod-regmap-core @!TARGET_x86 @!TARGET_bcm47xx @!TARGET_uml + KCONFIG:= \ + CONFIG_NET_DSA_REALTEK \ + CONFIG_NET_DSA_REALTEK_MDIO=y \ + CONFIG_NET_DSA_REALTEK_SMI=y + FILES:= $(LINUX_DIR)/drivers/net/dsa/realtek/realtek_dsa.ko +endef + +define KernelPackage/dsa-realtek/description + Common kernel module for Realtek RTL83xx DSA switch family +endef + +$(eval $(call KernelPackage,dsa-realtek)) + + +define KernelPackage/dsa-rtl8366rb + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Realtek RTL8365MB switch DSA support + DEPENDS:=+kmod-dsa-realtek @!TARGET_x86 @!TARGET_bcm47xx @!TARGET_uml + KCONFIG:= \ + CONFIG_NET_DSA_REALTEK_RTL8366RB \ + CONFIG_NET_DSA_TAG_RTL4_A + FILES:= \ + $(LINUX_DIR)/drivers/net/dsa/realtek/rtl8366.ko \ + $(LINUX_DIR)/net/dsa/tag_rtl4_a.ko + AUTOLOAD:=$(call AutoLoad,42,rtl8366,1) +endef + +define KernelPackage/dsa-rtl8366rb/description + DSA based kernel modules for the Realtek RTL8366RB switch family +endef + +$(eval $(call KernelPackage,dsa-rtl8366rb)) + + +define KernelPackage/dsa-rtl8365mb + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Realtek RTL8365MB switch DSA support + DEPENDS:=+kmod-dsa-realtek @!TARGET_x86 @!TARGET_bcm47xx @!TARGET_uml + KCONFIG:= \ + CONFIG_NET_DSA_REALTEK_RTL8365MB \ + CONFIG_NET_DSA_TAG_RTL8_4 + FILES:= \ + $(LINUX_DIR)/drivers/net/dsa/realtek/rtl8365mb.ko \ + $(LINUX_DIR)/net/dsa/tag_rtl8_4.ko + AUTOLOAD:=$(call AutoLoad,42,rtl8365mb,1) +endef + +define KernelPackage/dsa-rtl8365mb/description + DSA based kernel modules for the Realtek RTL8365MB switch family +endef + +$(eval $(call KernelPackage,dsa-rtl8365mb)) + + define KernelPackage/swconfig SUBMENU:=$(NETWORK_DEVICES_MENU) TITLE:=switch configuration API diff --git a/target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch b/target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch new file mode 100644 index 0000000000..d19338e7df --- /dev/null +++ b/target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch @@ -0,0 +1,66 @@ +From d48a5472b8f2b29800bb25913f9403765005f1bc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= +Date: Mon, 18 Sep 2023 21:19:14 +0200 +Subject: [PATCH] net: dsa: realtek: Convert to platform remove callback + returning void +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The .remove() callback for a platform driver returns an int which makes +many driver authors wrongly assume it's possible to do error handling by +returning an error code. However the value returned is ignored (apart +from emitting a warning) and this typically results in resource leaks. +To improve here there is a quest to make the remove callback return +void. In the first step of this quest all drivers are converted to +.remove_new() which already returns void. Eventually after all drivers +are converted, .remove_new() is renamed to .remove(). + +Trivially convert this driver from always returning zero in the remove +callback to the void returning variant. + +Signed-off-by: Uwe Kleine-König +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Reviewed-by: Alvin Šipraga +Signed-off-by: David S. Miller +Signed-off-by: Luiz Angelo Daros de Luca +--- + drivers/net/dsa/realtek/realtek-smi.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -506,12 +506,12 @@ static int realtek_smi_probe(struct plat + return 0; + } + +-static int realtek_smi_remove(struct platform_device *pdev) ++static void realtek_smi_remove(struct platform_device *pdev) + { + struct realtek_priv *priv = platform_get_drvdata(pdev); + + if (!priv) +- return 0; ++ return; + + dsa_unregister_switch(priv->ds); + if (priv->slave_mii_bus) +@@ -520,8 +520,6 @@ static int realtek_smi_remove(struct pla + /* leave the device reset asserted */ + if (priv->reset) + gpiod_set_value(priv->reset, 1); +- +- return 0; + } + + static void realtek_smi_shutdown(struct platform_device *pdev) +@@ -559,7 +557,7 @@ static struct platform_driver realtek_sm + .of_match_table = realtek_smi_of_match, + }, + .probe = realtek_smi_probe, +- .remove = realtek_smi_remove, ++ .remove_new = realtek_smi_remove, + .shutdown = realtek_smi_shutdown, + }; + module_platform_driver(realtek_smi_driver); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch b/target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch new file mode 100644 index 0000000000..82958ecff8 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch @@ -0,0 +1,30 @@ +From 33f4336cbd32c21717b60d013693a0bd51a27db6 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:37 -0300 +Subject: net: dsa: realtek: drop cleanup from realtek_ops +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It was never used and never referenced. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Alvin Šipraga +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek.h | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -91,7 +91,6 @@ struct realtek_ops { + int (*detect)(struct realtek_priv *priv); + int (*reset_chip)(struct realtek_priv *priv); + int (*setup)(struct realtek_priv *priv); +- void (*cleanup)(struct realtek_priv *priv); + int (*get_mib_counter)(struct realtek_priv *priv, + int port, + struct rtl8366_mib_counter *mib, diff --git a/target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch b/target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch new file mode 100644 index 0000000000..d50ea1bb30 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch @@ -0,0 +1,146 @@ +From ded3813b44fe11a3bbd2c9d7df8870e8c19a7ccd Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:38 -0300 +Subject: net: dsa: realtek: introduce REALTEK_DSA namespace + +Create a namespace to group the exported symbols. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-mdio.c | 1 + + drivers/net/dsa/realtek/realtek-smi.c | 1 + + drivers/net/dsa/realtek/rtl8365mb.c | 1 + + drivers/net/dsa/realtek/rtl8366-core.c | 22 +++++++++++----------- + drivers/net/dsa/realtek/rtl8366rb.c | 1 + + 5 files changed, 15 insertions(+), 11 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -288,3 +288,4 @@ mdio_module_driver(realtek_mdio_driver); + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); + MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -565,3 +565,4 @@ module_platform_driver(realtek_smi_drive + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -2178,3 +2178,4 @@ EXPORT_SYMBOL_GPL(rtl8365mb_variant); + MODULE_AUTHOR("Alvin Šipraga "); + MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/rtl8366-core.c ++++ b/drivers/net/dsa/realtek/rtl8366-core.c +@@ -34,7 +34,7 @@ int rtl8366_mc_is_used(struct realtek_pr + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_mc_is_used); ++EXPORT_SYMBOL_NS_GPL(rtl8366_mc_is_used, REALTEK_DSA); + + /** + * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration +@@ -187,7 +187,7 @@ int rtl8366_set_vlan(struct realtek_priv + + return ret; + } +-EXPORT_SYMBOL_GPL(rtl8366_set_vlan); ++EXPORT_SYMBOL_NS_GPL(rtl8366_set_vlan, REALTEK_DSA); + + int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, + unsigned int vid) +@@ -217,7 +217,7 @@ int rtl8366_set_pvid(struct realtek_priv + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_set_pvid); ++EXPORT_SYMBOL_NS_GPL(rtl8366_set_pvid, REALTEK_DSA); + + int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable) + { +@@ -243,7 +243,7 @@ int rtl8366_enable_vlan4k(struct realtek + priv->vlan4k_enabled = enable; + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k); ++EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan4k, REALTEK_DSA); + + int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable) + { +@@ -265,7 +265,7 @@ int rtl8366_enable_vlan(struct realtek_p + + return ret; + } +-EXPORT_SYMBOL_GPL(rtl8366_enable_vlan); ++EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan, REALTEK_DSA); + + int rtl8366_reset_vlan(struct realtek_priv *priv) + { +@@ -290,7 +290,7 @@ int rtl8366_reset_vlan(struct realtek_pr + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_reset_vlan); ++EXPORT_SYMBOL_NS_GPL(rtl8366_reset_vlan, REALTEK_DSA); + + int rtl8366_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, +@@ -345,7 +345,7 @@ int rtl8366_vlan_add(struct dsa_switch * + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_vlan_add); ++EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_add, REALTEK_DSA); + + int rtl8366_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +@@ -389,7 +389,7 @@ int rtl8366_vlan_del(struct dsa_switch * + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_vlan_del); ++EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_del, REALTEK_DSA); + + void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, + uint8_t *data) +@@ -407,7 +407,7 @@ void rtl8366_get_strings(struct dsa_swit + mib->name, ETH_GSTRING_LEN); + } + } +-EXPORT_SYMBOL_GPL(rtl8366_get_strings); ++EXPORT_SYMBOL_NS_GPL(rtl8366_get_strings, REALTEK_DSA); + + int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset) + { +@@ -421,7 +421,7 @@ int rtl8366_get_sset_count(struct dsa_sw + + return priv->num_mib_counters; + } +-EXPORT_SYMBOL_GPL(rtl8366_get_sset_count); ++EXPORT_SYMBOL_NS_GPL(rtl8366_get_sset_count, REALTEK_DSA); + + void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) + { +@@ -445,4 +445,4 @@ void rtl8366_get_ethtool_stats(struct ds + data[i] = mibvalue; + } + } +-EXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats); ++EXPORT_SYMBOL_NS_GPL(rtl8366_get_ethtool_stats, REALTEK_DSA); +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -1852,3 +1852,4 @@ EXPORT_SYMBOL_GPL(rtl8366rb_variant); + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(REALTEK_DSA); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch b/target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch new file mode 100644 index 0000000000..aa6dfcf7e6 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch @@ -0,0 +1,539 @@ +From bce254b839abe67577bebdef0838796af409c229 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:39 -0300 +Subject: net: dsa: realtek: convert variants into real drivers + +Previously, the interface modules realtek-smi and realtek-mdio served as +a platform and an MDIO driver, respectively. Each interface module +redundantly specified the same compatible strings for both variants and +referenced symbols from the variants. + +Now, each variant module has been transformed into a unified driver +serving both as a platform and an MDIO driver. This modification +reverses the relationship between the interface and variant modules, +with the variant module now utilizing symbols from the interface +modules. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/Kconfig | 20 +++---- + drivers/net/dsa/realtek/realtek-mdio.c | 68 +++++++++++++++--------- + drivers/net/dsa/realtek/realtek-mdio.h | 48 +++++++++++++++++ + drivers/net/dsa/realtek/realtek-smi.c | 73 +++++++++++++++----------- + drivers/net/dsa/realtek/realtek-smi.h | 48 +++++++++++++++++ + drivers/net/dsa/realtek/rtl8365mb.c | 54 ++++++++++++++++++- + drivers/net/dsa/realtek/rtl8366rb.c | 54 ++++++++++++++++++- + 7 files changed, 292 insertions(+), 73 deletions(-) + create mode 100644 drivers/net/dsa/realtek/realtek-mdio.h + create mode 100644 drivers/net/dsa/realtek/realtek-smi.h + +--- a/drivers/net/dsa/realtek/Kconfig ++++ b/drivers/net/dsa/realtek/Kconfig +@@ -16,37 +16,29 @@ menuconfig NET_DSA_REALTEK + if NET_DSA_REALTEK + + config NET_DSA_REALTEK_MDIO +- tristate "Realtek MDIO interface driver" ++ tristate "Realtek MDIO interface support" + depends on OF +- depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB +- depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB +- depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB + help + Select to enable support for registering switches configured + through MDIO. + + config NET_DSA_REALTEK_SMI +- tristate "Realtek SMI interface driver" ++ tristate "Realtek SMI interface support" + depends on OF +- depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB +- depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB +- depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB + help + Select to enable support for registering switches connected + through SMI. + + config NET_DSA_REALTEK_RTL8365MB +- tristate "Realtek RTL8365MB switch subdriver" +- imply NET_DSA_REALTEK_SMI +- imply NET_DSA_REALTEK_MDIO ++ tristate "Realtek RTL8365MB switch driver" ++ depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL8_4 + help + Select to enable support for Realtek RTL8365MB-VC and RTL8367S. + + config NET_DSA_REALTEK_RTL8366RB +- tristate "Realtek RTL8366RB switch subdriver" +- imply NET_DSA_REALTEK_SMI +- imply NET_DSA_REALTEK_MDIO ++ tristate "Realtek RTL8366RB switch driver" ++ depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL4_A + help + Select to enable support for Realtek RTL8366RB. +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -25,6 +25,7 @@ + #include + + #include "realtek.h" ++#include "realtek-mdio.h" + + /* Read/write via mdiobus */ + #define REALTEK_MDIO_CTRL0_REG 31 +@@ -140,7 +141,19 @@ static const struct regmap_config realte + .disable_locking = true, + }; + +-static int realtek_mdio_probe(struct mdio_device *mdiodev) ++/** ++ * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch ++ * @mdiodev: mdio_device to probe on. ++ * ++ * This function should be used as the .probe in an mdio_driver. It ++ * initializes realtek_priv and read data from the device-tree node. The switch ++ * is hard reset if a method is provided. It checks the switch chip ID and, ++ * finally, a DSA switch is registered. ++ * ++ * Context: Can sleep. Takes and releases priv->map_lock. ++ * Return: Returns 0 on success, a negative error on failure. ++ */ ++int realtek_mdio_probe(struct mdio_device *mdiodev) + { + struct realtek_priv *priv; + struct device *dev = &mdiodev->dev; +@@ -235,8 +248,20 @@ static int realtek_mdio_probe(struct mdi + + return 0; + } ++EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA); + +-static void realtek_mdio_remove(struct mdio_device *mdiodev) ++/** ++ * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch ++ * @mdiodev: mdio_device to be removed. ++ * ++ * This function should be used as the .remove_new in an mdio_driver. First ++ * it unregisters the DSA switch and cleans internal data. If a method is ++ * provided, the hard reset is asserted to avoid traffic leakage. ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void realtek_mdio_remove(struct mdio_device *mdiodev) + { + struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); + +@@ -249,8 +274,21 @@ static void realtek_mdio_remove(struct m + if (priv->reset) + gpiod_set_value(priv->reset, 1); + } ++EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA); + +-static void realtek_mdio_shutdown(struct mdio_device *mdiodev) ++/** ++ * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch ++ * @mdiodev: mdio_device shutting down. ++ * ++ * This function should be used as the .shutdown in an mdio_driver. It shuts ++ * down the DSA switch and cleans the platform driver data, to prevent ++ * realtek_mdio_remove() from running afterwards, which is possible if the ++ * parent bus implements its own .shutdown() as .remove(). ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void realtek_mdio_shutdown(struct mdio_device *mdiodev) + { + struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); + +@@ -261,29 +299,7 @@ static void realtek_mdio_shutdown(struct + + dev_set_drvdata(&mdiodev->dev, NULL); + } +- +-static const struct of_device_id realtek_mdio_of_match[] = { +-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) +- { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, +-#endif +-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) +- { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, +-#endif +- { /* sentinel */ }, +-}; +-MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); +- +-static struct mdio_driver realtek_mdio_driver = { +- .mdiodrv.driver = { +- .name = "realtek-mdio", +- .of_match_table = realtek_mdio_of_match, +- }, +- .probe = realtek_mdio_probe, +- .remove = realtek_mdio_remove, +- .shutdown = realtek_mdio_shutdown, +-}; +- +-mdio_module_driver(realtek_mdio_driver); ++EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA); + + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); + MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); +--- /dev/null ++++ b/drivers/net/dsa/realtek/realtek-mdio.h +@@ -0,0 +1,48 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++ ++#ifndef _REALTEK_MDIO_H ++#define _REALTEK_MDIO_H ++ ++#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) ++ ++static inline int realtek_mdio_driver_register(struct mdio_driver *drv) ++{ ++ return mdio_driver_register(drv); ++} ++ ++static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv) ++{ ++ mdio_driver_unregister(drv); ++} ++ ++int realtek_mdio_probe(struct mdio_device *mdiodev); ++void realtek_mdio_remove(struct mdio_device *mdiodev); ++void realtek_mdio_shutdown(struct mdio_device *mdiodev); ++ ++#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */ ++ ++static inline int realtek_mdio_driver_register(struct mdio_driver *drv) ++{ ++ return 0; ++} ++ ++static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv) ++{ ++} ++ ++static inline int realtek_mdio_probe(struct mdio_device *mdiodev) ++{ ++ return -ENOENT; ++} ++ ++static inline void realtek_mdio_remove(struct mdio_device *mdiodev) ++{ ++} ++ ++static inline void realtek_mdio_shutdown(struct mdio_device *mdiodev) ++{ ++} ++ ++#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */ ++ ++#endif /* _REALTEK_MDIO_H */ +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -40,6 +40,7 @@ + #include + + #include "realtek.h" ++#include "realtek-smi.h" + + #define REALTEK_SMI_ACK_RETRY_COUNT 5 + +@@ -408,7 +409,19 @@ err_put_node: + return ret; + } + +-static int realtek_smi_probe(struct platform_device *pdev) ++/** ++ * realtek_smi_probe() - Probe a platform device for an SMI-connected switch ++ * @pdev: platform_device to probe on. ++ * ++ * This function should be used as the .probe in a platform_driver. It ++ * initializes realtek_priv and read data from the device-tree node. The switch ++ * is hard reset if a method is provided. It checks the switch chip ID and, ++ * finally, a DSA switch is registered. ++ * ++ * Context: Can sleep. Takes and releases priv->map_lock. ++ * Return: Returns 0 on success, a negative error on failure. ++ */ ++int realtek_smi_probe(struct platform_device *pdev) + { + const struct realtek_variant *var; + struct device *dev = &pdev->dev; +@@ -505,8 +518,20 @@ static int realtek_smi_probe(struct plat + } + return 0; + } ++EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA); + +-static void realtek_smi_remove(struct platform_device *pdev) ++/** ++ * realtek_smi_remove() - Remove the driver of a SMI-connected switch ++ * @pdev: platform_device to be removed. ++ * ++ * This function should be used as the .remove_new in a platform_driver. First ++ * it unregisters the DSA switch and cleans internal data. If a method is ++ * provided, the hard reset is asserted to avoid traffic leakage. ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void realtek_smi_remove(struct platform_device *pdev) + { + struct realtek_priv *priv = platform_get_drvdata(pdev); + +@@ -521,8 +546,21 @@ static void realtek_smi_remove(struct pl + if (priv->reset) + gpiod_set_value(priv->reset, 1); + } ++EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA); + +-static void realtek_smi_shutdown(struct platform_device *pdev) ++/** ++ * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch ++ * @pdev: platform_device shutting down. ++ * ++ * This function should be used as the .shutdown in a platform_driver. It shuts ++ * down the DSA switch and cleans the platform driver data, to prevent ++ * realtek_smi_remove() from running afterwards, which is possible if the ++ * parent bus implements its own .shutdown() as .remove(). ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void realtek_smi_shutdown(struct platform_device *pdev) + { + struct realtek_priv *priv = platform_get_drvdata(pdev); + +@@ -533,34 +571,7 @@ static void realtek_smi_shutdown(struct + + platform_set_drvdata(pdev, NULL); + } +- +-static const struct of_device_id realtek_smi_of_match[] = { +-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) +- { +- .compatible = "realtek,rtl8366rb", +- .data = &rtl8366rb_variant, +- }, +-#endif +-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) +- { +- .compatible = "realtek,rtl8365mb", +- .data = &rtl8365mb_variant, +- }, +-#endif +- { /* sentinel */ }, +-}; +-MODULE_DEVICE_TABLE(of, realtek_smi_of_match); +- +-static struct platform_driver realtek_smi_driver = { +- .driver = { +- .name = "realtek-smi", +- .of_match_table = realtek_smi_of_match, +- }, +- .probe = realtek_smi_probe, +- .remove_new = realtek_smi_remove, +- .shutdown = realtek_smi_shutdown, +-}; +-module_platform_driver(realtek_smi_driver); ++EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA); + + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); +--- /dev/null ++++ b/drivers/net/dsa/realtek/realtek-smi.h +@@ -0,0 +1,48 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++ ++#ifndef _REALTEK_SMI_H ++#define _REALTEK_SMI_H ++ ++#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) ++ ++static inline int realtek_smi_driver_register(struct platform_driver *drv) ++{ ++ return platform_driver_register(drv); ++} ++ ++static inline void realtek_smi_driver_unregister(struct platform_driver *drv) ++{ ++ platform_driver_unregister(drv); ++} ++ ++int realtek_smi_probe(struct platform_device *pdev); ++void realtek_smi_remove(struct platform_device *pdev); ++void realtek_smi_shutdown(struct platform_device *pdev); ++ ++#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */ ++ ++static inline int realtek_smi_driver_register(struct platform_driver *drv) ++{ ++ return 0; ++} ++ ++static inline void realtek_smi_driver_unregister(struct platform_driver *drv) ++{ ++} ++ ++static inline int realtek_smi_probe(struct platform_device *pdev) ++{ ++ return -ENOENT; ++} ++ ++static inline void realtek_smi_remove(struct platform_device *pdev) ++{ ++} ++ ++static inline void realtek_smi_shutdown(struct platform_device *pdev) ++{ ++} ++ ++#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */ ++ ++#endif /* _REALTEK_SMI_H */ +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -101,6 +101,8 @@ + #include + + #include "realtek.h" ++#include "realtek-smi.h" ++#include "realtek-mdio.h" + + /* Family-specific data and limits */ + #define RTL8365MB_PHYADDRMAX 7 +@@ -2173,7 +2175,57 @@ const struct realtek_variant rtl8365mb_v + .cmd_write = 0xb8, + .chip_data_sz = sizeof(struct rtl8365mb), + }; +-EXPORT_SYMBOL_GPL(rtl8365mb_variant); ++ ++static const struct of_device_id rtl8365mb_of_match[] = { ++ { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, rtl8365mb_of_match); ++ ++static struct platform_driver rtl8365mb_smi_driver = { ++ .driver = { ++ .name = "rtl8365mb-smi", ++ .of_match_table = rtl8365mb_of_match, ++ }, ++ .probe = realtek_smi_probe, ++ .remove_new = realtek_smi_remove, ++ .shutdown = realtek_smi_shutdown, ++}; ++ ++static struct mdio_driver rtl8365mb_mdio_driver = { ++ .mdiodrv.driver = { ++ .name = "rtl8365mb-mdio", ++ .of_match_table = rtl8365mb_of_match, ++ }, ++ .probe = realtek_mdio_probe, ++ .remove = realtek_mdio_remove, ++ .shutdown = realtek_mdio_shutdown, ++}; ++ ++static int rtl8365mb_init(void) ++{ ++ int ret; ++ ++ ret = realtek_mdio_driver_register(&rtl8365mb_mdio_driver); ++ if (ret) ++ return ret; ++ ++ ret = realtek_smi_driver_register(&rtl8365mb_smi_driver); ++ if (ret) { ++ realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver); ++ return ret; ++ } ++ ++ return 0; ++} ++module_init(rtl8365mb_init); ++ ++static void __exit rtl8365mb_exit(void) ++{ ++ realtek_smi_driver_unregister(&rtl8365mb_smi_driver); ++ realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver); ++} ++module_exit(rtl8365mb_exit); + + MODULE_AUTHOR("Alvin Šipraga "); + MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -22,6 +22,8 @@ + #include + + #include "realtek.h" ++#include "realtek-smi.h" ++#include "realtek-mdio.h" + + #define RTL8366RB_PORT_NUM_CPU 5 + #define RTL8366RB_NUM_PORTS 6 +@@ -1847,7 +1849,57 @@ const struct realtek_variant rtl8366rb_v + .cmd_write = 0xa8, + .chip_data_sz = sizeof(struct rtl8366rb), + }; +-EXPORT_SYMBOL_GPL(rtl8366rb_variant); ++ ++static const struct of_device_id rtl8366rb_of_match[] = { ++ { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, rtl8366rb_of_match); ++ ++static struct platform_driver rtl8366rb_smi_driver = { ++ .driver = { ++ .name = "rtl8366rb-smi", ++ .of_match_table = rtl8366rb_of_match, ++ }, ++ .probe = realtek_smi_probe, ++ .remove_new = realtek_smi_remove, ++ .shutdown = realtek_smi_shutdown, ++}; ++ ++static struct mdio_driver rtl8366rb_mdio_driver = { ++ .mdiodrv.driver = { ++ .name = "rtl8366rb-mdio", ++ .of_match_table = rtl8366rb_of_match, ++ }, ++ .probe = realtek_mdio_probe, ++ .remove = realtek_mdio_remove, ++ .shutdown = realtek_mdio_shutdown, ++}; ++ ++static int rtl8366rb_init(void) ++{ ++ int ret; ++ ++ ret = realtek_mdio_driver_register(&rtl8366rb_mdio_driver); ++ if (ret) ++ return ret; ++ ++ ret = realtek_smi_driver_register(&rtl8366rb_smi_driver); ++ if (ret) { ++ realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver); ++ return ret; ++ } ++ ++ return 0; ++} ++module_init(rtl8366rb_init); ++ ++static void __exit rtl8366rb_exit(void) ++{ ++ realtek_smi_driver_unregister(&rtl8366rb_smi_driver); ++ realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver); ++} ++module_exit(rtl8366rb_exit); + + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch b/target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch new file mode 100644 index 0000000000..b0e074b2c9 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch @@ -0,0 +1,91 @@ +From 4667a1db2f550d23e01ba655fce331196ead6e92 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:40 -0300 +Subject: net: dsa: realtek: keep variant reference in + realtek_priv + +Instead of copying values from the variant, we can keep a reference in +realtek_priv. + +This is a preliminary change for sharing code betwen interfaces. It will +allow to move most of the probe into a common module while still allow +code specific to each interface to read variant fields. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-mdio.c | 4 +--- + drivers/net/dsa/realtek/realtek-smi.c | 10 ++++------ + drivers/net/dsa/realtek/realtek.h | 5 ++--- + 3 files changed, 7 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -196,9 +196,7 @@ int realtek_mdio_probe(struct mdio_devic + priv->dev = &mdiodev->dev; + priv->chip_data = (void *)priv + sizeof(*priv); + +- priv->clk_delay = var->clk_delay; +- priv->cmd_read = var->cmd_read; +- priv->cmd_write = var->cmd_write; ++ priv->variant = var; + priv->ops = var->ops; + + priv->write_reg_noack = realtek_mdio_write; +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -46,7 +46,7 @@ + + static inline void realtek_smi_clk_delay(struct realtek_priv *priv) + { +- ndelay(priv->clk_delay); ++ ndelay(priv->variant->clk_delay); + } + + static void realtek_smi_start(struct realtek_priv *priv) +@@ -209,7 +209,7 @@ static int realtek_smi_read_reg(struct r + realtek_smi_start(priv); + + /* Send READ command */ +- ret = realtek_smi_write_byte(priv, priv->cmd_read); ++ ret = realtek_smi_write_byte(priv, priv->variant->cmd_read); + if (ret) + goto out; + +@@ -250,7 +250,7 @@ static int realtek_smi_write_reg(struct + realtek_smi_start(priv); + + /* Send WRITE command */ +- ret = realtek_smi_write_byte(priv, priv->cmd_write); ++ ret = realtek_smi_write_byte(priv, priv->variant->cmd_write); + if (ret) + goto out; + +@@ -459,9 +459,7 @@ int realtek_smi_probe(struct platform_de + + /* Link forward and backward */ + priv->dev = dev; +- priv->clk_delay = var->clk_delay; +- priv->cmd_read = var->cmd_read; +- priv->cmd_write = var->cmd_write; ++ priv->variant = var; + priv->ops = var->ops; + + priv->setup_interface = realtek_smi_setup_mdio; +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -58,9 +58,8 @@ struct realtek_priv { + struct mii_bus *bus; + int mdio_addr; + +- unsigned int clk_delay; +- u8 cmd_read; +- u8 cmd_write; ++ const struct realtek_variant *variant; ++ + spinlock_t lock; /* Locks around command writes */ + struct dsa_switch *ds; + struct irq_domain *irqdomain; diff --git a/target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch b/target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch new file mode 100644 index 0000000000..544b1b2992 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch @@ -0,0 +1,875 @@ +From 8be040ecd94c1a9a137927d18534edfae0a9b68a Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:41 -0300 +Subject: net: dsa: realtek: common rtl83xx module + +Some code can be shared between both interface modules (MDIO and SMI) +and among variants. These interface functions migrated to a common +module: + +- rtl83xx_lock +- rtl83xx_unlock +- rtl83xx_probe +- rtl83xx_register_switch +- rtl83xx_unregister_switch +- rtl83xx_shutdown +- rtl83xx_remove + +The reset during probe was moved to the end of the common probe. This way, +we avoid a reset if anything else fails. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/Makefile | 2 + + drivers/net/dsa/realtek/realtek-mdio.c | 152 +++------------- + drivers/net/dsa/realtek/realtek-smi.c | 166 ++++------------- + drivers/net/dsa/realtek/realtek.h | 1 + + drivers/net/dsa/realtek/rtl8365mb.c | 9 +- + drivers/net/dsa/realtek/rtl8366rb.c | 9 +- + drivers/net/dsa/realtek/rtl83xx.c | 235 +++++++++++++++++++++++++ + drivers/net/dsa/realtek/rtl83xx.h | 21 +++ + 8 files changed, 322 insertions(+), 273 deletions(-) + create mode 100644 drivers/net/dsa/realtek/rtl83xx.c + create mode 100644 drivers/net/dsa/realtek/rtl83xx.h + +--- a/drivers/net/dsa/realtek/Makefile ++++ b/drivers/net/dsa/realtek/Makefile +@@ -1,4 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_NET_DSA_REALTEK) += realtek_dsa.o ++realtek_dsa-objs := rtl83xx.o + obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o + obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o + obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -26,6 +26,7 @@ + + #include "realtek.h" + #include "realtek-mdio.h" ++#include "rtl83xx.h" + + /* Read/write via mdiobus */ + #define REALTEK_MDIO_CTRL0_REG 31 +@@ -100,147 +101,41 @@ out_unlock: + return ret; + } + +-static void realtek_mdio_lock(void *ctx) +-{ +- struct realtek_priv *priv = ctx; +- +- mutex_lock(&priv->map_lock); +-} +- +-static void realtek_mdio_unlock(void *ctx) +-{ +- struct realtek_priv *priv = ctx; +- +- mutex_unlock(&priv->map_lock); +-} +- +-static const struct regmap_config realtek_mdio_regmap_config = { +- .reg_bits = 10, /* A4..A0 R4..R0 */ +- .val_bits = 16, +- .reg_stride = 1, +- /* PHY regs are at 0x8000 */ +- .max_register = 0xffff, +- .reg_format_endian = REGMAP_ENDIAN_BIG, ++static const struct realtek_interface_info realtek_mdio_info = { + .reg_read = realtek_mdio_read, + .reg_write = realtek_mdio_write, +- .cache_type = REGCACHE_NONE, +- .lock = realtek_mdio_lock, +- .unlock = realtek_mdio_unlock, +-}; +- +-static const struct regmap_config realtek_mdio_nolock_regmap_config = { +- .reg_bits = 10, /* A4..A0 R4..R0 */ +- .val_bits = 16, +- .reg_stride = 1, +- /* PHY regs are at 0x8000 */ +- .max_register = 0xffff, +- .reg_format_endian = REGMAP_ENDIAN_BIG, +- .reg_read = realtek_mdio_read, +- .reg_write = realtek_mdio_write, +- .cache_type = REGCACHE_NONE, +- .disable_locking = true, + }; + + /** + * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch + * @mdiodev: mdio_device to probe on. + * +- * This function should be used as the .probe in an mdio_driver. It +- * initializes realtek_priv and read data from the device-tree node. The switch +- * is hard reset if a method is provided. It checks the switch chip ID and, +- * finally, a DSA switch is registered. ++ * This function should be used as the .probe in an mdio_driver. After ++ * calling the common probe function for both interfaces, it initializes the ++ * values specific for MDIO-connected devices. Finally, it calls a common ++ * function to register the DSA switch. + * + * Context: Can sleep. Takes and releases priv->map_lock. + * Return: Returns 0 on success, a negative error on failure. + */ + int realtek_mdio_probe(struct mdio_device *mdiodev) + { +- struct realtek_priv *priv; + struct device *dev = &mdiodev->dev; +- const struct realtek_variant *var; +- struct regmap_config rc; +- struct device_node *np; ++ struct realtek_priv *priv; + int ret; + +- var = of_device_get_match_data(dev); +- if (!var) +- return -EINVAL; +- +- priv = devm_kzalloc(&mdiodev->dev, +- size_add(sizeof(*priv), var->chip_data_sz), +- GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- mutex_init(&priv->map_lock); +- +- rc = realtek_mdio_regmap_config; +- rc.lock_arg = priv; +- priv->map = devm_regmap_init(dev, NULL, priv, &rc); +- if (IS_ERR(priv->map)) { +- ret = PTR_ERR(priv->map); +- dev_err(dev, "regmap init failed: %d\n", ret); +- return ret; +- } ++ priv = rtl83xx_probe(dev, &realtek_mdio_info); ++ if (IS_ERR(priv)) ++ return PTR_ERR(priv); + +- rc = realtek_mdio_nolock_regmap_config; +- priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); +- if (IS_ERR(priv->map_nolock)) { +- ret = PTR_ERR(priv->map_nolock); +- dev_err(dev, "regmap init failed: %d\n", ret); +- return ret; +- } +- +- priv->mdio_addr = mdiodev->addr; + priv->bus = mdiodev->bus; +- priv->dev = &mdiodev->dev; +- priv->chip_data = (void *)priv + sizeof(*priv); +- +- priv->variant = var; +- priv->ops = var->ops; +- ++ priv->mdio_addr = mdiodev->addr; + priv->write_reg_noack = realtek_mdio_write; ++ priv->ds_ops = priv->variant->ds_ops_mdio; + +- np = dev->of_node; +- +- dev_set_drvdata(dev, priv); +- +- /* TODO: if power is software controlled, set up any regulators here */ +- priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); +- +- priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); +- if (IS_ERR(priv->reset)) { +- dev_err(dev, "failed to get RESET GPIO\n"); +- return PTR_ERR(priv->reset); +- } +- +- if (priv->reset) { +- gpiod_set_value(priv->reset, 1); +- dev_dbg(dev, "asserted RESET\n"); +- msleep(REALTEK_HW_STOP_DELAY); +- gpiod_set_value(priv->reset, 0); +- msleep(REALTEK_HW_START_DELAY); +- dev_dbg(dev, "deasserted RESET\n"); +- } +- +- ret = priv->ops->detect(priv); +- if (ret) { +- dev_err(dev, "unable to detect switch\n"); +- return ret; +- } +- +- priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); +- if (!priv->ds) +- return -ENOMEM; +- +- priv->ds->dev = dev; +- priv->ds->num_ports = priv->num_ports; +- priv->ds->priv = priv; +- priv->ds->ops = var->ds_ops_mdio; +- +- ret = dsa_register_switch(priv->ds); ++ ret = rtl83xx_register_switch(priv); + if (ret) { +- dev_err(priv->dev, "unable to register switch ret = %d\n", ret); ++ rtl83xx_remove(priv); + return ret; + } + +@@ -253,8 +148,7 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, + * @mdiodev: mdio_device to be removed. + * + * This function should be used as the .remove_new in an mdio_driver. First +- * it unregisters the DSA switch and cleans internal data. If a method is +- * provided, the hard reset is asserted to avoid traffic leakage. ++ * it unregisters the DSA switch and then it calls the common remove function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -266,11 +160,9 @@ void realtek_mdio_remove(struct mdio_dev + if (!priv) + return; + +- dsa_unregister_switch(priv->ds); ++ rtl83xx_unregister_switch(priv); + +- /* leave the device reset asserted */ +- if (priv->reset) +- gpiod_set_value(priv->reset, 1); ++ rtl83xx_remove(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA); + +@@ -278,10 +170,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove + * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch + * @mdiodev: mdio_device shutting down. + * +- * This function should be used as the .shutdown in an mdio_driver. It shuts +- * down the DSA switch and cleans the platform driver data, to prevent +- * realtek_mdio_remove() from running afterwards, which is possible if the +- * parent bus implements its own .shutdown() as .remove(). ++ * This function should be used as the .shutdown in a platform_driver. It calls ++ * the common shutdown function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -293,9 +183,7 @@ void realtek_mdio_shutdown(struct mdio_d + if (!priv) + return; + +- dsa_switch_shutdown(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); ++ rtl83xx_shutdown(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA); + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -41,6 +41,7 @@ + + #include "realtek.h" + #include "realtek-smi.h" ++#include "rtl83xx.h" + + #define REALTEK_SMI_ACK_RETRY_COUNT 5 + +@@ -311,47 +312,6 @@ static int realtek_smi_read(void *ctx, u + return realtek_smi_read_reg(priv, reg, val); + } + +-static void realtek_smi_lock(void *ctx) +-{ +- struct realtek_priv *priv = ctx; +- +- mutex_lock(&priv->map_lock); +-} +- +-static void realtek_smi_unlock(void *ctx) +-{ +- struct realtek_priv *priv = ctx; +- +- mutex_unlock(&priv->map_lock); +-} +- +-static const struct regmap_config realtek_smi_regmap_config = { +- .reg_bits = 10, /* A4..A0 R4..R0 */ +- .val_bits = 16, +- .reg_stride = 1, +- /* PHY regs are at 0x8000 */ +- .max_register = 0xffff, +- .reg_format_endian = REGMAP_ENDIAN_BIG, +- .reg_read = realtek_smi_read, +- .reg_write = realtek_smi_write, +- .cache_type = REGCACHE_NONE, +- .lock = realtek_smi_lock, +- .unlock = realtek_smi_unlock, +-}; +- +-static const struct regmap_config realtek_smi_nolock_regmap_config = { +- .reg_bits = 10, /* A4..A0 R4..R0 */ +- .val_bits = 16, +- .reg_stride = 1, +- /* PHY regs are at 0x8000 */ +- .max_register = 0xffff, +- .reg_format_endian = REGMAP_ENDIAN_BIG, +- .reg_read = realtek_smi_read, +- .reg_write = realtek_smi_write, +- .cache_type = REGCACHE_NONE, +- .disable_locking = true, +-}; +- + static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) + { + struct realtek_priv *priv = bus->priv; +@@ -409,111 +369,56 @@ err_put_node: + return ret; + } + ++static const struct realtek_interface_info realtek_smi_info = { ++ .reg_read = realtek_smi_read, ++ .reg_write = realtek_smi_write, ++}; ++ + /** + * realtek_smi_probe() - Probe a platform device for an SMI-connected switch + * @pdev: platform_device to probe on. + * +- * This function should be used as the .probe in a platform_driver. It +- * initializes realtek_priv and read data from the device-tree node. The switch +- * is hard reset if a method is provided. It checks the switch chip ID and, +- * finally, a DSA switch is registered. ++ * This function should be used as the .probe in a platform_driver. After ++ * calling the common probe function for both interfaces, it initializes the ++ * values specific for SMI-connected devices. Finally, it calls a common ++ * function to register the DSA switch. + * + * Context: Can sleep. Takes and releases priv->map_lock. + * Return: Returns 0 on success, a negative error on failure. + */ + int realtek_smi_probe(struct platform_device *pdev) + { +- const struct realtek_variant *var; + struct device *dev = &pdev->dev; + struct realtek_priv *priv; +- struct regmap_config rc; +- struct device_node *np; + int ret; + +- var = of_device_get_match_data(dev); +- np = dev->of_node; +- +- priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- priv->chip_data = (void *)priv + sizeof(*priv); +- +- mutex_init(&priv->map_lock); +- +- rc = realtek_smi_regmap_config; +- rc.lock_arg = priv; +- priv->map = devm_regmap_init(dev, NULL, priv, &rc); +- if (IS_ERR(priv->map)) { +- ret = PTR_ERR(priv->map); +- dev_err(dev, "regmap init failed: %d\n", ret); +- return ret; +- } +- +- rc = realtek_smi_nolock_regmap_config; +- priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); +- if (IS_ERR(priv->map_nolock)) { +- ret = PTR_ERR(priv->map_nolock); +- dev_err(dev, "regmap init failed: %d\n", ret); +- return ret; +- } +- +- /* Link forward and backward */ +- priv->dev = dev; +- priv->variant = var; +- priv->ops = var->ops; +- +- priv->setup_interface = realtek_smi_setup_mdio; +- priv->write_reg_noack = realtek_smi_write_reg_noack; +- +- dev_set_drvdata(dev, priv); +- spin_lock_init(&priv->lock); +- +- /* TODO: if power is software controlled, set up any regulators here */ +- +- priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); +- if (IS_ERR(priv->reset)) { +- dev_err(dev, "failed to get RESET GPIO\n"); +- return PTR_ERR(priv->reset); +- } +- if (priv->reset) { +- gpiod_set_value(priv->reset, 1); +- dev_dbg(dev, "asserted RESET\n"); +- msleep(REALTEK_HW_STOP_DELAY); +- gpiod_set_value(priv->reset, 0); +- msleep(REALTEK_HW_START_DELAY); +- dev_dbg(dev, "deasserted RESET\n"); +- } ++ priv = rtl83xx_probe(dev, &realtek_smi_info); ++ if (IS_ERR(priv)) ++ return PTR_ERR(priv); + + /* Fetch MDIO pins */ + priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); +- if (IS_ERR(priv->mdc)) ++ if (IS_ERR(priv->mdc)) { ++ rtl83xx_remove(priv); + return PTR_ERR(priv->mdc); ++ } ++ + priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); +- if (IS_ERR(priv->mdio)) ++ if (IS_ERR(priv->mdio)) { ++ rtl83xx_remove(priv); + return PTR_ERR(priv->mdio); +- +- priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); +- +- ret = priv->ops->detect(priv); +- if (ret) { +- dev_err(dev, "unable to detect switch\n"); +- return ret; + } + +- priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); +- if (!priv->ds) +- return -ENOMEM; +- +- priv->ds->dev = dev; +- priv->ds->num_ports = priv->num_ports; +- priv->ds->priv = priv; ++ priv->write_reg_noack = realtek_smi_write_reg_noack; ++ priv->setup_interface = realtek_smi_setup_mdio; ++ priv->ds_ops = priv->variant->ds_ops_smi; + +- priv->ds->ops = var->ds_ops_smi; +- ret = dsa_register_switch(priv->ds); ++ ret = rtl83xx_register_switch(priv); + if (ret) { +- dev_err_probe(dev, ret, "unable to register switch\n"); ++ rtl83xx_remove(priv); + return ret; + } ++ + return 0; + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA); +@@ -523,8 +428,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, + * @pdev: platform_device to be removed. + * + * This function should be used as the .remove_new in a platform_driver. First +- * it unregisters the DSA switch and cleans internal data. If a method is +- * provided, the hard reset is asserted to avoid traffic leakage. ++ * it unregisters the DSA switch and cleans internal data. Finally, it calls ++ * the common remove function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -536,13 +441,12 @@ void realtek_smi_remove(struct platform_ + if (!priv) + return; + +- dsa_unregister_switch(priv->ds); ++ rtl83xx_unregister_switch(priv); ++ + if (priv->slave_mii_bus) + of_node_put(priv->slave_mii_bus->dev.of_node); + +- /* leave the device reset asserted */ +- if (priv->reset) +- gpiod_set_value(priv->reset, 1); ++ rtl83xx_remove(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA); + +@@ -550,10 +454,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, + * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch + * @pdev: platform_device shutting down. + * +- * This function should be used as the .shutdown in a platform_driver. It shuts +- * down the DSA switch and cleans the platform driver data, to prevent +- * realtek_smi_remove() from running afterwards, which is possible if the +- * parent bus implements its own .shutdown() as .remove(). ++ * This function should be used as the .shutdown in a platform_driver. It calls ++ * the common shutdown function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -565,9 +467,7 @@ void realtek_smi_shutdown(struct platfor + if (!priv) + return; + +- dsa_switch_shutdown(priv->ds); +- +- platform_set_drvdata(pdev, NULL); ++ rtl83xx_shutdown(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA); + +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -62,6 +62,7 @@ struct realtek_priv { + + spinlock_t lock; /* Locks around command writes */ + struct dsa_switch *ds; ++ const struct dsa_switch_ops *ds_ops; + struct irq_domain *irqdomain; + bool leds_disabled; + +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -103,6 +103,7 @@ + #include "realtek.h" + #include "realtek-smi.h" + #include "realtek-mdio.h" ++#include "rtl83xx.h" + + /* Family-specific data and limits */ + #define RTL8365MB_PHYADDRMAX 7 +@@ -691,7 +692,7 @@ static int rtl8365mb_phy_ocp_read(struct + u32 val; + int ret; + +- mutex_lock(&priv->map_lock); ++ rtl83xx_lock(priv); + + ret = rtl8365mb_phy_poll_busy(priv); + if (ret) +@@ -724,7 +725,7 @@ static int rtl8365mb_phy_ocp_read(struct + *data = val & 0xFFFF; + + out: +- mutex_unlock(&priv->map_lock); ++ rtl83xx_unlock(priv); + + return ret; + } +@@ -735,7 +736,7 @@ static int rtl8365mb_phy_ocp_write(struc + u32 val; + int ret; + +- mutex_lock(&priv->map_lock); ++ rtl83xx_lock(priv); + + ret = rtl8365mb_phy_poll_busy(priv); + if (ret) +@@ -766,7 +767,7 @@ static int rtl8365mb_phy_ocp_write(struc + goto out; + + out: +- mutex_unlock(&priv->map_lock); ++ rtl83xx_unlock(priv); + + return 0; + } +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -24,6 +24,7 @@ + #include "realtek.h" + #include "realtek-smi.h" + #include "realtek-mdio.h" ++#include "rtl83xx.h" + + #define RTL8366RB_PORT_NUM_CPU 5 + #define RTL8366RB_NUM_PORTS 6 +@@ -1634,7 +1635,7 @@ static int rtl8366rb_phy_read(struct rea + if (phy > RTL8366RB_PHY_NO_MAX) + return -EINVAL; + +- mutex_lock(&priv->map_lock); ++ rtl83xx_lock(priv); + + ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_READ); +@@ -1662,7 +1663,7 @@ static int rtl8366rb_phy_read(struct rea + phy, regnum, reg, val); + + out: +- mutex_unlock(&priv->map_lock); ++ rtl83xx_unlock(priv); + + return ret; + } +@@ -1676,7 +1677,7 @@ static int rtl8366rb_phy_write(struct re + if (phy > RTL8366RB_PHY_NO_MAX) + return -EINVAL; + +- mutex_lock(&priv->map_lock); ++ rtl83xx_lock(priv); + + ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_WRITE); +@@ -1693,7 +1694,7 @@ static int rtl8366rb_phy_write(struct re + goto out; + + out: +- mutex_unlock(&priv->map_lock); ++ rtl83xx_unlock(priv); + + return ret; + } +--- /dev/null ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -0,0 +1,236 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include ++#include ++#include /* krnl 6.1 only */ ++ ++#include "realtek.h" ++#include "rtl83xx.h" ++ ++/** ++ * rtl83xx_lock() - Locks the mutex used by regmaps ++ * @ctx: realtek_priv pointer ++ * ++ * This function is passed to regmap to be used as the lock function. ++ * It is also used externally to block regmap before executing multiple ++ * operations that must happen in sequence (which will use ++ * realtek_priv.map_nolock instead). ++ * ++ * Context: Can sleep. Holds priv->map_lock lock. ++ * Return: nothing ++ */ ++void rtl83xx_lock(void *ctx) ++{ ++ struct realtek_priv *priv = ctx; ++ ++ mutex_lock(&priv->map_lock); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_lock, REALTEK_DSA); ++ ++/** ++ * rtl83xx_unlock() - Unlocks the mutex used by regmaps ++ * @ctx: realtek_priv pointer ++ * ++ * This function unlocks the lock acquired by rtl83xx_lock. ++ * ++ * Context: Releases priv->map_lock lock. ++ * Return: nothing ++ */ ++void rtl83xx_unlock(void *ctx) ++{ ++ struct realtek_priv *priv = ctx; ++ ++ mutex_unlock(&priv->map_lock); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA); ++ ++/** ++ * rtl83xx_probe() - probe a Realtek switch ++ * @dev: the device being probed ++ * @interface_info: specific management interface info. ++ * ++ * This function initializes realtek_priv and reads data from the device tree ++ * node. The switch is hard resetted if a method is provided. ++ * ++ * Context: Can sleep. ++ * Return: Pointer to the realtek_priv or ERR_PTR() in case of failure. ++ * ++ * The realtek_priv pointer does not need to be freed as it is controlled by ++ * devres. ++ */ ++struct realtek_priv * ++rtl83xx_probe(struct device *dev, ++ const struct realtek_interface_info *interface_info) ++{ ++ const struct realtek_variant *var; ++ struct realtek_priv *priv; ++ struct regmap_config rc = { ++ .reg_bits = 10, /* A4..A0 R4..R0 */ ++ .val_bits = 16, ++ .reg_stride = 1, ++ .max_register = 0xffff, ++ .reg_format_endian = REGMAP_ENDIAN_BIG, ++ .reg_read = interface_info->reg_read, ++ .reg_write = interface_info->reg_write, ++ .cache_type = REGCACHE_NONE, ++ .lock = rtl83xx_lock, ++ .unlock = rtl83xx_unlock, ++ }; ++ int ret; ++ ++ var = of_device_get_match_data(dev); ++ if (!var) ++ return ERR_PTR(-EINVAL); ++ ++ priv = devm_kzalloc(dev, size_add(sizeof(*priv), var->chip_data_sz), ++ GFP_KERNEL); ++ if (!priv) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_init(&priv->map_lock); ++ ++ rc.lock_arg = priv; ++ priv->map = devm_regmap_init(dev, NULL, priv, &rc); ++ if (IS_ERR(priv->map)) { ++ ret = PTR_ERR(priv->map); ++ dev_err(dev, "regmap init failed: %d\n", ret); ++ return ERR_PTR(ret); ++ } ++ ++ rc.disable_locking = true; ++ priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); ++ if (IS_ERR(priv->map_nolock)) { ++ ret = PTR_ERR(priv->map_nolock); ++ dev_err(dev, "regmap init failed: %d\n", ret); ++ return ERR_PTR(ret); ++ } ++ ++ /* Link forward and backward */ ++ priv->dev = dev; ++ priv->variant = var; ++ priv->ops = var->ops; ++ priv->chip_data = (void *)priv + sizeof(*priv); ++ ++ spin_lock_init(&priv->lock); ++ ++ priv->leds_disabled = of_property_read_bool(dev->of_node, ++ "realtek,disable-leds"); ++ ++ /* TODO: if power is software controlled, set up any regulators here */ ++ priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(priv->reset)) { ++ dev_err(dev, "failed to get RESET GPIO\n"); ++ return ERR_CAST(priv->reset); ++ } ++ ++ dev_set_drvdata(dev, priv); ++ ++ if (priv->reset) { ++ gpiod_set_value(priv->reset, 1); ++ dev_dbg(dev, "asserted RESET\n"); ++ msleep(REALTEK_HW_STOP_DELAY); ++ gpiod_set_value(priv->reset, 0); ++ msleep(REALTEK_HW_START_DELAY); ++ dev_dbg(dev, "deasserted RESET\n"); ++ } ++ ++ return priv; ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REALTEK_DSA); ++ ++/** ++ * rtl83xx_register_switch() - detects and register a switch ++ * @priv: realtek_priv pointer ++ * ++ * This function first checks the switch chip ID and register a DSA ++ * switch. ++ * ++ * Context: Can sleep. Takes and releases priv->map_lock. ++ * Return: 0 on success, negative value for failure. ++ */ ++int rtl83xx_register_switch(struct realtek_priv *priv) ++{ ++ struct dsa_switch *ds; ++ int ret; ++ ++ ret = priv->ops->detect(priv); ++ if (ret) { ++ dev_err_probe(priv->dev, ret, "unable to detect switch\n"); ++ return ret; ++ } ++ ++ ds = devm_kzalloc(priv->dev, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return -ENOMEM; ++ ++ ds->priv = priv; ++ ds->dev = priv->dev; ++ ds->ops = priv->ds_ops; ++ ds->num_ports = priv->num_ports; ++ priv->ds = ds; ++ ++ ret = dsa_register_switch(ds); ++ if (ret) { ++ dev_err_probe(priv->dev, ret, "unable to register switch\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, REALTEK_DSA); ++ ++/** ++ * rtl83xx_unregister_switch() - unregister a switch ++ * @priv: realtek_priv pointer ++ * ++ * This function unregister a DSA switch. ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void rtl83xx_unregister_switch(struct realtek_priv *priv) ++{ ++ dsa_unregister_switch(priv->ds); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA); ++ ++/** ++ * rtl83xx_shutdown() - shutdown a switch ++ * @priv: realtek_priv pointer ++ * ++ * This function shuts down the DSA switch and cleans the platform driver data, ++ * to prevent realtek_{smi,mdio}_remove() from running afterwards, which is ++ * possible if the parent bus implements its own .shutdown() as .remove(). ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void rtl83xx_shutdown(struct realtek_priv *priv) ++{ ++ dsa_switch_shutdown(priv->ds); ++ ++ dev_set_drvdata(priv->dev, NULL); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, REALTEK_DSA); ++ ++/** ++ * rtl83xx_remove() - Cleanup a realtek switch driver ++ * @priv: realtek_priv pointer ++ * ++ * If a method is provided, this function asserts the hard reset of the switch ++ * in order to avoid leaking traffic when the driver is gone. ++ * ++ * Context: Might sleep if priv->gdev->chip->can_sleep. ++ * Return: nothing ++ */ ++void rtl83xx_remove(struct realtek_priv *priv) ++{ ++ /* leave the device reset asserted */ ++ if (priv->reset) ++ gpiod_set_value(priv->reset, 1); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); ++ ++MODULE_AUTHOR("Luiz Angelo Daros de Luca "); ++MODULE_DESCRIPTION("Realtek DSA switches common module"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/net/dsa/realtek/rtl83xx.h +@@ -0,0 +1,21 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++ ++#ifndef _RTL83XX_H ++#define _RTL83XX_H ++ ++struct realtek_interface_info { ++ int (*reg_read)(void *ctx, u32 reg, u32 *val); ++ int (*reg_write)(void *ctx, u32 reg, u32 val); ++}; ++ ++void rtl83xx_lock(void *ctx); ++void rtl83xx_unlock(void *ctx); ++struct realtek_priv * ++rtl83xx_probe(struct device *dev, ++ const struct realtek_interface_info *interface_info); ++int rtl83xx_register_switch(struct realtek_priv *priv); ++void rtl83xx_unregister_switch(struct realtek_priv *priv); ++void rtl83xx_shutdown(struct realtek_priv *priv); ++void rtl83xx_remove(struct realtek_priv *priv); ++ ++#endif /* _RTL83XX_H */ diff --git a/target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch b/target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch new file mode 100644 index 0000000000..2754b62319 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch @@ -0,0 +1,93 @@ +From 98b75c1c149c653ad11a440636213eb070325158 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:42 -0300 +Subject: net: dsa: realtek: merge rtl83xx and interface + modules into realtek_dsa + +Since rtl83xx and realtek-{smi,mdio} are always loaded together, +we can optimize resource usage by consolidating them into a single +module. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/Kconfig | 4 ++-- + drivers/net/dsa/realtek/Makefile | 11 +++++++++-- + drivers/net/dsa/realtek/realtek-mdio.c | 5 ----- + drivers/net/dsa/realtek/realtek-smi.c | 5 ----- + drivers/net/dsa/realtek/rtl83xx.c | 1 + + 5 files changed, 12 insertions(+), 14 deletions(-) + +--- a/drivers/net/dsa/realtek/Kconfig ++++ b/drivers/net/dsa/realtek/Kconfig +@@ -16,14 +16,14 @@ menuconfig NET_DSA_REALTEK + if NET_DSA_REALTEK + + config NET_DSA_REALTEK_MDIO +- tristate "Realtek MDIO interface support" ++ bool "Realtek MDIO interface support" + depends on OF + help + Select to enable support for registering switches configured + through MDIO. + + config NET_DSA_REALTEK_SMI +- tristate "Realtek SMI interface support" ++ bool "Realtek SMI interface support" + depends on OF + help + Select to enable support for registering switches connected +--- a/drivers/net/dsa/realtek/Makefile ++++ b/drivers/net/dsa/realtek/Makefile +@@ -1,8 +1,15 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_NET_DSA_REALTEK) += realtek_dsa.o + realtek_dsa-objs := rtl83xx.o +-obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o +-obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o ++ ++ifdef CONFIG_NET_DSA_REALTEK_MDIO ++realtek_dsa-objs += realtek-mdio.o ++endif ++ ++ifdef CONFIG_NET_DSA_REALTEK_SMI ++realtek_dsa-objs += realtek-smi.o ++endif ++ + obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o + rtl8366-objs := rtl8366-core.o rtl8366rb.o + obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -186,8 +186,3 @@ void realtek_mdio_shutdown(struct mdio_d + rtl83xx_shutdown(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA); +- +-MODULE_AUTHOR("Luiz Angelo Daros de Luca "); +-MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); +-MODULE_LICENSE("GPL"); +-MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -470,8 +470,3 @@ void realtek_smi_shutdown(struct platfor + rtl83xx_shutdown(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA); +- +-MODULE_AUTHOR("Linus Walleij "); +-MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); +-MODULE_LICENSE("GPL"); +-MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -232,5 +232,6 @@ void rtl83xx_remove(struct realtek_priv + EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); + + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); ++MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Realtek DSA switches common module"); + MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch b/target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch new file mode 100644 index 0000000000..423e7500d7 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch @@ -0,0 +1,34 @@ +From 8685c98d45c54346caf005de69988e13c731c533 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:43 -0300 +Subject: net: dsa: realtek: get internal MDIO node by name + +The binding docs requires for SMI-connected devices that the switch +must have a child node named "mdio" and with a compatible string of +"realtek,smi-mdio". Meanwile, for MDIO-connected switches, the binding +docs only requires a child node named "mdio". + +This patch changes the driver to use the common denominator for both +interfaces, looking for the MDIO node by name, ignoring the compatible +string. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-smi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -333,7 +333,7 @@ static int realtek_smi_setup_mdio(struct + struct device_node *mdio_np; + int ret; + +- mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio"); ++ mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio"); + if (!mdio_np) { + dev_err(priv->dev, "no MDIO bus node\n"); + return -ENODEV; diff --git a/target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch b/target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch new file mode 100644 index 0000000000..1958c7feb4 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch @@ -0,0 +1,83 @@ +From 68c66d8d8a19088967a0ab6bb98cb5ecc80ca0be Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:44 -0300 +Subject: net: dsa: realtek: clean slave_mii_bus setup + +Remove the line assigning dev.of_node in mdio_bus as subsequent +of_mdiobus_register will always overwrite it. + +As discussed in [1], allow the DSA core to be simplified, by not +assigning ds->slave_mii_bus when the MDIO bus is described in OF, as it +is unnecessary. + +Since commit 3b73a7b8ec38 ("net: mdio_bus: add refcounting for fwnodes +to mdiobus"), we can put the "mdio" node just after the MDIO bus +registration. + +[1] https://lkml.kernel.org/netdev/20231213120656.x46fyad6ls7sqyzv@skbuf/T/#u + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-smi.c | 13 +++---------- + 1 file changed, 3 insertions(+), 10 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -331,7 +331,7 @@ static int realtek_smi_setup_mdio(struct + { + struct realtek_priv *priv = ds->priv; + struct device_node *mdio_np; +- int ret; ++ int ret = 0; + + mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio"); + if (!mdio_np) { +@@ -344,15 +344,14 @@ static int realtek_smi_setup_mdio(struct + ret = -ENOMEM; + goto err_put_node; + } ++ + priv->slave_mii_bus->priv = priv; + priv->slave_mii_bus->name = "SMI slave MII"; + priv->slave_mii_bus->read = realtek_smi_mdio_read; + priv->slave_mii_bus->write = realtek_smi_mdio_write; + snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", + ds->index); +- priv->slave_mii_bus->dev.of_node = mdio_np; + priv->slave_mii_bus->parent = priv->dev; +- ds->slave_mii_bus = priv->slave_mii_bus; + + ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np); + if (ret) { +@@ -361,8 +360,6 @@ static int realtek_smi_setup_mdio(struct + goto err_put_node; + } + +- return 0; +- + err_put_node: + of_node_put(mdio_np); + +@@ -428,8 +425,7 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, + * @pdev: platform_device to be removed. + * + * This function should be used as the .remove_new in a platform_driver. First +- * it unregisters the DSA switch and cleans internal data. Finally, it calls +- * the common remove function. ++ * it unregisters the DSA switch and then it calls the common remove function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -443,9 +439,6 @@ void realtek_smi_remove(struct platform_ + + rtl83xx_unregister_switch(priv); + +- if (priv->slave_mii_bus) +- of_node_put(priv->slave_mii_bus->dev.of_node); +- + rtl83xx_remove(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch b/target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch new file mode 100644 index 0000000000..50e96d7eef --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch @@ -0,0 +1,198 @@ +From b4bd77971f3c290c4694ed710cc6967593b10bc2 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:45 -0300 +Subject: net: dsa: realtek: migrate slave_mii_bus setup to + realtek_dsa + +In the user MDIO driver, despite numerous references to SMI, including +its compatible string, there's nothing inherently specific about the SMI +interface in the user MDIO bus. Consequently, the code has been migrated +to the rtl83xx module. All references to SMI have been eliminated. + +The MDIO bus id was changed from Realtek- to the switch +devname suffixed with :slave_mii, giving more information about the bus +it is referencing. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-smi.c | 57 +--------------------- + drivers/net/dsa/realtek/rtl83xx.c | 68 +++++++++++++++++++++++++++ + drivers/net/dsa/realtek/rtl83xx.h | 1 + + 3 files changed, 70 insertions(+), 56 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -31,7 +31,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -312,60 +311,6 @@ static int realtek_smi_read(void *ctx, u + return realtek_smi_read_reg(priv, reg, val); + } + +-static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) +-{ +- struct realtek_priv *priv = bus->priv; +- +- return priv->ops->phy_read(priv, addr, regnum); +-} +- +-static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, +- u16 val) +-{ +- struct realtek_priv *priv = bus->priv; +- +- return priv->ops->phy_write(priv, addr, regnum, val); +-} +- +-static int realtek_smi_setup_mdio(struct dsa_switch *ds) +-{ +- struct realtek_priv *priv = ds->priv; +- struct device_node *mdio_np; +- int ret = 0; +- +- mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio"); +- if (!mdio_np) { +- dev_err(priv->dev, "no MDIO bus node\n"); +- return -ENODEV; +- } +- +- priv->slave_mii_bus = devm_mdiobus_alloc(priv->dev); +- if (!priv->slave_mii_bus) { +- ret = -ENOMEM; +- goto err_put_node; +- } +- +- priv->slave_mii_bus->priv = priv; +- priv->slave_mii_bus->name = "SMI slave MII"; +- priv->slave_mii_bus->read = realtek_smi_mdio_read; +- priv->slave_mii_bus->write = realtek_smi_mdio_write; +- snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", +- ds->index); +- priv->slave_mii_bus->parent = priv->dev; +- +- ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np); +- if (ret) { +- dev_err(priv->dev, "unable to register MDIO bus %s\n", +- priv->slave_mii_bus->id); +- goto err_put_node; +- } +- +-err_put_node: +- of_node_put(mdio_np); +- +- return ret; +-} +- + static const struct realtek_interface_info realtek_smi_info = { + .reg_read = realtek_smi_read, + .reg_write = realtek_smi_write, +@@ -407,7 +352,7 @@ int realtek_smi_probe(struct platform_de + } + + priv->write_reg_noack = realtek_smi_write_reg_noack; +- priv->setup_interface = realtek_smi_setup_mdio; ++ priv->setup_interface = rtl83xx_setup_user_mdio; + priv->ds_ops = priv->variant->ds_ops_smi; + + ret = rtl83xx_register_switch(priv); +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -3,6 +3,7 @@ + #include + #include + #include /* krnl 6.1 only */ ++#include + + #include "realtek.h" + #include "rtl83xx.h" +@@ -44,6 +45,73 @@ void rtl83xx_unlock(void *ctx) + } + EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA); + ++static int rtl83xx_user_mdio_read(struct mii_bus *bus, int addr, int regnum) ++{ ++ struct realtek_priv *priv = bus->priv; ++ ++ return priv->ops->phy_read(priv, addr, regnum); ++} ++ ++static int rtl83xx_user_mdio_write(struct mii_bus *bus, int addr, int regnum, ++ u16 val) ++{ ++ struct realtek_priv *priv = bus->priv; ++ ++ return priv->ops->phy_write(priv, addr, regnum, val); ++} ++ ++/** ++ * rtl83xx_setup_user_mdio() - register the user mii bus driver ++ * @ds: DSA switch associated with this slave_mii_bus ++ * ++ * Registers the MDIO bus for built-in Ethernet PHYs, and associates it with ++ * the mandatory 'mdio' child OF node of the switch. ++ * ++ * Context: Can sleep. ++ * Return: 0 on success, negative value for failure. ++ */ ++int rtl83xx_setup_user_mdio(struct dsa_switch *ds) ++{ ++ struct realtek_priv *priv = ds->priv; ++ struct device_node *mdio_np; ++ struct mii_bus *bus; ++ int ret = 0; ++ ++ mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio"); ++ if (!mdio_np) { ++ dev_err(priv->dev, "no MDIO bus node\n"); ++ return -ENODEV; ++ } ++ ++ bus = devm_mdiobus_alloc(priv->dev); ++ if (!bus) { ++ ret = -ENOMEM; ++ goto err_put_node; ++ } ++ ++ bus->priv = priv; ++ bus->name = "Realtek user MII"; ++ bus->read = rtl83xx_user_mdio_read; ++ bus->write = rtl83xx_user_mdio_write; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s:slave_mii", dev_name(priv->dev)); ++ bus->parent = priv->dev; ++ ++ ret = devm_of_mdiobus_register(priv->dev, bus, mdio_np); ++ if (ret) { ++ dev_err(priv->dev, "unable to register MDIO bus %s\n", ++ bus->id); ++ goto err_put_node; ++ } ++ ++ priv->slave_mii_bus = bus; ++ ++err_put_node: ++ of_node_put(mdio_np); ++ ++ return ret; ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_setup_user_mdio, REALTEK_DSA); ++ + /** + * rtl83xx_probe() - probe a Realtek switch + * @dev: the device being probed +--- a/drivers/net/dsa/realtek/rtl83xx.h ++++ b/drivers/net/dsa/realtek/rtl83xx.h +@@ -10,6 +10,7 @@ struct realtek_interface_info { + + void rtl83xx_lock(void *ctx); + void rtl83xx_unlock(void *ctx); ++int rtl83xx_setup_user_mdio(struct dsa_switch *ds); + struct realtek_priv * + rtl83xx_probe(struct device *dev, + const struct realtek_interface_info *interface_info); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch b/target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch new file mode 100644 index 0000000000..b50a33590b --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch @@ -0,0 +1,261 @@ +From bba140a566ed075304c49c52ab32c0016cab624a Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:46 -0300 +Subject: net: dsa: realtek: use the same mii bus driver for + both interfaces + +The realtek-mdio will now use this driver instead of the generic DSA +driver ("dsa user smi"), which should not be used with OF[1]. + +With a single ds_ops for both interfaces, the ds_ops in realtek_priv is +no longer necessary. Now, the realtek_variant.ds_ops can be used +directly. + +The realtek_priv.setup_interface() has been removed as we can directly +call the new common function. + +[1] https://lkml.kernel.org/netdev/20220630200423.tieprdu5fpabflj7@bang-olufsen.dk/T/ + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-mdio.c | 1 - + drivers/net/dsa/realtek/realtek-smi.c | 2 - + drivers/net/dsa/realtek/realtek.h | 5 +-- + drivers/net/dsa/realtek/rtl8365mb.c | 49 +++--------------------- + drivers/net/dsa/realtek/rtl8366rb.c | 52 +++----------------------- + drivers/net/dsa/realtek/rtl83xx.c | 2 +- + 6 files changed, 14 insertions(+), 97 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -131,7 +131,6 @@ int realtek_mdio_probe(struct mdio_devic + priv->bus = mdiodev->bus; + priv->mdio_addr = mdiodev->addr; + priv->write_reg_noack = realtek_mdio_write; +- priv->ds_ops = priv->variant->ds_ops_mdio; + + ret = rtl83xx_register_switch(priv); + if (ret) { +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -352,8 +352,6 @@ int realtek_smi_probe(struct platform_de + } + + priv->write_reg_noack = realtek_smi_write_reg_noack; +- priv->setup_interface = rtl83xx_setup_user_mdio; +- priv->ds_ops = priv->variant->ds_ops_smi; + + ret = rtl83xx_register_switch(priv); + if (ret) { +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -62,7 +62,6 @@ struct realtek_priv { + + spinlock_t lock; /* Locks around command writes */ + struct dsa_switch *ds; +- const struct dsa_switch_ops *ds_ops; + struct irq_domain *irqdomain; + bool leds_disabled; + +@@ -73,7 +72,6 @@ struct realtek_priv { + struct rtl8366_mib_counter *mib_counters; + + const struct realtek_ops *ops; +- int (*setup_interface)(struct dsa_switch *ds); + int (*write_reg_noack)(void *ctx, u32 addr, u32 data); + + int vlan_enabled; +@@ -115,8 +113,7 @@ struct realtek_ops { + }; + + struct realtek_variant { +- const struct dsa_switch_ops *ds_ops_smi; +- const struct dsa_switch_ops *ds_ops_mdio; ++ const struct dsa_switch_ops *ds_ops; + const struct realtek_ops *ops; + unsigned int clk_delay; + u8 cmd_read; +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -828,17 +828,6 @@ static int rtl8365mb_phy_write(struct re + return 0; + } + +-static int rtl8365mb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) +-{ +- return rtl8365mb_phy_read(ds->priv, phy, regnum); +-} +- +-static int rtl8365mb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, +- u16 val) +-{ +- return rtl8365mb_phy_write(ds->priv, phy, regnum, val); +-} +- + static const struct rtl8365mb_extint * + rtl8365mb_get_port_extint(struct realtek_priv *priv, int port) + { +@@ -2018,12 +2007,10 @@ static int rtl8365mb_setup(struct dsa_sw + if (ret) + goto out_teardown_irq; + +- if (priv->setup_interface) { +- ret = priv->setup_interface(ds); +- if (ret) { +- dev_err(priv->dev, "could not set up MDIO bus\n"); +- goto out_teardown_irq; +- } ++ ret = rtl83xx_setup_user_mdio(ds); ++ if (ret) { ++ dev_err(priv->dev, "could not set up MDIO bus\n"); ++ goto out_teardown_irq; + } + + /* Start statistics counter polling */ +@@ -2117,28 +2104,7 @@ static int rtl8365mb_detect(struct realt + return 0; + } + +-static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = { +- .get_tag_protocol = rtl8365mb_get_tag_protocol, +- .change_tag_protocol = rtl8365mb_change_tag_protocol, +- .setup = rtl8365mb_setup, +- .teardown = rtl8365mb_teardown, +- .phylink_get_caps = rtl8365mb_phylink_get_caps, +- .phylink_mac_config = rtl8365mb_phylink_mac_config, +- .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, +- .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, +- .port_stp_state_set = rtl8365mb_port_stp_state_set, +- .get_strings = rtl8365mb_get_strings, +- .get_ethtool_stats = rtl8365mb_get_ethtool_stats, +- .get_sset_count = rtl8365mb_get_sset_count, +- .get_eth_phy_stats = rtl8365mb_get_phy_stats, +- .get_eth_mac_stats = rtl8365mb_get_mac_stats, +- .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, +- .get_stats64 = rtl8365mb_get_stats64, +- .port_change_mtu = rtl8365mb_port_change_mtu, +- .port_max_mtu = rtl8365mb_port_max_mtu, +-}; +- +-static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { ++static const struct dsa_switch_ops rtl8365mb_switch_ops = { + .get_tag_protocol = rtl8365mb_get_tag_protocol, + .change_tag_protocol = rtl8365mb_change_tag_protocol, + .setup = rtl8365mb_setup, +@@ -2147,8 +2113,6 @@ static const struct dsa_switch_ops rtl83 + .phylink_mac_config = rtl8365mb_phylink_mac_config, + .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, + .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, +- .phy_read = rtl8365mb_dsa_phy_read, +- .phy_write = rtl8365mb_dsa_phy_write, + .port_stp_state_set = rtl8365mb_port_stp_state_set, + .get_strings = rtl8365mb_get_strings, + .get_ethtool_stats = rtl8365mb_get_ethtool_stats, +@@ -2168,8 +2132,7 @@ static const struct realtek_ops rtl8365m + }; + + const struct realtek_variant rtl8365mb_variant = { +- .ds_ops_smi = &rtl8365mb_switch_ops_smi, +- .ds_ops_mdio = &rtl8365mb_switch_ops_mdio, ++ .ds_ops = &rtl8365mb_switch_ops, + .ops = &rtl8365mb_ops, + .clk_delay = 10, + .cmd_read = 0xb9, +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -1035,12 +1035,10 @@ static int rtl8366rb_setup(struct dsa_sw + if (ret) + dev_info(priv->dev, "no interrupt support\n"); + +- if (priv->setup_interface) { +- ret = priv->setup_interface(ds); +- if (ret) { +- dev_err(priv->dev, "could not set up MDIO bus\n"); +- return -ENODEV; +- } ++ ret = rtl83xx_setup_user_mdio(ds); ++ if (ret) { ++ dev_err(priv->dev, "could not set up MDIO bus\n"); ++ return -ENODEV; + } + + return 0; +@@ -1699,17 +1697,6 @@ out: + return ret; + } + +-static int rtl8366rb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) +-{ +- return rtl8366rb_phy_read(ds->priv, phy, regnum); +-} +- +-static int rtl8366rb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, +- u16 val) +-{ +- return rtl8366rb_phy_write(ds->priv, phy, regnum, val); +-} +- + static int rtl8366rb_reset_chip(struct realtek_priv *priv) + { + int timeout = 10; +@@ -1775,35 +1762,9 @@ static int rtl8366rb_detect(struct realt + return 0; + } + +-static const struct dsa_switch_ops rtl8366rb_switch_ops_smi = { +- .get_tag_protocol = rtl8366_get_tag_protocol, +- .setup = rtl8366rb_setup, +- .phylink_get_caps = rtl8366rb_phylink_get_caps, +- .phylink_mac_link_up = rtl8366rb_mac_link_up, +- .phylink_mac_link_down = rtl8366rb_mac_link_down, +- .get_strings = rtl8366_get_strings, +- .get_ethtool_stats = rtl8366_get_ethtool_stats, +- .get_sset_count = rtl8366_get_sset_count, +- .port_bridge_join = rtl8366rb_port_bridge_join, +- .port_bridge_leave = rtl8366rb_port_bridge_leave, +- .port_vlan_filtering = rtl8366rb_vlan_filtering, +- .port_vlan_add = rtl8366_vlan_add, +- .port_vlan_del = rtl8366_vlan_del, +- .port_enable = rtl8366rb_port_enable, +- .port_disable = rtl8366rb_port_disable, +- .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags, +- .port_bridge_flags = rtl8366rb_port_bridge_flags, +- .port_stp_state_set = rtl8366rb_port_stp_state_set, +- .port_fast_age = rtl8366rb_port_fast_age, +- .port_change_mtu = rtl8366rb_change_mtu, +- .port_max_mtu = rtl8366rb_max_mtu, +-}; +- +-static const struct dsa_switch_ops rtl8366rb_switch_ops_mdio = { ++static const struct dsa_switch_ops rtl8366rb_switch_ops = { + .get_tag_protocol = rtl8366_get_tag_protocol, + .setup = rtl8366rb_setup, +- .phy_read = rtl8366rb_dsa_phy_read, +- .phy_write = rtl8366rb_dsa_phy_write, + .phylink_get_caps = rtl8366rb_phylink_get_caps, + .phylink_mac_link_up = rtl8366rb_mac_link_up, + .phylink_mac_link_down = rtl8366rb_mac_link_down, +@@ -1842,8 +1803,7 @@ static const struct realtek_ops rtl8366r + }; + + const struct realtek_variant rtl8366rb_variant = { +- .ds_ops_smi = &rtl8366rb_switch_ops_smi, +- .ds_ops_mdio = &rtl8366rb_switch_ops_mdio, ++ .ds_ops = &rtl8366rb_switch_ops, + .ops = &rtl8366rb_ops, + .clk_delay = 10, + .cmd_read = 0xa9, +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -233,7 +233,7 @@ int rtl83xx_register_switch(struct realt + + ds->priv = priv; + ds->dev = priv->dev; +- ds->ops = priv->ds_ops; ++ ds->ops = priv->variant->ds_ops; + ds->num_ports = priv->num_ports; + priv->ds = ds; + diff --git a/target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch b/target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch new file mode 100644 index 0000000000..4cb55f2da9 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch @@ -0,0 +1,180 @@ +From 9fc469b2943d9b1ff2a7800f823e7cd7a5cac0ca Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:47 -0300 +Subject: net: dsa: realtek: embed dsa_switch into realtek_priv +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Embed dsa_switch within realtek_priv to eliminate the need for a second +memory allocation. + +Suggested-by: Alvin Šipraga +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek.h | 2 +- + drivers/net/dsa/realtek/rtl8365mb.c | 15 +++++++++------ + drivers/net/dsa/realtek/rtl8366rb.c | 3 ++- + drivers/net/dsa/realtek/rtl83xx.c | 15 +++++++-------- + 4 files changed, 19 insertions(+), 16 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -61,7 +61,7 @@ struct realtek_priv { + const struct realtek_variant *variant; + + spinlock_t lock; /* Locks around command writes */ +- struct dsa_switch *ds; ++ struct dsa_switch ds; + struct irq_domain *irqdomain; + bool leds_disabled; + +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -870,6 +870,7 @@ static int rtl8365mb_ext_config_rgmii(st + { + const struct rtl8365mb_extint *extint = + rtl8365mb_get_port_extint(priv, port); ++ struct dsa_switch *ds = &priv->ds; + struct device_node *dn; + struct dsa_port *dp; + int tx_delay = 0; +@@ -880,7 +881,7 @@ static int rtl8365mb_ext_config_rgmii(st + if (!extint) + return -ENODEV; + +- dp = dsa_to_port(priv->ds, port); ++ dp = dsa_to_port(ds, port); + dn = dp->dn; + + /* Set the RGMII TX/RX delay +@@ -1534,6 +1535,7 @@ static void rtl8365mb_get_stats64(struct + static void rtl8365mb_stats_setup(struct realtek_priv *priv) + { + struct rtl8365mb *mb = priv->chip_data; ++ struct dsa_switch *ds = &priv->ds; + int i; + + /* Per-chip global mutex to protect MIB counter access, since doing +@@ -1544,7 +1546,7 @@ static void rtl8365mb_stats_setup(struct + for (i = 0; i < priv->num_ports; i++) { + struct rtl8365mb_port *p = &mb->ports[i]; + +- if (dsa_is_unused_port(priv->ds, i)) ++ if (dsa_is_unused_port(ds, i)) + continue; + + /* Per-port spinlock to protect the stats64 data */ +@@ -1560,12 +1562,13 @@ static void rtl8365mb_stats_setup(struct + static void rtl8365mb_stats_teardown(struct realtek_priv *priv) + { + struct rtl8365mb *mb = priv->chip_data; ++ struct dsa_switch *ds = &priv->ds; + int i; + + for (i = 0; i < priv->num_ports; i++) { + struct rtl8365mb_port *p = &mb->ports[i]; + +- if (dsa_is_unused_port(priv->ds, i)) ++ if (dsa_is_unused_port(ds, i)) + continue; + + cancel_delayed_work_sync(&p->mib_work); +@@ -1964,7 +1967,7 @@ static int rtl8365mb_setup(struct dsa_sw + dev_info(priv->dev, "no interrupt support\n"); + + /* Configure CPU tagging */ +- dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) { ++ dsa_switch_for_each_cpu_port(cpu_dp, ds) { + cpu->mask |= BIT(cpu_dp->index); + + if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS) +@@ -1979,7 +1982,7 @@ static int rtl8365mb_setup(struct dsa_sw + for (i = 0; i < priv->num_ports; i++) { + struct rtl8365mb_port *p = &mb->ports[i]; + +- if (dsa_is_unused_port(priv->ds, i)) ++ if (dsa_is_unused_port(ds, i)) + continue; + + /* Forward only to the CPU */ +@@ -1996,7 +1999,7 @@ static int rtl8365mb_setup(struct dsa_sw + * ports will still forward frames to the CPU despite being + * administratively down by default. + */ +- rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED); ++ rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED); + + /* Set up per-port private data */ + p->priv = priv; +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -1565,6 +1565,7 @@ static int rtl8366rb_get_mc_index(struct + + static int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index) + { ++ struct dsa_switch *ds = &priv->ds; + struct rtl8366rb *rb; + bool pvid_enabled; + int ret; +@@ -1589,7 +1590,7 @@ static int rtl8366rb_set_mc_index(struct + * not drop any untagged or C-tagged frames. Make sure to update the + * filtering setting. + */ +- if (dsa_port_is_vlan_filtering(dsa_to_port(priv->ds, port))) ++ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) + ret = rtl8366rb_drop_untagged(priv, port, !pvid_enabled); + + return ret; +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -218,7 +218,7 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REAL + */ + int rtl83xx_register_switch(struct realtek_priv *priv) + { +- struct dsa_switch *ds; ++ struct dsa_switch *ds = &priv->ds; + int ret; + + ret = priv->ops->detect(priv); +@@ -227,15 +227,10 @@ int rtl83xx_register_switch(struct realt + return ret; + } + +- ds = devm_kzalloc(priv->dev, sizeof(*ds), GFP_KERNEL); +- if (!ds) +- return -ENOMEM; +- + ds->priv = priv; + ds->dev = priv->dev; + ds->ops = priv->variant->ds_ops; + ds->num_ports = priv->num_ports; +- priv->ds = ds; + + ret = dsa_register_switch(ds); + if (ret) { +@@ -258,7 +253,9 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_register_sw + */ + void rtl83xx_unregister_switch(struct realtek_priv *priv) + { +- dsa_unregister_switch(priv->ds); ++ struct dsa_switch *ds = &priv->ds; ++ ++ dsa_unregister_switch(ds); + } + EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA); + +@@ -275,7 +272,9 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_ + */ + void rtl83xx_shutdown(struct realtek_priv *priv) + { +- dsa_switch_shutdown(priv->ds); ++ struct dsa_switch *ds = &priv->ds; ++ ++ dsa_switch_shutdown(ds); + + dev_set_drvdata(priv->dev, NULL); + } diff --git a/target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch b/target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch new file mode 100644 index 0000000000..a3ccf5d4ad --- /dev/null +++ b/target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch @@ -0,0 +1,38 @@ +From 32e4a5447ed9fa904a2dfcf4609c64bce053b4e8 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Mon, 12 Feb 2024 18:34:33 -0300 +Subject: [PATCH] net: dsa: realtek: fix digital interface select macro for + EXT0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +While no supported devices currently utilize EXT0, the register reserves +the bits for an EXT0. EXT0 is utilized by devices from the generation +prior to rtl8365mb, such as those supported by the driver library +rtl8367b. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Alvin Šipraga +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/20240212-realtek-fix_ext0-v1-1-f3d2536d191a@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/realtek/rtl8365mb.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -209,10 +209,10 @@ + #define RTL8365MB_EXT_PORT_MODE_100FX 13 + + /* External interface mode configuration registers 0~1 */ +-#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */ ++#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT0,EXT1 */ + #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */ + #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \ +- ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ ++ ((_extint) <= 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ + (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ + 0x0) + #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ diff --git a/target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch b/target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch new file mode 100644 index 0000000000..e42dfc5d4d --- /dev/null +++ b/target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch @@ -0,0 +1,34 @@ +From 28001bb1955fcfa63e535848c4289fcd7bb88daf Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sun, 25 Feb 2024 13:29:53 -0300 +Subject: [PATCH 1/3] dt-bindings: net: dsa: realtek: reset-gpios is not + required +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The 'reset-gpios' should not be mandatory. although they might be +required for some devices if the switch reset was left asserted by a +previous driver, such as the bootloader. + +Signed-off-by: Luiz Angelo Daros de Luca +Cc: devicetree@vger.kernel.org +Acked-by: Arınç ÜNAL +Acked-by: Rob Herring +Reviewed-by: Linus Walleij +Reviewed-by: Alvin Šipraga +Signed-off-by: David S. Miller +--- + Documentation/devicetree/bindings/net/dsa/realtek.yaml | 1 - + 1 file changed, 1 deletion(-) + +--- a/Documentation/devicetree/bindings/net/dsa/realtek.yaml ++++ b/Documentation/devicetree/bindings/net/dsa/realtek.yaml +@@ -125,7 +125,6 @@ else: + - mdc-gpios + - mdio-gpios + - mdio +- - reset-gpios + + required: + - compatible diff --git a/target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch b/target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch new file mode 100644 index 0000000000..7e8a240626 --- /dev/null +++ b/target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch @@ -0,0 +1,33 @@ +From 5fc2d68fc81801162188995e4d3dc0b26747dd76 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sun, 25 Feb 2024 13:29:54 -0300 +Subject: [PATCH 2/3] dt-bindings: net: dsa: realtek: add reset controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Realtek switches can use a reset controller instead of reset-gpios. + +Signed-off-by: Luiz Angelo Daros de Luca +Cc: devicetree@vger.kernel.org +Acked-by: Arınç ÜNAL +Reviewed-by: Linus Walleij +Reviewed-by: Alvin Šipraga +Acked-by: Rob Herring +Signed-off-by: David S. Miller +--- + Documentation/devicetree/bindings/net/dsa/realtek.yaml | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/Documentation/devicetree/bindings/net/dsa/realtek.yaml ++++ b/Documentation/devicetree/bindings/net/dsa/realtek.yaml +@@ -59,6 +59,9 @@ properties: + description: GPIO to be used to reset the whole device + maxItems: 1 + ++ resets: ++ maxItems: 1 ++ + realtek,disable-leds: + type: boolean + description: | diff --git a/target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch b/target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch new file mode 100644 index 0000000000..7a280bf7ad --- /dev/null +++ b/target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch @@ -0,0 +1,123 @@ +From 56998aa6b7f0f31ce8df23c00701af2d8e8a1f1a Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sun, 25 Feb 2024 13:29:55 -0300 +Subject: [PATCH 3/3] net: dsa: realtek: support reset controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for resetting the device using a reset controller, +complementing the existing GPIO reset functionality (reset-gpios). + +Although the reset is optional and the driver performs a soft reset +during setup, if the initial reset pin state was asserted, the driver +will not detect the device until the reset is deasserted. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Reviewed-by: Alvin Šipraga +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek.h | 2 ++ + drivers/net/dsa/realtek/rtl83xx.c | 42 +++++++++++++++++++++++++++---- + drivers/net/dsa/realtek/rtl83xx.h | 2 ++ + 3 files changed, 41 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #define REALTEK_HW_STOP_DELAY 25 /* msecs */ + #define REALTEK_HW_START_DELAY 100 /* msecs */ +@@ -48,6 +49,7 @@ struct rtl8366_vlan_4k { + + struct realtek_priv { + struct device *dev; ++ struct reset_control *reset_ctl; + struct gpio_desc *reset; + struct gpio_desc *mdc; + struct gpio_desc *mdio; +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -185,6 +185,13 @@ rtl83xx_probe(struct device *dev, + "realtek,disable-leds"); + + /* TODO: if power is software controlled, set up any regulators here */ ++ priv->reset_ctl = devm_reset_control_get_optional(dev, NULL); ++ if (IS_ERR(priv->reset_ctl)) { ++ ret = PTR_ERR(priv->reset_ctl); ++ dev_err_probe(dev, ret, "failed to get reset control\n"); ++ return ERR_CAST(priv->reset_ctl); ++ } ++ + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + dev_err(dev, "failed to get RESET GPIO\n"); +@@ -193,11 +200,11 @@ rtl83xx_probe(struct device *dev, + + dev_set_drvdata(dev, priv); + +- if (priv->reset) { +- gpiod_set_value(priv->reset, 1); ++ if (priv->reset_ctl || priv->reset) { ++ rtl83xx_reset_assert(priv); + dev_dbg(dev, "asserted RESET\n"); + msleep(REALTEK_HW_STOP_DELAY); +- gpiod_set_value(priv->reset, 0); ++ rtl83xx_reset_deassert(priv); + msleep(REALTEK_HW_START_DELAY); + dev_dbg(dev, "deasserted RESET\n"); + } +@@ -293,11 +300,36 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, R + void rtl83xx_remove(struct realtek_priv *priv) + { + /* leave the device reset asserted */ +- if (priv->reset) +- gpiod_set_value(priv->reset, 1); ++ rtl83xx_reset_assert(priv); + } + EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); + ++void rtl83xx_reset_assert(struct realtek_priv *priv) ++{ ++ int ret; ++ ++ ret = reset_control_assert(priv->reset_ctl); ++ if (ret) ++ dev_warn(priv->dev, ++ "Failed to assert the switch reset control: %pe\n", ++ ERR_PTR(ret)); ++ ++ gpiod_set_value(priv->reset, true); ++} ++ ++void rtl83xx_reset_deassert(struct realtek_priv *priv) ++{ ++ int ret; ++ ++ ret = reset_control_deassert(priv->reset_ctl); ++ if (ret) ++ dev_warn(priv->dev, ++ "Failed to deassert the switch reset control: %pe\n", ++ ERR_PTR(ret)); ++ ++ gpiod_set_value(priv->reset, false); ++} ++ + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Realtek DSA switches common module"); +--- a/drivers/net/dsa/realtek/rtl83xx.h ++++ b/drivers/net/dsa/realtek/rtl83xx.h +@@ -18,5 +18,7 @@ int rtl83xx_register_switch(struct realt + void rtl83xx_unregister_switch(struct realtek_priv *priv); + void rtl83xx_shutdown(struct realtek_priv *priv); + void rtl83xx_remove(struct realtek_priv *priv); ++void rtl83xx_reset_assert(struct realtek_priv *priv); ++void rtl83xx_reset_deassert(struct realtek_priv *priv); + + #endif /* _RTL83XX_H */ diff --git a/target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch b/target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch new file mode 100644 index 0000000000..7299a25e37 --- /dev/null +++ b/target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch @@ -0,0 +1,45 @@ +From 4f580e9aced1816398c1c64f178302a22b8ea6e2 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sat, 27 Apr 2024 02:11:29 -0300 +Subject: [PATCH 2/3] net: dsa: realtek: do not assert reset on remove + +The necessity of asserting the reset on removal was previously +questioned, as DSA's own cleanup methods should suffice to prevent +traffic leakage[1]. + +When a driver has subdrivers controlled by devres, they will be +unregistered after the main driver's .remove is executed. If it asserts +a reset, the subdrivers will be unable to communicate with the hardware +during their cleanup. For LEDs, this means that they will fail to turn +off, resulting in a timeout error. + +[1] https://lore.kernel.org/r/20240123215606.26716-9-luizluca@gmail.com/ + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/rtl83xx.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -291,16 +291,13 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, R + * rtl83xx_remove() - Cleanup a realtek switch driver + * @priv: realtek_priv pointer + * +- * If a method is provided, this function asserts the hard reset of the switch +- * in order to avoid leaking traffic when the driver is gone. ++ * Placehold for common cleanup procedures. + * +- * Context: Might sleep if priv->gdev->chip->can_sleep. ++ * Context: Any + * Return: nothing + */ + void rtl83xx_remove(struct realtek_priv *priv) + { +- /* leave the device reset asserted */ +- rtl83xx_reset_assert(priv); + } + EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); + diff --git a/target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch b/target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch new file mode 100644 index 0000000000..4c9ea7fe12 --- /dev/null +++ b/target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch @@ -0,0 +1,396 @@ +From 32d617005475a71ebcc4ec8b2791e8d1481e9a10 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sat, 27 Apr 2024 02:11:30 -0300 +Subject: [PATCH 3/3] net: dsa: realtek: add LED drivers for rtl8366rb + +This commit introduces LED drivers for rtl8366rb, enabling LEDs to be +described in the device tree using the same format as qca8k. Each port +can configure up to 4 LEDs. + +If all LEDs in a group use the default state "keep", they will use the +default behavior after a reset. Changing the brightness of one LED, +either manually or by a trigger, will disable the default hardware +trigger and switch the entire LED group to manually controlled LEDs. +Once in this mode, there is no way to revert to hardware-controlled LEDs +(except by resetting the switch). + +Software triggers function as expected with manually controlled LEDs. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/rtl8366rb.c | 304 ++++++++++++++++++++++++---- + 1 file changed, 265 insertions(+), 39 deletions(-) + +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -180,6 +180,7 @@ + #define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f + + /* LED control registers */ ++/* The LED blink rate is global; it is used by all triggers in all groups. */ + #define RTL8366RB_LED_BLINKRATE_REG 0x0430 + #define RTL8366RB_LED_BLINKRATE_MASK 0x0007 + #define RTL8366RB_LED_BLINKRATE_28MS 0x0000 +@@ -195,31 +196,21 @@ + (4 * (led_group)) + #define RTL8366RB_LED_CTRL_MASK(led_group) \ + (0xf << RTL8366RB_LED_CTRL_OFFSET(led_group)) +-#define RTL8366RB_LED_OFF 0x0 +-#define RTL8366RB_LED_DUP_COL 0x1 +-#define RTL8366RB_LED_LINK_ACT 0x2 +-#define RTL8366RB_LED_SPD1000 0x3 +-#define RTL8366RB_LED_SPD100 0x4 +-#define RTL8366RB_LED_SPD10 0x5 +-#define RTL8366RB_LED_SPD1000_ACT 0x6 +-#define RTL8366RB_LED_SPD100_ACT 0x7 +-#define RTL8366RB_LED_SPD10_ACT 0x8 +-#define RTL8366RB_LED_SPD100_10_ACT 0x9 +-#define RTL8366RB_LED_FIBER 0xa +-#define RTL8366RB_LED_AN_FAULT 0xb +-#define RTL8366RB_LED_LINK_RX 0xc +-#define RTL8366RB_LED_LINK_TX 0xd +-#define RTL8366RB_LED_MASTER 0xe +-#define RTL8366RB_LED_FORCE 0xf + + /* The RTL8366RB_LED_X_X registers are used to manually set the LED state only + * when the corresponding LED group in RTL8366RB_LED_CTRL_REG is +- * RTL8366RB_LED_FORCE. Otherwise, it is ignored. ++ * RTL8366RB_LEDGROUP_FORCE. Otherwise, it is ignored. + */ + #define RTL8366RB_LED_0_1_CTRL_REG 0x0432 +-#define RTL8366RB_LED_1_OFFSET 6 + #define RTL8366RB_LED_2_3_CTRL_REG 0x0433 +-#define RTL8366RB_LED_3_OFFSET 6 ++#define RTL8366RB_LED_X_X_CTRL_REG(led_group) \ ++ ((led_group) <= 1 ? \ ++ RTL8366RB_LED_0_1_CTRL_REG : \ ++ RTL8366RB_LED_2_3_CTRL_REG) ++#define RTL8366RB_LED_0_X_CTRL_MASK GENMASK(5, 0) ++#define RTL8366RB_LED_X_1_CTRL_MASK GENMASK(11, 6) ++#define RTL8366RB_LED_2_X_CTRL_MASK GENMASK(5, 0) ++#define RTL8366RB_LED_X_3_CTRL_MASK GENMASK(11, 6) + + #define RTL8366RB_MIB_COUNT 33 + #define RTL8366RB_GLOBAL_MIB_COUNT 1 +@@ -363,14 +354,44 @@ + #define RTL8366RB_GREEN_FEATURE_TX BIT(0) + #define RTL8366RB_GREEN_FEATURE_RX BIT(2) + ++enum rtl8366_ledgroup_mode { ++ RTL8366RB_LEDGROUP_OFF = 0x0, ++ RTL8366RB_LEDGROUP_DUP_COL = 0x1, ++ RTL8366RB_LEDGROUP_LINK_ACT = 0x2, ++ RTL8366RB_LEDGROUP_SPD1000 = 0x3, ++ RTL8366RB_LEDGROUP_SPD100 = 0x4, ++ RTL8366RB_LEDGROUP_SPD10 = 0x5, ++ RTL8366RB_LEDGROUP_SPD1000_ACT = 0x6, ++ RTL8366RB_LEDGROUP_SPD100_ACT = 0x7, ++ RTL8366RB_LEDGROUP_SPD10_ACT = 0x8, ++ RTL8366RB_LEDGROUP_SPD100_10_ACT = 0x9, ++ RTL8366RB_LEDGROUP_FIBER = 0xa, ++ RTL8366RB_LEDGROUP_AN_FAULT = 0xb, ++ RTL8366RB_LEDGROUP_LINK_RX = 0xc, ++ RTL8366RB_LEDGROUP_LINK_TX = 0xd, ++ RTL8366RB_LEDGROUP_MASTER = 0xe, ++ RTL8366RB_LEDGROUP_FORCE = 0xf, ++ ++ __RTL8366RB_LEDGROUP_MODE_MAX ++}; ++ ++struct rtl8366rb_led { ++ u8 port_num; ++ u8 led_group; ++ struct realtek_priv *priv; ++ struct led_classdev cdev; ++}; ++ + /** + * struct rtl8366rb - RTL8366RB-specific data + * @max_mtu: per-port max MTU setting + * @pvid_enabled: if PVID is set for respective port ++ * @leds: per-port and per-ledgroup led info + */ + struct rtl8366rb { + unsigned int max_mtu[RTL8366RB_NUM_PORTS]; + bool pvid_enabled[RTL8366RB_NUM_PORTS]; ++ struct rtl8366rb_led leds[RTL8366RB_NUM_PORTS][RTL8366RB_NUM_LEDGROUPS]; + }; + + static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { +@@ -813,6 +834,217 @@ static int rtl8366rb_jam_table(const str + return 0; + } + ++static int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv, ++ u8 led_group, ++ enum rtl8366_ledgroup_mode mode) ++{ ++ int ret; ++ u32 val; ++ ++ val = mode << RTL8366RB_LED_CTRL_OFFSET(led_group); ++ ++ ret = regmap_update_bits(priv->map, ++ RTL8366RB_LED_CTRL_REG, ++ RTL8366RB_LED_CTRL_MASK(led_group), ++ val); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static inline u32 rtl8366rb_led_group_port_mask(u8 led_group, u8 port) ++{ ++ switch (led_group) { ++ case 0: ++ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); ++ case 1: ++ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); ++ case 2: ++ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); ++ case 3: ++ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); ++ default: ++ return 0; ++ } ++} ++ ++static int rb8366rb_get_port_led(struct rtl8366rb_led *led) ++{ ++ struct realtek_priv *priv = led->priv; ++ u8 led_group = led->led_group; ++ u8 port_num = led->port_num; ++ int ret; ++ u32 val; ++ ++ ret = regmap_read(priv->map, RTL8366RB_LED_X_X_CTRL_REG(led_group), ++ &val); ++ if (ret) { ++ dev_err(priv->dev, "error reading LED on port %d group %d\n", ++ led_group, port_num); ++ return ret; ++ } ++ ++ return !!(val & rtl8366rb_led_group_port_mask(led_group, port_num)); ++} ++ ++static int rb8366rb_set_port_led(struct rtl8366rb_led *led, bool enable) ++{ ++ struct realtek_priv *priv = led->priv; ++ u8 led_group = led->led_group; ++ u8 port_num = led->port_num; ++ int ret; ++ ++ ret = regmap_update_bits(priv->map, ++ RTL8366RB_LED_X_X_CTRL_REG(led_group), ++ rtl8366rb_led_group_port_mask(led_group, ++ port_num), ++ enable ? 0xffff : 0); ++ if (ret) { ++ dev_err(priv->dev, "error updating LED on port %d group %d\n", ++ led_group, port_num); ++ return ret; ++ } ++ ++ /* Change the LED group to manual controlled LEDs if required */ ++ ret = rb8366rb_set_ledgroup_mode(priv, led_group, ++ RTL8366RB_LEDGROUP_FORCE); ++ ++ if (ret) { ++ dev_err(priv->dev, "error updating LED GROUP group %d\n", ++ led_group); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++rtl8366rb_cled_brightness_set_blocking(struct led_classdev *ldev, ++ enum led_brightness brightness) ++{ ++ struct rtl8366rb_led *led = container_of(ldev, struct rtl8366rb_led, ++ cdev); ++ ++ return rb8366rb_set_port_led(led, brightness == LED_ON); ++} ++ ++static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp, ++ struct fwnode_handle *led_fwnode) ++{ ++ struct rtl8366rb *rb = priv->chip_data; ++ struct led_init_data init_data = { }; ++ enum led_default_state state; ++ struct rtl8366rb_led *led; ++ u32 led_group; ++ int ret; ++ ++ ret = fwnode_property_read_u32(led_fwnode, "reg", &led_group); ++ if (ret) ++ return ret; ++ ++ if (led_group >= RTL8366RB_NUM_LEDGROUPS) { ++ dev_warn(priv->dev, "Invalid LED reg %d defined for port %d", ++ led_group, dp->index); ++ return -EINVAL; ++ } ++ ++ led = &rb->leds[dp->index][led_group]; ++ led->port_num = dp->index; ++ led->led_group = led_group; ++ led->priv = priv; ++ ++ state = led_init_default_state_get(led_fwnode); ++ switch (state) { ++ case LEDS_DEFSTATE_ON: ++ led->cdev.brightness = 1; ++ rb8366rb_set_port_led(led, 1); ++ break; ++ case LEDS_DEFSTATE_KEEP: ++ led->cdev.brightness = ++ rb8366rb_get_port_led(led); ++ break; ++ case LEDS_DEFSTATE_OFF: ++ default: ++ led->cdev.brightness = 0; ++ rb8366rb_set_port_led(led, 0); ++ } ++ ++ led->cdev.max_brightness = 1; ++ led->cdev.brightness_set_blocking = ++ rtl8366rb_cled_brightness_set_blocking; ++ init_data.fwnode = led_fwnode; ++ init_data.devname_mandatory = true; ++ ++ init_data.devicename = kasprintf(GFP_KERNEL, "Realtek-%d:0%d:%d", ++ dp->ds->index, dp->index, led_group); ++ if (!init_data.devicename) ++ return -ENOMEM; ++ ++ ret = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data); ++ if (ret) { ++ dev_warn(priv->dev, "Failed to init LED %d for port %d", ++ led_group, dp->index); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int rtl8366rb_setup_all_leds_off(struct realtek_priv *priv) ++{ ++ int ret = 0; ++ int i; ++ ++ regmap_update_bits(priv->map, ++ RTL8366RB_INTERRUPT_CONTROL_REG, ++ RTL8366RB_P4_RGMII_LED, ++ 0); ++ ++ for (i = 0; i < RTL8366RB_NUM_LEDGROUPS; i++) { ++ ret = rb8366rb_set_ledgroup_mode(priv, i, ++ RTL8366RB_LEDGROUP_OFF); ++ if (ret) ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int rtl8366rb_setup_leds(struct realtek_priv *priv) ++{ ++ struct device_node *leds_np, *led_np; ++ struct dsa_switch *ds = &priv->ds; ++ struct dsa_port *dp; ++ int ret = 0; ++ ++ dsa_switch_for_each_port(dp, ds) { ++ if (!dp->dn) ++ continue; ++ ++ leds_np = of_get_child_by_name(dp->dn, "leds"); ++ if (!leds_np) { ++ dev_dbg(priv->dev, "No leds defined for port %d", ++ dp->index); ++ continue; ++ } ++ ++ for_each_child_of_node(leds_np, led_np) { ++ ret = rtl8366rb_setup_led(priv, dp, ++ of_fwnode_handle(led_np)); ++ if (ret) { ++ of_node_put(led_np); ++ break; ++ } ++ } ++ ++ of_node_put(leds_np); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ + static int rtl8366rb_setup(struct dsa_switch *ds) + { + struct realtek_priv *priv = ds->priv; +@@ -821,7 +1053,6 @@ static int rtl8366rb_setup(struct dsa_sw + u32 chip_ver = 0; + u32 chip_id = 0; + int jam_size; +- u32 val; + int ret; + int i; + +@@ -997,7 +1228,9 @@ static int rtl8366rb_setup(struct dsa_sw + if (ret) + return ret; + +- /* Set blinking, TODO: make this configurable */ ++ /* Set blinking, used by all LED groups using HW triggers. ++ * TODO: make this configurable ++ */ + ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG, + RTL8366RB_LED_BLINKRATE_MASK, + RTL8366RB_LED_BLINKRATE_56MS); +@@ -1005,26 +1238,19 @@ static int rtl8366rb_setup(struct dsa_sw + return ret; + + /* Set up LED activity: +- * Each port has 4 LEDs, we configure all ports to the same +- * behaviour (no individual config) but we can set up each +- * LED separately. ++ * Each port has 4 LEDs on fixed groups. Each group shares the same ++ * hardware trigger across all ports. LEDs can only be indiviually ++ * controlled setting the LED group to fixed mode and using the driver ++ * to toggle them LEDs on/off. + */ + if (priv->leds_disabled) { +- /* Turn everything off */ +- regmap_update_bits(priv->map, +- RTL8366RB_INTERRUPT_CONTROL_REG, +- RTL8366RB_P4_RGMII_LED, +- 0); +- +- for (i = 0; i < RTL8366RB_NUM_LEDGROUPS; i++) { +- val = RTL8366RB_LED_OFF << RTL8366RB_LED_CTRL_OFFSET(i); +- ret = regmap_update_bits(priv->map, +- RTL8366RB_LED_CTRL_REG, +- RTL8366RB_LED_CTRL_MASK(i), +- val); +- if (ret) +- return ret; +- } ++ ret = rtl8366rb_setup_all_leds_off(priv); ++ if (ret) ++ return ret; ++ } else { ++ ret = rtl8366rb_setup_leds(priv); ++ if (ret) ++ return ret; + } + + ret = rtl8366_reset_vlan(priv); From 7bffb469bc8c44333be6239aef4fdc8cbeb5bc03 Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 7 Oct 2024 10:35:41 +0400 Subject: [PATCH 08/17] qca-ssdk: add support for ipq50xx The codename for IPQ50xx is Maple (abbreviated as 'MP'), so let's pass the codename to allow the QCA-SSDK to build for the IPQ50xx SoC. In addition, disable compiling the MP_PHY driver in favor of a native driver being upstreamed. Co-developed-by: Ziyang Huang Signed-off-by: Ziyang Huang Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- package/kernel/qca-ssdk/Makefile | 5 +- .../111-hsl_phy-split-MP_PHY-config.patch | 127 ++++++++++++++++++ ...t-MP-allow-to-ignore-reset-controlls.patch | 40 ++++++ .../patches/121-MP-fix-build-issues.patch | 82 +++++++++++ ...replace-ioremap_nocache-with-ioremap.patch | 57 ++++++++ 5 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 package/kernel/qca-ssdk/patches/111-hsl_phy-split-MP_PHY-config.patch create mode 100644 package/kernel/qca-ssdk/patches/112-init-MP-allow-to-ignore-reset-controlls.patch create mode 100644 package/kernel/qca-ssdk/patches/121-MP-fix-build-issues.patch create mode 100644 package/kernel/qca-ssdk/patches/122-init-replace-ioremap_nocache-with-ioremap.patch diff --git a/package/kernel/qca-ssdk/Makefile b/package/kernel/qca-ssdk/Makefile index 16b2b4477d..032f0de331 100644 --- a/package/kernel/qca-ssdk/Makefile +++ b/package/kernel/qca-ssdk/Makefile @@ -49,7 +49,7 @@ MAKE_FLAGS+= \ PTP_FEATURE=disable SWCONFIG_FEATURE=disable \ ISISC_ENABLE=disable MHT_ENABLE=disable \ IN_QCA803X_PHY=FALSE IN_QCA808X_PHY=FALSE \ - IN_MALIBU_PHY=FALSE \ + IN_MALIBU_PHY=FALSE IN_MP_PHY=FALSE \ $(LNX_CONFIG_OPTS) ifeq ($(CONFIG_TARGET_SUBTARGET), "ipq807x") @@ -60,6 +60,9 @@ ifeq ($(CONFIG_TARGET_SUBTARGET), "ipq60xx") MAKE_FLAGS+= CHIP_TYPE=CPPE endif +ifeq ($(CONFIG_TARGET_SUBTARGET), "ipq50xx") + MAKE_FLAGS+= CHIP_TYPE=MP +endif define Build/Compile +$(MAKE) $(PKG_JOBS) $(MAKE_FLAGS) -C $(PKG_BUILD_DIR) $(LNX_CONFIG_OPTS) diff --git a/package/kernel/qca-ssdk/patches/111-hsl_phy-split-MP_PHY-config.patch b/package/kernel/qca-ssdk/patches/111-hsl_phy-split-MP_PHY-config.patch new file mode 100644 index 0000000000..8dd18c08e0 --- /dev/null +++ b/package/kernel/qca-ssdk/patches/111-hsl_phy-split-MP_PHY-config.patch @@ -0,0 +1,127 @@ +From 15847e1f56b7f9423095cd96fd9d524a41bee814 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 15:24:07 +0800 +Subject: [PATCH] hsl_phy: split MP_PHY config + +Compiling the MP_PHY driver for ipq50xx is disabled in the Makefile in +favor of a native driver being upstreamed. As such, conditionally disable +unneeded flags and code associated to initializing the MP GE PHY that +would otherwise conflict with the native driver. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + config | 1 + + make/linux_opt.mk | 5 +++++ + src/adpt/mp/adpt_mp_portctrl.c | 4 ++++ + src/hsl/phy/Makefile | 8 +------- + src/hsl/phy/hsl_phy.c | 4 ++-- + 5 files changed, 13 insertions(+), 9 deletions(-) + +diff --git a/config b/config +index 99d99dff..1f74e4f8 100644 +--- a/config ++++ b/config +@@ -299,6 +299,7 @@ else ifeq (DESS, $(CHIP_TYPE)) + else ifeq (MP, $(CHIP_TYPE)) + IN_QCA803X_PHY=TRUE + IN_QCA808X_PHY=TRUE ++ IN_MP_PHY=TRUE + IN_SFP_PHY=TRUE + IN_SFP=TRUE + else ifeq (APPE, $(CHIP_TYPE)) +diff --git a/make/linux_opt.mk b/make/linux_opt.mk +index 6936b754..66b08ef5 100644 +--- a/make/linux_opt.mk ++++ b/make/linux_opt.mk +@@ -183,6 +183,11 @@ endif + ifeq (TRUE, $(IN_QCA808X_PHY)) + MODULE_CFLAG += -DIN_QCA808X_PHY + endif ++ ++ifeq (TRUE, $(IN_MP_PHY)) ++ MODULE_CFLAG += -DIN_MP_PHY ++endif ++ + ifeq (TRUE, $(IN_SFP_PHY)) + MODULE_CFLAG += -DIN_SFP_PHY + endif +diff --git a/src/adpt/mp/adpt_mp_portctrl.c b/src/adpt/mp/adpt_mp_portctrl.c +index 2c983fff..db60fc72 100644 +--- a/src/adpt/mp/adpt_mp_portctrl.c ++++ b/src/adpt/mp/adpt_mp_portctrl.c +@@ -92,12 +92,15 @@ static sw_error_t + adpt_mp_port_reset_set(a_uint32_t dev_id, a_uint32_t port_id) + { + sw_error_t rv = 0; ++#ifdef IN_MP_PHY + a_uint32_t phy_addr; + hsl_phy_ops_t *phy_drv; ++#endif + + ADPT_DEV_ID_CHECK(dev_id); + + if (port_id == SSDK_PHYSICAL_PORT1) { ++#ifdef IN_MP_PHY + /*internal gephy reset*/ + SW_RTN_ON_NULL (phy_drv = hsl_phy_api_ops_get(dev_id, + port_id)); +@@ -107,6 +110,7 @@ adpt_mp_port_reset_set(a_uint32_t dev_id, a_uint32_t port_id) + SW_RTN_ON_ERROR (rv); + rv = phy_drv->phy_function_reset(dev_id, phy_addr, PHY_FIFO_RESET); + SW_RTN_ON_ERROR (rv); ++#endif + } else if (port_id == SSDK_PHYSICAL_PORT2) { + rv = adpt_mp_uniphy_adapter_port_reset(dev_id, port_id); + } else { +diff --git a/src/hsl/phy/Makefile b/src/hsl/phy/Makefile +index 68d0679f..0eae9377 100755 +--- a/src/hsl/phy/Makefile ++++ b/src/hsl/phy/Makefile +@@ -23,7 +23,7 @@ ifeq (ISIS, $(CHIP_TYPE)) + SRC_LIST = f1_phy.c + endif + +-ifeq (MP, $(CHIP_TYPE)) ++ifeq (TRUE, $(IN_MP_PHY)) + SRC_LIST = mpge_phy.c + ifeq (TRUE, $(IN_LED)) + SRC_LIST += mpge_led.c +@@ -40,12 +40,6 @@ endif + + ifeq (ALL_CHIP, $(CHIP_TYPE)) + SRC_LIST = f1_phy.c f2_phy.c malibu_phy.c +-ifneq (,$(filter MP, $(SUPPORT_CHIP))) +- SRC_LIST += mpge_phy.c +-ifeq (TRUE, $(IN_LED)) +- SRC_LIST += mpge_led.c +-endif +-endif + endif + + ifeq (NONHK_CHIP, $(CHIP_TYPE)) +diff --git a/src/hsl/phy/hsl_phy.c b/src/hsl/phy/hsl_phy.c +index f2cf90e2..efab2343 100644 +--- a/src/hsl/phy/hsl_phy.c ++++ b/src/hsl/phy/hsl_phy.c +@@ -28,7 +28,7 @@ + #if defined(ATHENA) ||defined(SHIVA) ||defined(HORUS) + #include + #endif +-#ifdef MP ++#ifdef IN_MP_PHY + #include "mpge_phy.h" + #endif + #ifdef IN_MALIBU_PHY +@@ -94,7 +94,7 @@ phy_driver_instance_t ssdk_phy_driver[] = + #else + {SFP_PHY_CHIP, {0}, NULL, NULL, NULL}, + #endif +- #ifdef MP ++ #ifdef IN_MP_PHY + {MPGE_PHY_CHIP, {0}, NULL, mpge_phy_init, NULL}, + #else + {MPGE_PHY_CHIP, {0}, NULL, NULL, NULL}, +-- +2.40.1 + diff --git a/package/kernel/qca-ssdk/patches/112-init-MP-allow-to-ignore-reset-controlls.patch b/package/kernel/qca-ssdk/patches/112-init-MP-allow-to-ignore-reset-controlls.patch new file mode 100644 index 0000000000..616ccea252 --- /dev/null +++ b/package/kernel/qca-ssdk/patches/112-init-MP-allow-to-ignore-reset-controlls.patch @@ -0,0 +1,40 @@ +From 01fb404dbda1872ad99cea88bf43313bed30200a Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 15:24:07 +0800 +Subject: [PATCH] init: MP: allow to ignore reset controlls + +The SSDK is not used anymore to initialize the internal IPQ5018 GE PHY as +there is a separate driver pending upstream review/approval: +https://lore.kernel.org/all/TYZPR01MB5556D5568546D6DA4313209EC9762@ \ +TYZPR01MB5556.apcprd01.prod.exchangelabs.com/ + +As such, change the code to not error out when the reset controls aren't +found in the DTS where the SSDK expects them. These resets are now defined +under the definition based on the new driver mentioned above. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + src/init/ssdk_clk.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/init/ssdk_clk.c b/src/init/ssdk_clk.c +index 71e59452..bc244c6e 100644 +--- a/src/init/ssdk_clk.c ++++ b/src/init/ssdk_clk.c +@@ -1282,10 +1282,8 @@ ssdk_mp_reset_init(void) + + for (i = 0; i < MP_BCR_RST_MAX; i++) { + rst = of_reset_control_get(rst_node, mp_rst_ids[i]); +- if (IS_ERR(rst)) { +- SSDK_ERROR("%s not exist!\n", mp_rst_ids[i]); +- return; +- } ++ if (IS_ERR(rst)) ++ continue; + ssdk_gcc_reset(rst, SSDK_RESET_ASSERT); + msleep(200); + ssdk_gcc_reset(rst, SSDK_RESET_DEASSERT); +-- +2.40.1 + diff --git a/package/kernel/qca-ssdk/patches/121-MP-fix-build-issues.patch b/package/kernel/qca-ssdk/patches/121-MP-fix-build-issues.patch new file mode 100644 index 0000000000..14fe42bc73 --- /dev/null +++ b/package/kernel/qca-ssdk/patches/121-MP-fix-build-issues.patch @@ -0,0 +1,82 @@ +From a4378eb29c7b9dd95601d20f507a2220457f8ede Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 15:24:07 +0800 +Subject: [PATCH] MP: fix build issues + +Enable the IN_VSI make flag which enables macro definitions needed to +successfully compile the SSDK for the ipq50xx target. In addition, fix an +incorrect return type by expanding the macro called and return a boolean +instead of an integer. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + config | 30 +----------------------------- + src/adpt/mp/adpt_mp_portctrl.c | 3 ++- + 2 files changed, 3 insertions(+), 30 deletions(-) + +diff --git a/config b/config +index 1f74e4f8..58d67648 100644 +--- a/config ++++ b/config +@@ -374,6 +374,7 @@ ifneq (, $(filter MPPE APPE HPPE CPPE ALL_CHIP, $(CHIP_TYPE))) + endif + + ifneq (, $(filter MP, $(CHIP_TYPE))) ++ IN_VSI=TRUE + IN_UNIPHY=TRUE + endif + +@@ -436,35 +437,6 @@ endif + # SDK Features According To Specfic Switch # + ############################################# + ifeq (MP, $(CHIP_TYPE)) +- ifeq (disable, $(ISISC_ENABLE)) +- IN_ACL=FALSE +- IN_FDB=FALSE +- IN_IGMP=FALSE +- IN_LEAKY=FALSE +- IN_LED=FALSE +- IN_MIRROR=FALSE +- IN_MISC=FALSE +- IN_PORTVLAN=FALSE +- IN_QOS=FALSE +- IN_RATE=FALSE +- IN_STP=FALSE +- IN_VLAN=FALSE +- IN_REDUCED_ACL=FALSE +- IN_COSMAP=FALSE +- IN_IP=FALSE +- IN_NAT=FALSE +- IN_FLOW=FALSE +- IN_TRUNK=FALSE +- IN_RSS_HASH=FALSE +- IN_SEC=FALSE +- IN_QM=FALSE +- IN_PPPOE=FALSE +- IN_VSI=FALSE +- IN_SERVCODE=FALSE +- IN_BM=FALSE +- IN_SHAPER=FALSE +- IN_POLICER=FALSE +- endif + IN_CTRLPKT=TRUE + endif + +diff --git a/src/adpt/mp/adpt_mp_portctrl.c b/src/adpt/mp/adpt_mp_portctrl.c +index db60fc72..c230e214 100644 +--- a/src/adpt/mp/adpt_mp_portctrl.c ++++ b/src/adpt/mp/adpt_mp_portctrl.c +@@ -45,7 +45,8 @@ _adpt_mp_gcc_mac_clock_set(a_uint32_t dev_id, + static a_bool_t + _adpt_mp_port_phy_connected (a_uint32_t dev_id, fal_port_t port_id) + { +- ADPT_DEV_ID_CHECK(dev_id); ++ if (dev_id >= SW_MAX_NR_DEV) ++ return A_FALSE; + + /* force port which connect s17c or other device chip*/ + if (hsl_port_feature_get(dev_id, port_id, PHY_F_FORCE | PHY_F_SFP)) { +-- +2.40.1 + diff --git a/package/kernel/qca-ssdk/patches/122-init-replace-ioremap_nocache-with-ioremap.patch b/package/kernel/qca-ssdk/patches/122-init-replace-ioremap_nocache-with-ioremap.patch new file mode 100644 index 0000000000..1e1e909bdb --- /dev/null +++ b/package/kernel/qca-ssdk/patches/122-init-replace-ioremap_nocache-with-ioremap.patch @@ -0,0 +1,57 @@ +From a90a9f3e2a21cb87c2cbf2ddb999846aa614e88a Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 15:24:07 +0800 +Subject: [PATCH 2/2] init: replace ioremap_nocache() with ioremap() + +As per https://lore.kernel.org/linux-mips/20191209194819.GA28157@lst.de/T/, +ioremap_nocache is deprecated so let's replace all calls by ioremap instead. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + src/init/ssdk_clk.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/init/ssdk_clk.c b/src/init/ssdk_clk.c +index bc244c6e..dc45691e 100644 +--- a/src/init/ssdk_clk.c ++++ b/src/init/ssdk_clk.c +@@ -1183,7 +1183,7 @@ ssdk_mp_tcsr_get(a_uint32_t tcsr_offset, a_uint32_t *tcsr_val) + { + void __iomem *tcsr_base = NULL; + +- tcsr_base = ioremap_nocache(TCSR_ETH_ADDR, TCSR_ETH_SIZE); ++ tcsr_base = ioremap(TCSR_ETH_ADDR, TCSR_ETH_SIZE); + if (!tcsr_base) + { + SSDK_ERROR("Failed to map tcsr eth address!\n"); +@@ -1200,7 +1200,7 @@ ssdk_mp_tcsr_set(a_uint32_t tcsr_offset, a_uint32_t tcsr_val) + { + void __iomem *tcsr_base = NULL; + +- tcsr_base = ioremap_nocache(TCSR_ETH_ADDR, TCSR_ETH_SIZE); ++ tcsr_base = ioremap(TCSR_ETH_ADDR, TCSR_ETH_SIZE); + if (!tcsr_base) + { + SSDK_ERROR("Failed to map tcsr eth address!\n"); +@@ -1248,7 +1248,7 @@ ssdk_mp_cmnblk_stable_check(void) + a_uint32_t reg_val; + int i, loops = 20; + +- pll_lock = ioremap_nocache(CMN_PLL_LOCKED_ADDR, CMN_PLL_LOCKED_SIZE); ++ pll_lock = ioremap(CMN_PLL_LOCKED_ADDR, CMN_PLL_LOCKED_SIZE); + if (!pll_lock) { + SSDK_ERROR("Failed to map CMN PLL LOCK register!\n"); + return A_FALSE; +@@ -1303,7 +1303,7 @@ static void ssdk_cmnblk_pll_src_set(enum cmnblk_pll_src_type pll_source) + void __iomem *cmn_pll_src_base = NULL; + a_uint32_t reg_val; + +- cmn_pll_src_base = ioremap_nocache(CMN_BLK_PLL_SRC_ADDR, CMN_BLK_SIZE); ++ cmn_pll_src_base = ioremap(CMN_BLK_PLL_SRC_ADDR, CMN_BLK_SIZE); + if (!cmn_pll_src_base) { + SSDK_ERROR("Failed to map cmn pll source address!\n"); + return; +-- +2.40.1 + From 39750798f7ad9f8e0b999eee999bceb38138763e Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 7 Oct 2024 10:42:07 +0400 Subject: [PATCH 09/17] qca-nss-dp: add support for IPQ50xx Add support for the Qualcomm IPQ50xx in the QCA NSS dataplane driver. The QCA implementation uses depracated DMA api calls and a downstream SCM call, so convert to proper Linux DMA and SCM api calls. In addition, add fixed-link support to support SGMII which is used to connect the internal IPQ50xx switch to an external switch (ex. QCA8337) Co-developed-by: Ziyang Huang Signed-off-by: Ziyang Huang Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- ...012-01-syn-gmac-use-standard-DMA-api.patch | 245 ++++++++++++++++++ ...e-corrent-scm-function-to-write-tcsr.patch | 46 ++++ .../0013-nss_dp_main-support-fixed-link.patch | 49 ++++ 3 files changed, 340 insertions(+) create mode 100644 package/kernel/qca-nss-dp/patches/0012-01-syn-gmac-use-standard-DMA-api.patch create mode 100644 package/kernel/qca-nss-dp/patches/0012-02-ipq50xx-use-corrent-scm-function-to-write-tcsr.patch create mode 100644 package/kernel/qca-nss-dp/patches/0013-nss_dp_main-support-fixed-link.patch diff --git a/package/kernel/qca-nss-dp/patches/0012-01-syn-gmac-use-standard-DMA-api.patch b/package/kernel/qca-nss-dp/patches/0012-01-syn-gmac-use-standard-DMA-api.patch new file mode 100644 index 0000000000..882bc8cad9 --- /dev/null +++ b/package/kernel/qca-nss-dp/patches/0012-01-syn-gmac-use-standard-DMA-api.patch @@ -0,0 +1,245 @@ +From 5ad8cf24897ff903112967a9662cb13ed4cbbf57 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Mon, 22 Apr 2024 21:47:58 +0800 +Subject: [PATCH] syn-gmac: use standard DMA api + +The SSDK uses several old DMA API calls based on raw assembly which have +been deprecated. So let's convert to and use the standard Linux DMA API +instead. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c | 14 ++++++-- + hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c | 2 ++ + hal/dp_ops/syn_gmac_dp/syn_dp_rx.c | 47 +++++++++++++------------- + hal/dp_ops/syn_gmac_dp/syn_dp_tx.c | 23 ++++--------- + 4 files changed, 42 insertions(+), 44 deletions(-) + +diff --git a/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c b/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c +index 8cbbcaaf..1c9006c7 100644 +--- a/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c +@@ -26,6 +26,7 @@ static int syn_dp_cfg_rx_setup_desc_queue(struct syn_dp_info *dev_info) + { + struct syn_dp_info_rx *rx_info = &dev_info->dp_info_rx; + struct dma_desc_rx *first_desc = NULL; ++ dma_addr_t dma_addr; + struct net_device *netdev = rx_info->netdev; + + netdev_dbg(netdev, "Total size of memory required for Rx Descriptors in Ring Mode = %u\n", (uint32_t)((sizeof(struct dma_desc_rx) * SYN_DP_RX_DESC_SIZE))); +@@ -33,13 +34,15 @@ static int syn_dp_cfg_rx_setup_desc_queue(struct syn_dp_info *dev_info) + /* + * Allocate cacheable descriptors for Rx + */ +- first_desc = kzalloc(sizeof(struct dma_desc_rx) * SYN_DP_RX_DESC_SIZE, GFP_KERNEL); ++ first_desc = dma_alloc_coherent(rx_info->dev, ++ sizeof(struct dma_desc_rx) * SYN_DP_RX_DESC_SIZE, ++ &dma_addr, GFP_KERNEL); + if (!first_desc) { + netdev_dbg(netdev, "Error in Rx Descriptor Memory allocation in Ring mode\n"); + return -ENOMEM; + } + +- dev_info->rx_desc_dma_addr = (dma_addr_t)virt_to_phys(first_desc); ++ dev_info->rx_desc_dma_addr = dma_addr; + rx_info->rx_desc = first_desc; + syn_dp_gmac_rx_desc_init_ring(rx_info->rx_desc, SYN_DP_RX_DESC_SIZE); + +@@ -98,6 +101,10 @@ void syn_dp_cfg_rx_cleanup_rings(struct syn_dp_info *dev_info) + for (i = 0; i < rx_info->busy_rx_desc_cnt; i++) { + rx_skb_index = (rx_skb_index + i) & SYN_DP_RX_DESC_MAX_INDEX; + rxdesc = rx_info->rx_desc; ++ ++ dma_unmap_single(rx_info->dev, rxdesc->buffer1, ++ rxdesc->length, DMA_FROM_DEVICE); ++ + skb = rx_info->rx_buf_pool[rx_skb_index].skb; + if (unlikely(skb != NULL)) { + dev_kfree_skb_any(skb); +@@ -105,7 +112,8 @@ void syn_dp_cfg_rx_cleanup_rings(struct syn_dp_info *dev_info) + } + } + +- kfree(rx_info->rx_desc); ++ dma_free_coherent(rx_info->dev, (sizeof(struct dma_desc_rx) * SYN_DP_RX_DESC_SIZE), ++ rx_info->rx_desc, dev_info->rx_desc_dma_addr); + rx_info->rx_desc = NULL; + dev_info->rx_desc_dma_addr = (dma_addr_t)0; + } +diff --git a/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c b/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c +index bf5e19a0..284e8880 100644 +--- a/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c +@@ -91,6 +91,8 @@ void syn_dp_cfg_tx_cleanup_rings(struct syn_dp_info *dev_info) + tx_skb_index = syn_dp_tx_inc_index(tx_skb_index, i); + txdesc = tx_info->tx_desc; + ++ dma_unmap_single(tx_info->dev, txdesc->buffer1, txdesc->length, DMA_TO_DEVICE); ++ + skb = tx_info->tx_buf_pool[tx_skb_index].skb; + if (unlikely(skb != NULL)) { + dev_kfree_skb_any(skb); +diff --git a/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c b/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c +index 1ddeb7d6..1798d4e7 100644 +--- a/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c +@@ -73,16 +73,6 @@ static inline void syn_dp_rx_refill_one_desc(struct dma_desc_rx *rx_desc, + */ + static inline void syn_dp_rx_inval_and_flush(struct syn_dp_info_rx *rx_info, uint32_t start, uint32_t end) + { +- /* +- * Batched flush and invalidation of the rx descriptors +- */ +- if (end > start) { +- dmac_flush_range_no_dsb((void *)&rx_info->rx_desc[start], (void *)&rx_info->rx_desc[end] + sizeof(struct dma_desc_rx)); +- } else { +- dmac_flush_range_no_dsb((void *)&rx_info->rx_desc[start], (void *)&rx_info->rx_desc[SYN_DP_RX_DESC_MAX_INDEX] + sizeof(struct dma_desc_rx)); +- dmac_flush_range_no_dsb((void *)&rx_info->rx_desc[0], (void *)&rx_info->rx_desc[end] + sizeof(struct dma_desc_rx)); +- } +- + dsb(st); + } + +@@ -124,15 +114,19 @@ int syn_dp_rx_refill_page_mode(struct syn_dp_info_rx *rx_info) + break; + } + ++ skb_fill_page_desc(skb, 0, pg, 0, PAGE_SIZE); ++ + /* + * Get virtual address of allocated page. + */ + page_addr = page_address(pg); +- dma_addr = (dma_addr_t)virt_to_phys(page_addr); +- +- skb_fill_page_desc(skb, 0, pg, 0, PAGE_SIZE); ++ dma_addr = dma_map_page(rx_info->dev, pg, 0, PAGE_SIZE, DMA_FROM_DEVICE); ++ if (unlikely(dma_mapping_error(rx_info->dev, dma_addr))) { ++ dev_kfree_skb(skb); ++ netdev_dbg(netdev, "DMA mapping failed for empty buffer\n"); ++ break; ++ } + +- dmac_inv_range_no_dsb(page_addr, (page_addr + PAGE_SIZE)); + rx_refill_idx = rx_info->rx_refill_idx; + rx_desc = rx_info->rx_desc + rx_refill_idx; + +@@ -181,8 +175,15 @@ int syn_dp_rx_refill(struct syn_dp_info_rx *rx_info) + + skb_reserve(skb, SYN_DP_SKB_HEADROOM + NET_IP_ALIGN); + +- dma_addr = (dma_addr_t)virt_to_phys(skb->data); +- dmac_inv_range_no_dsb((void *)skb->data, (void *)(skb->data + inval_len)); ++ dma_addr = dma_map_single(rx_info->dev, skb->data, ++ inval_len, ++ DMA_FROM_DEVICE); ++ if (unlikely(dma_mapping_error(rx_info->dev, dma_addr))) { ++ dev_kfree_skb(skb); ++ netdev_dbg(netdev, "DMA mapping failed for empty buffer\n"); ++ break; ++ } ++ + rx_refill_idx = rx_info->rx_refill_idx; + rx_desc = rx_info->rx_desc + rx_refill_idx; + +@@ -407,12 +408,6 @@ int syn_dp_rx(struct syn_dp_info_rx *rx_info, int budget) + * this code is executing. + */ + end = syn_dp_rx_inc_index(rx_info->rx_idx, busy); +- if (end > start) { +- dmac_inv_range_no_dsb((void *)&rx_info->rx_desc[start], (void *)&rx_info->rx_desc[end] + sizeof(struct dma_desc_rx)); +- } else { +- dmac_inv_range_no_dsb((void *)&rx_info->rx_desc[start], (void *)&rx_info->rx_desc[SYN_DP_RX_DESC_MAX_INDEX] + sizeof(struct dma_desc_rx)); +- dmac_inv_range_no_dsb((void *)&rx_info->rx_desc[0], (void *)&rx_info->rx_desc[end] + sizeof(struct dma_desc_rx)); +- } + + dsb(st); + +@@ -439,8 +434,12 @@ int syn_dp_rx(struct syn_dp_info_rx *rx_info, int budget) + * speculative prefetch by CPU may have occurred. + */ + frame_length = syn_dp_gmac_get_rx_desc_frame_length(status); +- dmac_inv_range((void *)rx_buf->map_addr_virt, +- (void *)(((uint8_t *)rx_buf->map_addr_virt) + frame_length)); ++ if (likely(!rx_info->page_mode)) ++ dma_unmap_single(rx_info->dev, rx_desc->buffer1, ++ rx_info->alloc_buf_len, DMA_FROM_DEVICE); ++ else ++ dma_unmap_page(rx_info->dev, rx_desc->buffer1, ++ PAGE_SIZE, DMA_FROM_DEVICE); + prefetch((void *)rx_buf->map_addr_virt); + + rx_next_idx = syn_dp_rx_inc_index(rx_idx, 1); +diff --git a/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c b/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c +index c97e252b..6d4adb3f 100644 +--- a/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c +@@ -104,9 +104,7 @@ static inline struct dma_desc_tx *syn_dp_tx_process_nr_frags(struct syn_dp_info_ + BUG_ON(!length); + #endif + +- dma_addr = (dma_addr_t)virt_to_phys(frag_addr); +- +- dmac_clean_range_no_dsb(frag_addr, frag_addr + length); ++ dma_addr = dma_map_single(tx_info->dev, frag_addr, length, DMA_TO_DEVICE); + + *total_length += length; + tx_desc = syn_dp_tx_set_desc_sg(tx_info, dma_addr, length, DESC_OWN_BY_DMA); +@@ -150,8 +148,7 @@ int syn_dp_tx_nr_frags(struct syn_dp_info_tx *tx_info, struct sk_buff *skb) + /* + * Flush the dma for non-paged skb data + */ +- dma_addr = (dma_addr_t)virt_to_phys(skb->data); +- dmac_clean_range_no_dsb((void *)skb->data, (void *)(skb->data + length)); ++ dma_addr = dma_map_single(tx_info->dev, skb->data, length, DMA_TO_DEVICE); + + total_len = length; + +@@ -256,12 +253,7 @@ int syn_dp_tx_frag_list(struct syn_dp_info_tx *tx_info, struct sk_buff *skb) + return NETDEV_TX_BUSY; + } + +- dma_addr = (dma_addr_t)virt_to_phys(skb->data); +- +- /* +- * Flush the data area of the head skb +- */ +- dmac_clean_range_no_dsb((void *)skb->data, (void *)(skb->data + length)); ++ dma_addr = dma_map_single(tx_info->dev, skb->data, length, DMA_TO_DEVICE); + + total_len = length; + +@@ -290,9 +282,7 @@ int syn_dp_tx_frag_list(struct syn_dp_info_tx *tx_info, struct sk_buff *skb) + BUG_ON(!length); + #endif + +- dma_addr = (dma_addr_t)virt_to_phys(iter_skb->data); +- +- dmac_clean_range_no_dsb((void *)iter_skb->data, (void *)(iter_skb->data + length)); ++ dma_addr = dma_map_single(tx_info->dev, iter_skb->data, length, DMA_TO_DEVICE); + + total_len += length; + +@@ -445,6 +435,7 @@ int syn_dp_tx_complete(struct syn_dp_info_tx *tx_info, int budget) + break; + } + ++ dma_unmap_single(tx_info->dev, desc->buffer1, desc->length, DMA_TO_DEVICE); + + if (likely(status & DESC_TX_LAST)) { + tx_skb_index = syn_dp_tx_comp_index_get(tx_info); +@@ -571,9 +562,7 @@ int syn_dp_tx(struct syn_dp_info_tx *tx_info, struct sk_buff *skb) + return NETDEV_TX_BUSY; + } + +- dma_addr = (dma_addr_t)virt_to_phys(skb->data); +- +- dmac_clean_range_no_dsb((void *)skb->data, (void *)(skb->data + skb->len)); ++ dma_addr = dma_map_single(tx_info->dev, skb->data, skb->len, DMA_TO_DEVICE); + + /* + * Queue packet to the GMAC rings +-- +2.40.1 + diff --git a/package/kernel/qca-nss-dp/patches/0012-02-ipq50xx-use-corrent-scm-function-to-write-tcsr.patch b/package/kernel/qca-nss-dp/patches/0012-02-ipq50xx-use-corrent-scm-function-to-write-tcsr.patch new file mode 100644 index 0000000000..10a8eef6da --- /dev/null +++ b/package/kernel/qca-nss-dp/patches/0012-02-ipq50xx-use-corrent-scm-function-to-write-tcsr.patch @@ -0,0 +1,46 @@ +From ba430b1a512dc1972807a1dd5a8d31a78ac572ff Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Mon, 22 Apr 2024 21:49:18 +0800 +Subject: [PATCH] ipq50xx: use corrent scm function to write tcsr + +QCA leverages its own API function to write to the TCSR register space, +not available upstream. As such, convert to the upstream qcom SCM IO +write function. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + hal/soc_ops/ipq50xx/nss_ipq50xx.c | 9 ++------- + 1 file changed, 2 insertions(+), 7 deletions(-) + +diff --git a/hal/soc_ops/ipq50xx/nss_ipq50xx.c b/hal/soc_ops/ipq50xx/nss_ipq50xx.c +index 3e4491c0..e56de1cc 100644 +--- a/hal/soc_ops/ipq50xx/nss_ipq50xx.c ++++ b/hal/soc_ops/ipq50xx/nss_ipq50xx.c +@@ -18,7 +18,7 @@ + + #include + #include +-#include ++#include + #include "nss_dp_hal.h" + + /* +@@ -78,13 +78,8 @@ static void nss_dp_hal_tcsr_set(void) + * If TZ is not enabled, we can write to the register directly. + */ + if (qcom_scm_is_available()) { +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) +- err = qcom_scm_tcsr_reg_write((tcsr_base + TCSR_GMAC_AXI_CACHE_OVERRIDE_OFFSET), ++ err = qcom_scm_io_writel((tcsr_base + TCSR_GMAC_AXI_CACHE_OVERRIDE_OFFSET), + TCSR_GMAC_AXI_CACHE_OVERRIDE_VALUE); +-#else +- err = qti_scm_tcsr_reg_write((tcsr_base + TCSR_GMAC_AXI_CACHE_OVERRIDE_OFFSET), +- TCSR_GMAC_AXI_CACHE_OVERRIDE_VALUE); +-#endif + if (err) { + pr_err("%s: SCM TCSR write error: %d\n", __func__, err); + } +-- +2.40.1 + diff --git a/package/kernel/qca-nss-dp/patches/0013-nss_dp_main-support-fixed-link.patch b/package/kernel/qca-nss-dp/patches/0013-nss_dp_main-support-fixed-link.patch new file mode 100644 index 0000000000..668046881a --- /dev/null +++ b/package/kernel/qca-nss-dp/patches/0013-nss_dp_main-support-fixed-link.patch @@ -0,0 +1,49 @@ +From 309a1a330ccaa103a7648e944d97a0032116b338 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Mon, 22 Apr 2024 21:50:39 +0800 +Subject: [PATCH] nss_dp_main: support fixed-link + +Add support for "fixed link" to support ethernet MACs which are not +connected to an MDIO managed PHY. For example, IPQ5018 has two internal +MACs, one connected to a GE PHY and the other connected to a Uniphy to +support connections to an external switch over a fixed link. As such, +check for a fixed link in absence of a phy-handle and return an error +when no phy-handle and fixed link is found. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + nss_dp_main.c | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/nss_dp_main.c b/nss_dp_main.c +index 9a09edd5..204063bf 100644 +--- a/nss_dp_main.c ++++ b/nss_dp_main.c +@@ -619,11 +619,20 @@ static int32_t nss_dp_of_get_pdata(struct device_node *np, + } + + dp_priv->phy_node = of_parse_phandle(np, "phy-handle", 0); +- if (!dp_priv->phy_node) { +- pr_err("%s: error parsing phy-handle\n", np->name); +- return -EFAULT; ++ if(!dp_priv->phy_node) { ++ if(of_phy_is_fixed_link(np)) { ++ int ret = of_phy_register_fixed_link(np); ++ if(ret < 0) { ++ pr_err("%s: fail to register fixed-link: %d\n", np->name, ret); ++ return -EFAULT; ++ } ++ } ++ dp_priv->phy_node = of_node_get(np); + } + ++ if(!dp_priv->phy_node) ++ pr_err("%s: no phy-handle or fixed-link found\n", np->name); ++ + if (of_property_read_u32(np, "qcom,mactype", &hal_pdata->mactype)) { + pr_err("%s: error reading mactype\n", np->name); + return -EFAULT; +-- +2.40.1 + From 34d9172655af4e71f9b06229cbd5a716d359ac23 Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 7 Oct 2024 10:28:09 +0400 Subject: [PATCH 10/17] qualcommax: add ipq50xx target Introduce support for the Qualcomm IPQ50xx SoC. This series adds support for the following components: - minimal boot support: GCC/pinctrl/watchdog/CPUFreq/SDI (upstreamed) - USB2 (upstreamed) - Thermal/Tsens - PCIe gen2 1&2-lane PHY and controller - PWM and PWM LED - QPIC SPI NAND controller - CMN PLL Block (provider of fixed rate clocks to GCC/ethernet/more.) - Ethernet: IPQ5018 Internal GE PHY (1 gbps) - Remoteproc MPD driver for IPQ5018 (2.4G) & QCN6122 (5/6G) Wifi Co-developed-by: Ziyang Huang Signed-off-by: Ziyang Huang Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- target/linux/qualcommax/Makefile | 2 +- .../arch/arm64/boot/dts/qcom/ipq5018-ess.dtsi | 116 + target/linux/qualcommax/image/ipq50xx.mk | 0 .../linux/qualcommax/ipq50xx/config-default | 26 + target/linux/qualcommax/ipq50xx/target.mk | 7 + ...-arm64-dts-qcom-ipq5018-add-watchdog.patch | 31 + ...support-indicating-SDI-default-state.patch | 41 + ...are-qcom-scm-disable-SDI-if-required.patch | 83 + ...qcom-scm-document-IPQ5018-compatible.patch | 25 + ...-indicate-that-SDI-shoud-be-disabled.patch | 26 + ...-phy-qcom-m31-Add-IPQ5018-compatible.patch | 28 + ...atible-phy-init-sequence-for-IPQ5018.patch | 89 + ...ings-usb-dwc3-Add-IPQ5018-compatible.patch | 41 + ...s-qcom-ipq5018-Add-USB-related-nodes.patch | 86 + ...qcom-ipq5018-add-QUP1-SPI-controller.patch | 56 + ...k-qcom-a53pll-add-IPQ5018-compatible.patch | 25 + ...apss-ipq-pll-add-support-for-IPQ5018.patch | 62 + ...m-ipq5018-enable-the-CPUFreq-support.patch | 98 + ...add-few-more-reserved-memory-regions.patch | 66 + ...-ops-for-IPQ5018-to-fix-boot-failure.patch | 83 + ...pss-ipq-pll-fix-PLL-rate-for-IPQ5018.patch | 32 + ...-dt-bindings-pwm-add-IPQ6018-binding.patch | 60 + ...river-for-qualcomm-ipq6018-pwm-block.patch | 330 +++ ...r-Add-simple-mfd-support-for-IPQ6018.patch | 148 + ...indings-nvmem-add-IPQ5018-compatible.patch | 22 + ...al-qcom-tsens-Add-ipq5018-compatible.patch | 26 + ...com-add-new-feat-for-soc-without-rpm.patch | 45 + ...-tsens-add-support-for-IPQ5018-tsens.patch | 118 + ...0154-dts-qcom-IPQ5018-add-tsens-node.patch | 200 ++ ...com-uniphy-pcie-Document-PCIe-uniphy.patch | 85 + ...om-Introduce-PCIe-UNIPHY-28LP-driver.patch | 332 +++ ...m-uniphy-pcie-add-IPQ5018-compatible.patch | 25 + ...hy-pcie-28lp-add-support-for-IPQ5018.patch | 77 + ...dt-bindings-PCI-qcom-Add-IPQ5018-SoC.patch | 78 + ...161-PCI-qcom-add-support-for-IPQ5018.patch | 77 + ...-qcom-IPQ5018-add-PCIe-related-nodes.patch | 216 ++ ...mfd-qcom-tcsr-add-IPQ5018-compatible.patch | 18 + ...arm64-dts-qcom-IPQ5018-add-TCSR-node.patch | 22 + ...018-enable-the-download-mode-support.patch | 19 + ...-bindings-pwm-add-IPQ5018-compatible.patch | 22 + ...nctrl-qcom-IPQ5018-update-pwm-groups.patch | 65 + ...-arm64-dts-qcom-ipq5018-Add-PWM-node.patch | 27 + ...64-dts-qcom-ipq5018-Add-crypto-nodes.patch | 41 + ...arm64-dts-qcom-ipq5018-Add-PRNG-node.patch | 25 + ...dts-qcom-ipq5018-Add-QUP1-UART2-node.patch | 27 + ...4-dts-qcom-ipq5018-Add-QUP3-I2C-node.patch | 32 + ...ndings-Introduce-qcom-spi-qpic-snand.patch | 97 + ...wnand-qcom-cleanup-qcom_nandc-driver.patch | 983 +++++++ ...d-qcom-Add-qcom-prefix-to-common-api.patch | 874 ++++++ ...04-mtd-nand-Add-qpic_common-API-file.patch | 2418 +++++++++++++++++ ...nand-qcom-use-FIELD_PREP-and-GENMASK.patch | 191 ++ ...er-for-QCOM-SPI-NAND-flash-Interface.patch | 1729 ++++++++++++ ...-spi-spi-qpic-fix-compilation-issues.patch | 45 + ...0411-spi-spi-qpic-snand-support-BCH8.patch | 53 + ...and-qpic-only-support-max-4-bytes-ID.patch | 25 + ...4-dts-qcom-ipq5018-Add-SPI-nand-node.patch | 52 + ...pq5018-Add-more-nand-compatible-for-.patch | 23 + ...k-qcom-Add-CMN-PLL-clock-controller-.patch | 113 + ...-PLL-clock-controller-driver-for-IPQ.patch | 288 ++ ...clk-qcom-cmn-pll-add-IPQ5018-support.patch | 78 + ...s-qcom-ipq5018-Add-ethernet-cmn-node.patch | 45 + ...ntroduce-IPQ5018-internal-PHY-driver.patch | 184 ++ ...arm64-dts-qcom-ipq5018-add-mdio-node.patch | 43 + ...m64-dts-qcom-ipq5018-add-ge_phy-node.patch | 45 + ...enable-configuration-of-DAC-settings.patch | 111 + ...add-IPQ5018-initvals-and-CDT-feature.patch | 108 + ...remove-the-unsupported-clk-combinati.patch | 35 + ...-phy-rx-and-tx-clk-providers-by-name.patch | 62 + ...always-enable-SGMII-auto-negotiation.patch | 30 + ...sa-qca8k-support-PHY-to-PHY-CPU-link.patch | 49 + ...c-qcom-Add-support-for-multipd-model.patch | 202 ++ ...m-ipq5332-add-support-to-pass-metada.patch | 43 + ...m-ipq5332-add-msa-lock-unlock-suppor.patch | 126 + ...-q6v5-Add-multipd-interrupts-support.patch | 148 + ...Add-Hexagon-based-multipd-rproc-driv.patch | 901 ++++++ ...mpd-split-q6_wcss-to-rootpd-and-user.patch | 321 +++ ...q6v5_mpd-fix-incorrent-use-of-rproc-.patch | 206 ++ .../0811-firmware-qcom_scm-support-MPD.patch | 128 + ...0812-soc-qcom-mdt_loader-support-MPD.patch | 199 ++ ...moteproc-qcom_q6v5_mpd-enable-clocks.patch | 38 + ...teproc-qcom_q6v5_mpd-support-ipq5018.patch | 111 + ...-add-support-for-passing-v1-bootargs.patch | 163 ++ ...64-dts-qcom-ipq5018-add-wifi-support.patch | 241 ++ ...8-add-tz_apps-reserved-memory-region.patch | 22 + 84 files changed, 13689 insertions(+), 1 deletion(-) create mode 100644 target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-ess.dtsi create mode 100644 target/linux/qualcommax/image/ipq50xx.mk create mode 100644 target/linux/qualcommax/ipq50xx/config-default create mode 100644 target/linux/qualcommax/ipq50xx/target.mk create mode 100644 target/linux/qualcommax/patches-6.6/0065-v6.7-arm64-dts-qcom-ipq5018-add-watchdog.patch create mode 100644 target/linux/qualcommax/patches-6.6/0066-v6.7-dt-bindings-firmware-qcom-scm-support-indicating-SDI-default-state.patch create mode 100644 target/linux/qualcommax/patches-6.6/0067-v6.7-firmware-qcom-scm-disable-SDI-if-required.patch create mode 100644 target/linux/qualcommax/patches-6.6/0068-v6.7-dt-bindings-qcom-scm-document-IPQ5018-compatible.patch create mode 100644 target/linux/qualcommax/patches-6.6/0069-v6.7-arm64-dts-qcom-IPQ5018-indicate-that-SDI-shoud-be-disabled.patch create mode 100644 target/linux/qualcommax/patches-6.6/0070-v6.7-dt-bindings-phy-qcom-m31-Add-IPQ5018-compatible.patch create mode 100644 target/linux/qualcommax/patches-6.6/0071-v6.7-phy-qcom-m31-Add-compatible-phy-init-sequence-for-IPQ5018.patch create mode 100644 target/linux/qualcommax/patches-6.6/0072-v6.7-dt-bindings-usb-dwc3-Add-IPQ5018-compatible.patch create mode 100644 target/linux/qualcommax/patches-6.6/0073-v6.8-arm64-dts-qcom-ipq5018-Add-USB-related-nodes.patch create mode 100644 target/linux/qualcommax/patches-6.6/0074-v6.8-arm64-dts-qcom-ipq5018-add-QUP1-SPI-controller.patch create mode 100644 target/linux/qualcommax/patches-6.6/0075-v6.8-dt-bindings-clock-qcom-a53pll-add-IPQ5018-compatible.patch create mode 100644 target/linux/qualcommax/patches-6.6/0076-v6.8-clk-qcom-apss-ipq-pll-add-support-for-IPQ5018.patch create mode 100644 target/linux/qualcommax/patches-6.6/0077-v6.8-arm64-dts-qcom-ipq5018-enable-the-CPUFreq-support.patch create mode 100644 target/linux/qualcommax/patches-6.6/0078-v6.8-arm64-dts-qcom-ipq5018-add-few-more-reserved-memory-regions.patch create mode 100644 target/linux/qualcommax/patches-6.6/0080-v6.10-clk-qcom-apss-ipq-pll-use-stromer-ops-for-IPQ5018-to-fix-boot-failure.patch create mode 100644 target/linux/qualcommax/patches-6.6/0081-v6.10-clk-qcom-apss-ipq-pll-fix-PLL-rate-for-IPQ5018.patch create mode 100644 target/linux/qualcommax/patches-6.6/0140-dt-bindings-pwm-add-IPQ6018-binding.patch create mode 100644 target/linux/qualcommax/patches-6.6/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch create mode 100644 target/linux/qualcommax/patches-6.6/0142-dt-bindings-mfd-qcom-tcsr-Add-simple-mfd-support-for-IPQ6018.patch create mode 100644 target/linux/qualcommax/patches-6.6/0150-dt-bindings-nvmem-add-IPQ5018-compatible.patch create mode 100644 target/linux/qualcommax/patches-6.6/0151-dt-bindings-thermal-qcom-tsens-Add-ipq5018-compatible.patch create mode 100644 target/linux/qualcommax/patches-6.6/0152-thermal-qcom-add-new-feat-for-soc-without-rpm.patch create mode 100644 target/linux/qualcommax/patches-6.6/0153-thermal-qcom-tsens-add-support-for-IPQ5018-tsens.patch create mode 100644 target/linux/qualcommax/patches-6.6/0154-dts-qcom-IPQ5018-add-tsens-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0155-dt-bindings-phy-qcom-uniphy-pcie-Document-PCIe-uniphy.patch create mode 100644 target/linux/qualcommax/patches-6.6/0156-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch create mode 100644 target/linux/qualcommax/patches-6.6/0157-dt-bindings-phy-qcom-uniphy-pcie-add-IPQ5018-compatible.patch create mode 100644 target/linux/qualcommax/patches-6.6/0158-phy-qcom-uniphy-pcie-28lp-add-support-for-IPQ5018.patch create mode 100644 target/linux/qualcommax/patches-6.6/0160-dt-bindings-PCI-qcom-Add-IPQ5018-SoC.patch create mode 100644 target/linux/qualcommax/patches-6.6/0161-PCI-qcom-add-support-for-IPQ5018.patch create mode 100644 target/linux/qualcommax/patches-6.6/0162-arm64-dts-qcom-IPQ5018-add-PCIe-related-nodes.patch create mode 100644 target/linux/qualcommax/patches-6.6/0301-dt-bindings-mfd-qcom-tcsr-add-IPQ5018-compatible.patch create mode 100644 target/linux/qualcommax/patches-6.6/0302-arm64-dts-qcom-IPQ5018-add-TCSR-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0303-arm64-dts-qcom-IPQ5018-enable-the-download-mode-support.patch create mode 100644 target/linux/qualcommax/patches-6.6/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch create mode 100644 target/linux/qualcommax/patches-6.6/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch create mode 100644 target/linux/qualcommax/patches-6.6/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0324-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch create mode 100644 target/linux/qualcommax/patches-6.6/0337-arm64-dts-qcom-ipq5018-Add-PRNG-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0339-arm64-dts-qcom-ipq5018-Add-QUP1-UART2-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0340-arm64-dts-qcom-ipq5018-Add-QUP3-I2C-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0401-spi-dt-bindings-Introduce-qcom-spi-qpic-snand.patch create mode 100644 target/linux/qualcommax/patches-6.6/0402-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch create mode 100644 target/linux/qualcommax/patches-6.6/0403-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch create mode 100644 target/linux/qualcommax/patches-6.6/0404-mtd-nand-Add-qpic_common-API-file.patch create mode 100644 target/linux/qualcommax/patches-6.6/0405-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch create mode 100644 target/linux/qualcommax/patches-6.6/0406-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Interface.patch create mode 100644 target/linux/qualcommax/patches-6.6/0408-spi-spi-qpic-fix-compilation-issues.patch create mode 100644 target/linux/qualcommax/patches-6.6/0411-spi-spi-qpic-snand-support-BCH8.patch create mode 100644 target/linux/qualcommax/patches-6.6/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch create mode 100644 target/linux/qualcommax/patches-6.6/0421-arm64-dts-qcom-ipq5018-Add-SPI-nand-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0431-arm64-dts-qcom-ipq5018-Add-more-nand-compatible-for-.patch create mode 100644 target/linux/qualcommax/patches-6.6/0701-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller-.patch create mode 100644 target/linux/qualcommax/patches-6.6/0702-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ.patch create mode 100644 target/linux/qualcommax/patches-6.6/0703-clk-qcom-cmn-pll-add-IPQ5018-support.patch create mode 100644 target/linux/qualcommax/patches-6.6/0704-arm64-dts-qcom-ipq5018-Add-ethernet-cmn-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0711-net-phy-qcom-Introduce-IPQ5018-internal-PHY-driver.patch create mode 100644 target/linux/qualcommax/patches-6.6/0712-arm64-dts-qcom-ipq5018-add-mdio-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0713-arm64-dts-qcom-ipq5018-add-ge_phy-node.patch create mode 100644 target/linux/qualcommax/patches-6.6/0714-net-phy-qcom-IPQ5018-enable-configuration-of-DAC-settings.patch create mode 100644 target/linux/qualcommax/patches-6.6/0715-net-phy-qcom-add-IPQ5018-initvals-and-CDT-feature.patch create mode 100644 target/linux/qualcommax/patches-6.6/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch create mode 100644 target/linux/qualcommax/patches-6.6/0722-clk-gcc-ipq5018-refer-to-ge-phy-rx-and-tx-clk-providers-by-name.patch create mode 100644 target/linux/qualcommax/patches-6.6/0751-net-dsa-qca8k-always-enable-SGMII-auto-negotiation.patch create mode 100644 target/linux/qualcommax/patches-6.6/0752-net-dsa-qca8k-support-PHY-to-PHY-CPU-link.patch create mode 100644 target/linux/qualcommax/patches-6.6/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch create mode 100644 target/linux/qualcommax/patches-6.6/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch create mode 100644 target/linux/qualcommax/patches-6.6/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch create mode 100644 target/linux/qualcommax/patches-6.6/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch create mode 100644 target/linux/qualcommax/patches-6.6/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch create mode 100644 target/linux/qualcommax/patches-6.6/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch create mode 100644 target/linux/qualcommax/patches-6.6/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch create mode 100644 target/linux/qualcommax/patches-6.6/0811-firmware-qcom_scm-support-MPD.patch create mode 100644 target/linux/qualcommax/patches-6.6/0812-soc-qcom-mdt_loader-support-MPD.patch create mode 100644 target/linux/qualcommax/patches-6.6/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch create mode 100644 target/linux/qualcommax/patches-6.6/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch create mode 100644 target/linux/qualcommax/patches-6.6/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch create mode 100644 target/linux/qualcommax/patches-6.6/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch create mode 100644 target/linux/qualcommax/patches-6.6/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch diff --git a/target/linux/qualcommax/Makefile b/target/linux/qualcommax/Makefile index 3c9bb840e3..b3b6dc6118 100644 --- a/target/linux/qualcommax/Makefile +++ b/target/linux/qualcommax/Makefile @@ -6,7 +6,7 @@ BOARDNAME:=Qualcomm Atheros 802.11ax WiSoC-s FEATURES:=squashfs ramdisk fpu nand rtc emmc KERNELNAME:=Image CPU_TYPE:=cortex-a53 -SUBTARGETS:=ipq807x ipq60xx +SUBTARGETS:=ipq807x ipq60xx ipq50xx KERNEL_PATCHVER:=6.6 diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-ess.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-ess.dtsi new file mode 100644 index 0000000000..a42b7bbeaa --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-ess.dtsi @@ -0,0 +1,116 @@ +#include + +&soc { + ess_instance: ess-instance { + #address-cells = <1>; + #size-cells = <1>; + num_devices = <1>; + + switch: ess-switch@39c00000 { + compatible = "qcom,ess-switch-ipq50xx"; + device_id = <0>; + cmnblk_clk = "internal_96MHz"; + reg = <0x39c00000 0x200000>; + switch_access_mode = "local bus"; + clocks = <&gcc GCC_CMN_BLK_AHB_CLK>, + <&gcc GCC_CMN_BLK_SYS_CLK>, + <&gcc GCC_UNIPHY_AHB_CLK>, + <&gcc GCC_UNIPHY_SYS_CLK>, + <&gcc GCC_MDIO0_AHB_CLK>, + <&gcc GCC_MDIO1_AHB_CLK>, + <&gcc GCC_GMAC0_CFG_CLK>, + <&gcc GCC_GMAC0_SYS_CLK>, + <&gcc GCC_GMAC1_CFG_CLK>, + <&gcc GCC_GMAC1_SYS_CLK>, + <&gcc GCC_GEPHY_RX_CLK>, + <&gcc GCC_GEPHY_TX_CLK>, + <&gcc GCC_UNIPHY_RX_CLK>, + <&gcc GCC_UNIPHY_TX_CLK>, + <&gcc GCC_GMAC0_RX_CLK>, + <&gcc GCC_GMAC0_TX_CLK>, + <&gcc GCC_GMAC1_RX_CLK>, + <&gcc GCC_GMAC1_TX_CLK>, + <&gcc GCC_SNOC_GMAC0_AHB_CLK>, + <&gcc GCC_SNOC_GMAC1_AHB_CLK>, + <&gcc GCC_GMAC0_PTP_CLK>, + <&gcc GCC_GMAC1_PTP_CLK>; + clock-names = "cmn_ahb_clk", + "cmn_sys_clk", + "uniphy_ahb_clk", + "uniphy_sys_clk", + "gcc_mdio0_ahb_clk", + "gcc_mdio1_ahb_clk", + "gcc_gmac0_cfg_clk", + "gcc_gmac0_sys_clk", + "gcc_gmac1_cfg_clk", + "gcc_gmac1_sys_clk", + "uniphy0_port1_rx_clk", + "uniphy0_port1_tx_clk", + "uniphy1_port5_rx_clk", + "uniphy1_port5_tx_clk", + "nss_port1_rx_clk", + "nss_port1_tx_clk", + "nss_port2_rx_clk", + "nss_port2_tx_clk", + "gcc_snoc_gmac0_ahb_clk", + "gcc_snoc_gmac1_ahb_clk", + "gcc_gmac0_ptp_clk", + "gcc_gmac1_ptp_clk"; + resets = <&gcc GCC_GMAC0_BCR>, + <&gcc GCC_GMAC1_BCR>, + <&gcc GCC_UNIPHY_BCR>, + <&gcc GCC_UNIPHY_SOFT_RESET>; + reset-names = "gmac0_bcr_rst", + "gmac1_bcr_rst", + "uniphy_bcr_rst", + "uniphy1_soft_rst"; + + status = "disabled"; + }; + }; + + ess-uniphy@98000 { + compatible = "qcom,ess-uniphy"; + reg = <0x98000 0x800>; + uniphy_access_mode = "local bus"; + }; + + nss-dp-common { + compatible = "qcom,nss-dp-common"; + qcom,tcsr-base = <0x01937000>; + }; + + dp1: dp1 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <1>; + + reg = <0x39C00000 0x10000>; + interrupts = ; + clocks = <&gcc GCC_SNOC_GMAC0_AXI_CLK>; + clock-names = "nss-snoc-gmac-axi-clk"; + + qcom,mactype = <2>; /* GMAC_HAL_TYPE_SYN_GMAC */ + local-mac-address = [000000000000]; + phy-mode = "internal"; + phy-handle = <&ge_phy>; + + status = "disabled"; + }; + + dp2: dp2 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <2>; + + reg = <0x39D00000 0x10000>; + interrupts = ; + clocks = <&gcc GCC_SNOC_GMAC1_AXI_CLK>; + clock-names = "nss-snoc-gmac-axi-clk"; + + qcom,mactype = <2>; /* GMAC_HAL_TYPE_SYN_GMAC */ + local-mac-address = [000000000000]; + phy-mode = "sgmii"; + status = "disabled"; + }; +}; diff --git a/target/linux/qualcommax/image/ipq50xx.mk b/target/linux/qualcommax/image/ipq50xx.mk new file mode 100644 index 0000000000..e69de29bb2 diff --git a/target/linux/qualcommax/ipq50xx/config-default b/target/linux/qualcommax/ipq50xx/config-default new file mode 100644 index 0000000000..63c1d9dd97 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/config-default @@ -0,0 +1,26 @@ +CONFIG_QCOM_APM=y +CONFIG_IPQ_GCC_5018=y +CONFIG_PINCTRL_IPQ5018=y + +CONFIG_MTD_SPI_NAND=y +CONFIG_SPI_QPIC_SNAND=y + +CONFIG_IPQ_CMN_PLL=y +CONFIG_IPQ5018_PHY=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_QCA8K=y +CONFIG_NET_DSA_TAG_QCA=y +CONFIG_QCA83XX_PHY=y + +CONFIG_QCOM_Q6V5_MPD=y +CONFIG_QCOM_QMI_HELPERS=y + +CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP=y +CONFIG_PCIE_QCOM=y + +CONFIG_PWM=y +CONFIG_PWM_IPQ=y +CONFIG_LEDS_PWM=y + +CONFIG_PHY_QCOM_M31_USB=y +CONFIG_USB_DWC3_QCOM=y diff --git a/target/linux/qualcommax/ipq50xx/target.mk b/target/linux/qualcommax/ipq50xx/target.mk new file mode 100644 index 0000000000..2e68329265 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/target.mk @@ -0,0 +1,7 @@ +SUBTARGET:=ipq50xx +BOARDNAME:=Qualcomm Atheros IPQ50xx +DEFAULT_PACKAGES += ath11k-firmware-ipq5018 + +define Target/Description + Build firmware images for Qualcomm Atheros IPQ50xx based boards. +endef diff --git a/target/linux/qualcommax/patches-6.6/0065-v6.7-arm64-dts-qcom-ipq5018-add-watchdog.patch b/target/linux/qualcommax/patches-6.6/0065-v6.7-arm64-dts-qcom-ipq5018-add-watchdog.patch new file mode 100644 index 0000000000..8e973e63f2 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0065-v6.7-arm64-dts-qcom-ipq5018-add-watchdog.patch @@ -0,0 +1,31 @@ +From 9cbaee8379e620f82112002f973adde19679df31 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Aug 2023 18:14:00 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq5018: add watchdog + +Add the required DT node for watchdog operation. + +Signed-off-by: Robert Marko +Reviewed-by: Konrad Dybcio +Link: https://lore.kernel.org/r/20230816161455.3310629-2-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -181,6 +181,13 @@ + }; + }; + ++ watchdog: watchdog@b017000 { ++ compatible = "qcom,apss-wdt-ipq5018", "qcom,kpss-wdt"; ++ reg = <0x0b017000 0x40>; ++ interrupts = ; ++ clocks = <&sleep_clk>; ++ }; ++ + timer@b120000 { + compatible = "arm,armv7-timer-mem"; + reg = <0x0b120000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.6/0066-v6.7-dt-bindings-firmware-qcom-scm-support-indicating-SDI-default-state.patch b/target/linux/qualcommax/patches-6.6/0066-v6.7-dt-bindings-firmware-qcom-scm-support-indicating-SDI-default-state.patch new file mode 100644 index 0000000000..46d3c61398 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0066-v6.7-dt-bindings-firmware-qcom-scm-support-indicating-SDI-default-state.patch @@ -0,0 +1,41 @@ +From 92dab9ea5f389c12828283146c60054642453a91 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Aug 2023 18:45:38 +0200 +Subject: [PATCH] dt-bindings: firmware: qcom,scm: support indicating SDI + default state + +IPQ5018 has SDI (Secure Debug Image) enabled by TZ by default, and that +means that WDT being asserted or just trying to reboot will hang the board +in the debug mode and only pulling the power and repowering will help. +Some IPQ4019 boards like Google WiFI have it enabled as well. + +So, lets add a boolean property to indicate that SDI is enabled by default +and thus needs to be disabled by the kernel. + +Signed-off-by: Robert Marko +Acked-by: Mukesh Ojha +Reviewed-by: Krzysztof Kozlowski +Reviewed-by: Brian Norris +Link: https://lore.kernel.org/r/20230816164641.3371878-1-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml ++++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +@@ -89,6 +89,14 @@ properties: + protocol to handle sleeping SCM calls. + maxItems: 1 + ++ qcom,sdi-enabled: ++ description: ++ Indicates that the SDI (Secure Debug Image) has been enabled by TZ ++ by default and it needs to be disabled. ++ If not disabled WDT assertion or reboot will cause the board to hang ++ in the debug mode. ++ type: boolean ++ + qcom,dload-mode: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: diff --git a/target/linux/qualcommax/patches-6.6/0067-v6.7-firmware-qcom-scm-disable-SDI-if-required.patch b/target/linux/qualcommax/patches-6.6/0067-v6.7-firmware-qcom-scm-disable-SDI-if-required.patch new file mode 100644 index 0000000000..9df758ae1b --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0067-v6.7-firmware-qcom-scm-disable-SDI-if-required.patch @@ -0,0 +1,83 @@ +From ff4aa3bc98258a240b9bbab53fd8d2fb8184c485 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Aug 2023 18:45:39 +0200 +Subject: [PATCH] firmware: qcom_scm: disable SDI if required + +IPQ5018 has SDI (Secure Debug Image) enabled by TZ by default, and that +means that WDT being asserted or just trying to reboot will hang the board +in the debug mode and only pulling the power and repowering will help. +Some IPQ4019 boards like Google WiFI have it enabled as well. + +Luckily, SDI can be disabled via an SCM call. + +So, lets use the boolean DT property to identify boards that have SDI +enabled by default and use the SCM call to disable SDI during SCM probe. +It is important to disable it as soon as possible as we might have a WDT +assertion at any time which would then leave the board in debug mode, +thus disabling it during SCM removal is not enough. + +Signed-off-by: Robert Marko +Reviewed-by: Guru Das Srinagesh +Link: https://lore.kernel.org/r/20230816164641.3371878-2-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + drivers/firmware/qcom_scm.c | 30 ++++++++++++++++++++++++++++++ + drivers/firmware/qcom_scm.h | 1 + + 2 files changed, 31 insertions(+) + +--- a/drivers/firmware/qcom_scm.c ++++ b/drivers/firmware/qcom_scm.c +@@ -410,6 +410,29 @@ int qcom_scm_set_remote_state(u32 state, + } + EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); + ++static int qcom_scm_disable_sdi(void) ++{ ++ int ret; ++ struct qcom_scm_desc desc = { ++ .svc = QCOM_SCM_SVC_BOOT, ++ .cmd = QCOM_SCM_BOOT_SDI_CONFIG, ++ .args[0] = 1, /* Disable watchdog debug */ ++ .args[1] = 0, /* Disable SDI */ ++ .arginfo = QCOM_SCM_ARGS(2), ++ .owner = ARM_SMCCC_OWNER_SIP, ++ }; ++ struct qcom_scm_res res; ++ ++ ret = qcom_scm_clk_enable(); ++ if (ret) ++ return ret; ++ ret = qcom_scm_call(__scm->dev, &desc, &res); ++ ++ qcom_scm_clk_disable(); ++ ++ return ret ? : res.result[0]; ++} ++ + static int __qcom_scm_set_dload_mode(struct device *dev, bool enable) + { + struct qcom_scm_desc desc = { +@@ -1473,6 +1496,13 @@ static int qcom_scm_probe(struct platfor + + __get_convention(); + ++ ++ /* ++ * Disable SDI if indicated by DT that it is enabled by default. ++ */ ++ if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled")) ++ qcom_scm_disable_sdi(); ++ + /* + * If requested enable "download mode", from this point on warmboot + * will cause the boot stages to enter download mode, unless +--- a/drivers/firmware/qcom_scm.h ++++ b/drivers/firmware/qcom_scm.h +@@ -80,6 +80,7 @@ extern int scm_legacy_call(struct device + #define QCOM_SCM_SVC_BOOT 0x01 + #define QCOM_SCM_BOOT_SET_ADDR 0x01 + #define QCOM_SCM_BOOT_TERMINATE_PC 0x02 ++#define QCOM_SCM_BOOT_SDI_CONFIG 0x09 + #define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10 + #define QCOM_SCM_BOOT_SET_ADDR_MC 0x11 + #define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a diff --git a/target/linux/qualcommax/patches-6.6/0068-v6.7-dt-bindings-qcom-scm-document-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0068-v6.7-dt-bindings-qcom-scm-document-IPQ5018-compatible.patch new file mode 100644 index 0000000000..d36ccddd27 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0068-v6.7-dt-bindings-qcom-scm-document-IPQ5018-compatible.patch @@ -0,0 +1,25 @@ +From f6aa7386bc40b552eea8ec1b1d2168afe3b31110 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Aug 2023 18:45:40 +0200 +Subject: [PATCH] dt-bindings: firmware: qcom,scm: document IPQ5018 compatible + +It seems that IPQ5018 compatible was never documented in the bindings. + +Signed-off-by: Robert Marko +Reviewed-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20230816164641.3371878-3-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 1 + + 1 file changed, 1 insertion(+) + +--- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml ++++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +@@ -24,6 +24,7 @@ properties: + - qcom,scm-apq8064 + - qcom,scm-apq8084 + - qcom,scm-ipq4019 ++ - qcom,scm-ipq5018 + - qcom,scm-ipq5332 + - qcom,scm-ipq6018 + - qcom,scm-ipq806x diff --git a/target/linux/qualcommax/patches-6.6/0069-v6.7-arm64-dts-qcom-IPQ5018-indicate-that-SDI-shoud-be-disabled.patch b/target/linux/qualcommax/patches-6.6/0069-v6.7-arm64-dts-qcom-IPQ5018-indicate-that-SDI-shoud-be-disabled.patch new file mode 100644 index 0000000000..6d598bf50f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0069-v6.7-arm64-dts-qcom-IPQ5018-indicate-that-SDI-shoud-be-disabled.patch @@ -0,0 +1,26 @@ +From 79796e87215db9587d6c66ec6f6781e091bc6464 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 16 Aug 2023 18:45:41 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq5018: indicate that SDI should be + disabled + +Now that SCM has support for indicating that SDI has been enabled by +default, lets set the property so SCM disables it during probing. + +Signed-off-by: Robert Marko +Link: https://lore.kernel.org/r/20230816164641.3371878-4-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -57,6 +57,7 @@ + firmware { + scm { + compatible = "qcom,scm-ipq5018", "qcom,scm"; ++ qcom,sdi-enabled; + }; + }; + diff --git a/target/linux/qualcommax/patches-6.6/0070-v6.7-dt-bindings-phy-qcom-m31-Add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0070-v6.7-dt-bindings-phy-qcom-m31-Add-IPQ5018-compatible.patch new file mode 100644 index 0000000000..777212249e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0070-v6.7-dt-bindings-phy-qcom-m31-Add-IPQ5018-compatible.patch @@ -0,0 +1,28 @@ +From 1852dfaacd3f4358bbfca134b63a02bbb30c1136 Mon Sep 17 00:00:00 2001 +From: Nitheesh Sekar +Date: Mon, 4 Sep 2023 12:06:32 +0530 +Subject: [PATCH] dt-bindings: phy: qcom,m31: Add IPQ5018 compatible + +IPQ5332 qcom,m31 phy driver can support IPQ5018. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Nitheesh Sekar +Link: https://lore.kernel.org/r/20230904063635.24975-2-quic_nsekar@quicinc.com +Signed-off-by: Vinod Koul +--- + .../devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml ++++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml +@@ -17,7 +17,9 @@ description: + properties: + compatible: + items: +- - const: qcom,ipq5332-usb-hsphy ++ - enum: ++ - qcom,ipq5018-usb-hsphy ++ - qcom,ipq5332-usb-hsphy + + "#phy-cells": + const: 0 diff --git a/target/linux/qualcommax/patches-6.6/0071-v6.7-phy-qcom-m31-Add-compatible-phy-init-sequence-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0071-v6.7-phy-qcom-m31-Add-compatible-phy-init-sequence-for-IPQ5018.patch new file mode 100644 index 0000000000..248b47c694 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0071-v6.7-phy-qcom-m31-Add-compatible-phy-init-sequence-for-IPQ5018.patch @@ -0,0 +1,89 @@ +From 68320e35f8cb1987b4ad34347fc7033832da99e3 Mon Sep 17 00:00:00 2001 +From: Nitheesh Sekar +Date: Mon, 4 Sep 2023 12:06:33 +0530 +Subject: [PATCH] phy: qcom-m31: Add compatible, phy init sequence for IPQ5018 + +Add phy init sequence and compatible string for IPQ5018 +chipset. + +Signed-off-by: Nitheesh Sekar +Link: https://lore.kernel.org/r/20230904063635.24975-3-quic_nsekar@quicinc.com +Signed-off-by: Vinod Koul +--- + drivers/phy/qualcomm/phy-qcom-m31.c | 51 +++++++++++++++++++++++++++++ + 1 file changed, 51 insertions(+) + +--- a/drivers/phy/qualcomm/phy-qcom-m31.c ++++ b/drivers/phy/qualcomm/phy-qcom-m31.c +@@ -82,6 +82,50 @@ struct m31_priv_data { + unsigned int nregs; + }; + ++static const struct m31_phy_regs m31_ipq5018_regs[] = { ++ { ++ .off = USB_PHY_CFG0, ++ .val = UTMI_PHY_OVERRIDE_EN ++ }, ++ { ++ .off = USB_PHY_UTMI_CTRL5, ++ .val = POR_EN, ++ .delay = 15 ++ }, ++ { ++ .off = USB_PHY_FSEL_SEL, ++ .val = FREQ_SEL ++ }, ++ { ++ .off = USB_PHY_HS_PHY_CTRL_COMMON0, ++ .val = COMMONONN | FSEL | RETENABLEN ++ }, ++ { ++ .off = USB_PHY_REFCLK_CTRL, ++ .val = CLKCORE ++ }, ++ { ++ .off = USB_PHY_UTMI_CTRL5, ++ .val = POR_EN ++ }, ++ { ++ .off = USB_PHY_HS_PHY_CTRL2, ++ .val = USB2_SUSPEND_N_SEL | USB2_SUSPEND_N | USB2_UTMI_CLK_EN ++ }, ++ { ++ .off = USB_PHY_UTMI_CTRL5, ++ .val = 0x0 ++ }, ++ { ++ .off = USB_PHY_HS_PHY_CTRL2, ++ .val = USB2_SUSPEND_N | USB2_UTMI_CLK_EN ++ }, ++ { ++ .off = USB_PHY_CFG0, ++ .val = 0x0 ++ }, ++}; ++ + static struct m31_phy_regs m31_ipq5332_regs[] = { + { + USB_PHY_CFG0, +@@ -267,6 +311,12 @@ static int m31usb_phy_probe(struct platf + return PTR_ERR_OR_ZERO(phy_provider); + } + ++static const struct m31_priv_data m31_ipq5018_data = { ++ .ulpi_mode = false, ++ .regs = m31_ipq5018_regs, ++ .nregs = ARRAY_SIZE(m31_ipq5018_regs), ++}; ++ + static const struct m31_priv_data m31_ipq5332_data = { + .ulpi_mode = false, + .regs = m31_ipq5332_regs, +@@ -274,6 +324,7 @@ static const struct m31_priv_data m31_ip + }; + + static const struct of_device_id m31usb_phy_id_table[] = { ++ { .compatible = "qcom,ipq5018-usb-hsphy", .data = &m31_ipq5018_data }, + { .compatible = "qcom,ipq5332-usb-hsphy", .data = &m31_ipq5332_data }, + { }, + }; diff --git a/target/linux/qualcommax/patches-6.6/0072-v6.7-dt-bindings-usb-dwc3-Add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0072-v6.7-dt-bindings-usb-dwc3-Add-IPQ5018-compatible.patch new file mode 100644 index 0000000000..146fcbff09 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0072-v6.7-dt-bindings-usb-dwc3-Add-IPQ5018-compatible.patch @@ -0,0 +1,41 @@ +From 3865a64284cc4845c61cf3dc6c7246349d80cc49 Mon Sep 17 00:00:00 2001 +From: Nitheesh Sekar +Date: Thu, 31 Aug 2023 08:35:03 +0530 +Subject: [PATCH] dt-bindings: usb: dwc3: Add IPQ5018 compatible + +Document the IPQ5018 dwc3 compatible. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Nitheesh Sekar +Link: https://lore.kernel.org/r/20230831030503.17100-1-quic_nsekar@quicinc.com +Signed-off-by: Greg Kroah-Hartman +--- + Documentation/devicetree/bindings/usb/qcom,dwc3.yaml | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml ++++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +@@ -14,6 +14,7 @@ properties: + items: + - enum: + - qcom,ipq4019-dwc3 ++ - qcom,ipq5018-dwc3 + - qcom,ipq5332-dwc3 + - qcom,ipq6018-dwc3 + - qcom,ipq8064-dwc3 +@@ -238,6 +239,7 @@ allOf: + compatible: + contains: + enum: ++ - qcom,ipq5018-dwc3 + - qcom,ipq5332-dwc3 + - qcom,msm8994-dwc3 + - qcom,qcs404-dwc3 +@@ -411,6 +413,7 @@ allOf: + compatible: + contains: + enum: ++ - qcom,ipq5018-dwc3 + - qcom,ipq5332-dwc3 + - qcom,sdm660-dwc3 + then: diff --git a/target/linux/qualcommax/patches-6.6/0073-v6.8-arm64-dts-qcom-ipq5018-Add-USB-related-nodes.patch b/target/linux/qualcommax/patches-6.6/0073-v6.8-arm64-dts-qcom-ipq5018-Add-USB-related-nodes.patch new file mode 100644 index 0000000000..99fdae732a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0073-v6.8-arm64-dts-qcom-ipq5018-Add-USB-related-nodes.patch @@ -0,0 +1,86 @@ +From e7166f2774aafefd29ff26ffbbb7f6d40ac8ea1c Mon Sep 17 00:00:00 2001 +From: Nitheesh Sekar +Date: Mon, 4 Sep 2023 12:06:34 +0530 +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add USB related nodes + +Add USB phy and controller nodes. + +Co-developed-by: Amandeep Singh +Signed-off-by: Amandeep Singh +Signed-off-by: Nitheesh Sekar +Link: https://lore.kernel.org/r/20230904063635.24975-4-quic_nsekar@quicinc.com +Signed-off-by: Bjorn Andersson +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 54 +++++++++++++++++++++++++++ + 1 file changed, 54 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -94,6 +94,19 @@ + #size-cells = <1>; + ranges = <0 0 0 0xffffffff>; + ++ usbphy0: phy@5b000 { ++ compatible = "qcom,ipq5018-usb-hsphy"; ++ reg = <0x0005b000 0x120>; ++ ++ clocks = <&gcc GCC_USB0_PHY_CFG_AHB_CLK>; ++ ++ resets = <&gcc GCC_QUSB2_0_PHY_BCR>; ++ ++ #phy-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ + tlmm: pinctrl@1000000 { + compatible = "qcom,ipq5018-tlmm"; + reg = <0x01000000 0x300000>; +@@ -156,6 +169,47 @@ + status = "disabled"; + }; + ++ usb: usb@8af8800 { ++ compatible = "qcom,ipq5018-dwc3", "qcom,dwc3"; ++ reg = <0x08af8800 0x400>; ++ ++ interrupts = ; ++ interrupt-names = "hs_phy_irq"; ++ ++ clocks = <&gcc GCC_USB0_MASTER_CLK>, ++ <&gcc GCC_SYS_NOC_USB0_AXI_CLK>, ++ <&gcc GCC_USB0_SLEEP_CLK>, ++ <&gcc GCC_USB0_MOCK_UTMI_CLK>; ++ clock-names = "core", ++ "iface", ++ "sleep", ++ "mock_utmi"; ++ ++ resets = <&gcc GCC_USB0_BCR>; ++ ++ qcom,select-utmi-as-pipe-clk; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ status = "disabled"; ++ ++ usb_dwc: usb@8a00000 { ++ compatible = "snps,dwc3"; ++ reg = <0x08a00000 0xe000>; ++ clocks = <&gcc GCC_USB0_MOCK_UTMI_CLK>; ++ clock-names = "ref"; ++ interrupts = ; ++ phy-names = "usb2-phy"; ++ phys = <&usbphy0>; ++ tx-fifo-resize; ++ snps,is-utmi-l1-suspend; ++ snps,hird-threshold = /bits/ 8 <0x0>; ++ snps,dis_u2_susphy_quirk; ++ snps,dis_u3_susphy_quirk; ++ }; ++ }; ++ + intc: interrupt-controller@b000000 { + compatible = "qcom,msm-qgic2"; + reg = <0x0b000000 0x1000>, /* GICD */ diff --git a/target/linux/qualcommax/patches-6.6/0074-v6.8-arm64-dts-qcom-ipq5018-add-QUP1-SPI-controller.patch b/target/linux/qualcommax/patches-6.6/0074-v6.8-arm64-dts-qcom-ipq5018-add-QUP1-SPI-controller.patch new file mode 100644 index 0000000000..d0d9e45dec --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0074-v6.8-arm64-dts-qcom-ipq5018-add-QUP1-SPI-controller.patch @@ -0,0 +1,56 @@ +From a1f42e08f0f04b72a6597f080db4bfbb3737910c Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 4 Oct 2023 21:12:30 +0200 +Subject: [PATCH] arm64: dts: qcom: ipq5018: add QUP1 SPI controller + +Add the required BAM and QUP nodes for the QUP1 SPI controller on IPQ5018. + +Signed-off-by: Robert Marko +Reviewed-by: Kathiravan Thirumoorthy +Link: https://lore.kernel.org/r/20231004191303.331055-1-robimarko@gmail.com +[bjorn: Padded address to 8 digits, fixed node sort order] +Signed-off-by: Bjorn Andersson +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -159,6 +159,16 @@ + status = "disabled"; + }; + ++ blsp_dma: dma-controller@7884000 { ++ compatible = "qcom,bam-v1.7.0"; ++ reg = <0x07884000 0x1d000>; ++ interrupts = ; ++ clocks = <&gcc GCC_BLSP1_AHB_CLK>; ++ clock-names = "bam_clk"; ++ #dma-cells = <1>; ++ qcom,ee = <0>; ++ }; ++ + blsp1_uart1: serial@78af000 { + compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; + reg = <0x078af000 0x200>; +@@ -169,6 +179,20 @@ + status = "disabled"; + }; + ++ blsp1_spi1: spi@78b5000 { ++ compatible = "qcom,spi-qup-v2.2.1"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x078b5000 0x600>; ++ interrupts = ; ++ clocks = <&gcc GCC_BLSP1_QUP1_SPI_APPS_CLK>, ++ <&gcc GCC_BLSP1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ dmas = <&blsp_dma 4>, <&blsp_dma 5>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ + usb: usb@8af8800 { + compatible = "qcom,ipq5018-dwc3", "qcom,dwc3"; + reg = <0x08af8800 0x400>; diff --git a/target/linux/qualcommax/patches-6.6/0075-v6.8-dt-bindings-clock-qcom-a53pll-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0075-v6.8-dt-bindings-clock-qcom-a53pll-add-IPQ5018-compatible.patch new file mode 100644 index 0000000000..314afa76d2 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0075-v6.8-dt-bindings-clock-qcom-a53pll-add-IPQ5018-compatible.patch @@ -0,0 +1,25 @@ +From 4d45d56e17348c6b6bb2bce126a4a5ea97b19900 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Mon, 25 Sep 2023 15:58:24 +0530 +Subject: [PATCH] dt-bindings: clock: qcom,a53pll: add IPQ5018 compatible + +Add IPQ5018 compatible to A53 PLL bindings. + +Signed-off-by: Gokul Sriram Palanisamy +Reviewed-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20230925102826.405446-2-quic_gokulsri@quicinc.com +Signed-off-by: Bjorn Andersson +--- + Documentation/devicetree/bindings/clock/qcom,a53pll.yaml | 1 + + 1 file changed, 1 insertion(+) + +--- a/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml ++++ b/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml +@@ -16,6 +16,7 @@ description: + properties: + compatible: + enum: ++ - qcom,ipq5018-a53pll + - qcom,ipq5332-a53pll + - qcom,ipq6018-a53pll + - qcom,ipq8074-a53pll diff --git a/target/linux/qualcommax/patches-6.6/0076-v6.8-clk-qcom-apss-ipq-pll-add-support-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0076-v6.8-clk-qcom-apss-ipq-pll-add-support-for-IPQ5018.patch new file mode 100644 index 0000000000..46dede07ca --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0076-v6.8-clk-qcom-apss-ipq-pll-add-support-for-IPQ5018.patch @@ -0,0 +1,62 @@ +From 50492f929486c044b43cb3e2c0e040aa9b61ea2b Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Mon, 25 Sep 2023 15:58:25 +0530 +Subject: [PATCH] clk: qcom: apss-ipq-pll: add support for IPQ5018 + +IPQ5018 APSS PLL is of type Stromer. Reuse Stromer Plus PLL offsets, +add configuration values and the compatible. + +Co-developed-by: Sricharan Ramabadhran +Signed-off-by: Sricharan Ramabadhran +Signed-off-by: Gokul Sriram Palanisamy +Reviewed-by: Dmitry Baryshkov +Link: https://lore.kernel.org/r/20230925102826.405446-3-quic_gokulsri@quicinc.com +Signed-off-by: Bjorn Andersson +--- + drivers/clk/qcom/apss-ipq-pll.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +--- a/drivers/clk/qcom/apss-ipq-pll.c ++++ b/drivers/clk/qcom/apss-ipq-pll.c +@@ -73,6 +73,20 @@ static struct clk_alpha_pll ipq_pll_stro + }, + }; + ++static const struct alpha_pll_config ipq5018_pll_config = { ++ .l = 0x32, ++ .config_ctl_val = 0x4001075b, ++ .config_ctl_hi_val = 0x304, ++ .main_output_mask = BIT(0), ++ .aux_output_mask = BIT(1), ++ .early_output_mask = BIT(3), ++ .alpha_en_mask = BIT(24), ++ .status_val = 0x3, ++ .status_mask = GENMASK(10, 8), ++ .lock_det = BIT(2), ++ .test_ctl_hi_val = 0x00400003, ++}; ++ + static const struct alpha_pll_config ipq5332_pll_config = { + .l = 0x2d, + .config_ctl_val = 0x4001075b, +@@ -129,6 +143,12 @@ struct apss_pll_data { + const struct alpha_pll_config *pll_config; + }; + ++static const struct apss_pll_data ipq5018_pll_data = { ++ .pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS, ++ .pll = &ipq_pll_stromer_plus, ++ .pll_config = &ipq5018_pll_config, ++}; ++ + static struct apss_pll_data ipq5332_pll_data = { + .pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS, + .pll = &ipq_pll_stromer_plus, +@@ -195,6 +215,7 @@ static int apss_ipq_pll_probe(struct pla + } + + static const struct of_device_id apss_ipq_pll_match_table[] = { ++ { .compatible = "qcom,ipq5018-a53pll", .data = &ipq5018_pll_data }, + { .compatible = "qcom,ipq5332-a53pll", .data = &ipq5332_pll_data }, + { .compatible = "qcom,ipq6018-a53pll", .data = &ipq6018_pll_data }, + { .compatible = "qcom,ipq8074-a53pll", .data = &ipq8074_pll_data }, diff --git a/target/linux/qualcommax/patches-6.6/0077-v6.8-arm64-dts-qcom-ipq5018-enable-the-CPUFreq-support.patch b/target/linux/qualcommax/patches-6.6/0077-v6.8-arm64-dts-qcom-ipq5018-enable-the-CPUFreq-support.patch new file mode 100644 index 0000000000..72bec54cb3 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0077-v6.8-arm64-dts-qcom-ipq5018-enable-the-CPUFreq-support.patch @@ -0,0 +1,98 @@ +From 3e4b53e04281ed3d9c7a4329c027097265c04d54 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +Date: Mon, 25 Sep 2023 15:58:26 +0530 +Subject: [PATCH] arm64: dts: qcom: ipq5018: enable the CPUFreq support + +Add the APCS, A53 PLL, cpu-opp-table nodes to set +the CPU frequency at 800MHz (idle) or 1.008GHz. + +Co-developed-by: Sricharan Ramabadhran +Signed-off-by: Sricharan Ramabadhran +Signed-off-by: Gokul Sriram Palanisamy +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20230925102826.405446-4-quic_gokulsri@quicinc.com +Signed-off-by: Bjorn Andersson +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 40 +++++++++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -5,6 +5,7 @@ + * Copyright (c) 2023 The Linux Foundation. All rights reserved. + */ + ++#include + #include + #include + #include +@@ -36,6 +37,8 @@ + reg = <0x0>; + enable-method = "psci"; + next-level-cache = <&L2_0>; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ operating-points-v2 = <&cpu_opp_table>; + }; + + CPU1: cpu@1 { +@@ -44,6 +47,8 @@ + reg = <0x1>; + enable-method = "psci"; + next-level-cache = <&L2_0>; ++ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>; ++ operating-points-v2 = <&cpu_opp_table>; + }; + + L2_0: l2-cache { +@@ -54,6 +59,25 @@ + }; + }; + ++ cpu_opp_table: opp-table-cpu { ++ compatible = "operating-points-v2"; ++ opp-shared; ++ ++ /* ++ opp-800000000 { ++ opp-hz = /bits/ 64 <800000000>; ++ opp-microvolt = <1100000>; ++ clock-latency-ns = <200000>; ++ }; ++ */ ++ ++ opp-1008000000 { ++ opp-hz = /bits/ 64 <1008000000>; ++ opp-microvolt = <1100000>; ++ clock-latency-ns = <200000>; ++ }; ++ }; ++ + firmware { + scm { + compatible = "qcom,scm-ipq5018", "qcom,scm"; +@@ -267,6 +291,24 @@ + clocks = <&sleep_clk>; + }; + ++ apcs_glb: mailbox@b111000 { ++ compatible = "qcom,ipq5018-apcs-apps-global", ++ "qcom,ipq6018-apcs-apps-global"; ++ reg = <0x0b111000 0x1000>; ++ #clock-cells = <1>; ++ clocks = <&a53pll>, <&xo_board_clk>, <&gcc GPLL0>; ++ clock-names = "pll", "xo", "gpll0"; ++ #mbox-cells = <1>; ++ }; ++ ++ a53pll: clock@b116000 { ++ compatible = "qcom,ipq5018-a53pll"; ++ reg = <0x0b116000 0x40>; ++ #clock-cells = <0>; ++ clocks = <&xo_board_clk>; ++ clock-names = "xo"; ++ }; ++ + timer@b120000 { + compatible = "arm,armv7-timer-mem"; + reg = <0x0b120000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.6/0078-v6.8-arm64-dts-qcom-ipq5018-add-few-more-reserved-memory-regions.patch b/target/linux/qualcommax/patches-6.6/0078-v6.8-arm64-dts-qcom-ipq5018-add-few-more-reserved-memory-regions.patch new file mode 100644 index 0000000000..97631e715a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0078-v6.8-arm64-dts-qcom-ipq5018-add-few-more-reserved-memory-regions.patch @@ -0,0 +1,66 @@ +From a427dd16e61f3d145bc24f0ed09692fc25931250 Mon Sep 17 00:00:00 2001 +From: Kathiravan Thirumoorthy +Date: Wed, 25 Oct 2023 22:12:12 +0530 +Subject: [PATCH] arm64: dts: qcom: ipq5018: add few more reserved memory + regions + +Like all other IPQ SoCs, bootloader will collect the system RAM contents +upon crash for the post morterm analysis. If we don't reserve the memory +region used by bootloader, obviously linux will consume it and upon next +boot on crash, bootloader will be loaded in the same region, which will +lead to loose some of the data, sometimes we may miss out critical +information. So lets reserve the region used by the bootloader. + +Similarly SBL copies some data into the reserved region and it will be +used in the crash scenario. So reserve 1MB for SBL as well. + +While at it, enable the SMEM support along with TCSR mutex. + +Signed-off-by: Kathiravan Thirumoorthy +Reviewed-by: Konrad Dybcio +Link: https://lore.kernel.org/r/20231025-ipq5018-misc-v1-1-7d14fde97fe7@quicinc.com +Signed-off-by: Bjorn Andersson +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -106,6 +106,24 @@ + #size-cells = <2>; + ranges; + ++ bootloader@4a800000 { ++ reg = <0x0 0x4a800000 0x0 0x200000>; ++ no-map; ++ }; ++ ++ sbl@4aa00000 { ++ reg = <0x0 0x4aa00000 0x0 0x100000>; ++ no-map; ++ }; ++ ++ smem@4ab00000 { ++ compatible = "qcom,smem"; ++ reg = <0x0 0x4ab00000 0x0 0x100000>; ++ no-map; ++ ++ hwlocks = <&tcsr_mutex 3>; ++ }; ++ + tz_region: tz@4ac00000 { + reg = <0x0 0x4ac00000 0x0 0x200000>; + no-map; +@@ -166,6 +184,12 @@ + #power-domain-cells = <1>; + }; + ++ tcsr_mutex: hwlock@1905000 { ++ compatible = "qcom,tcsr-mutex"; ++ reg = <0x01905000 0x20000>; ++ #hwlock-cells = <1>; ++ }; ++ + sdhc_1: mmc@7804000 { + compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5"; + reg = <0x7804000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.6/0080-v6.10-clk-qcom-apss-ipq-pll-use-stromer-ops-for-IPQ5018-to-fix-boot-failure.patch b/target/linux/qualcommax/patches-6.6/0080-v6.10-clk-qcom-apss-ipq-pll-use-stromer-ops-for-IPQ5018-to-fix-boot-failure.patch new file mode 100644 index 0000000000..ea6f61b57f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0080-v6.10-clk-qcom-apss-ipq-pll-use-stromer-ops-for-IPQ5018-to-fix-boot-failure.patch @@ -0,0 +1,83 @@ +From: Gabor Juhos +Subject: [PATCH] clk: qcom: apss-ipq-pll: use stromer ops for IPQ5018 to fix boot failure +Date: Fri, 15 Mar 2024 17:16:41 +0100 + +Booting v6.8 results in a hang on various IPQ5018 based boards. +Investigating the problem showed that the hang happens when the +clk_alpha_pll_stromer_plus_set_rate() function tries to write +into the PLL_MODE register of the APSS PLL. + +Checking the downstream code revealed that it uses [1] stromer +specific operations for IPQ5018, whereas in the current code +the stromer plus specific operations are used. + +The ops in the 'ipq_pll_stromer_plus' clock definition can't be +changed since that is needed for IPQ5332, so add a new alpha pll +clock declaration which uses the correct stromer ops and use this +new clock for IPQ5018 to avoid the boot failure. + +Also, change pll_type in 'ipq5018_pll_data' to +CLK_ALPHA_PLL_TYPE_STROMER to better reflect that it is a Stromer +PLL and change the apss_ipq_pll_probe() function accordingly. + +1. https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/blob/NHSS.QSDK.12.4/drivers/clk/qcom/apss-ipq5018.c#L67 + +Fixes: 50492f929486 ("clk: qcom: apss-ipq-pll: add support for IPQ5018") +Signed-off-by: Gabor Juhos +--- + drivers/clk/qcom/apss-ipq-pll.c | 30 +++++++++++++++++++++++++++--- + 1 file changed, 27 insertions(+), 3 deletions(-) + +--- a/drivers/clk/qcom/apss-ipq-pll.c ++++ b/drivers/clk/qcom/apss-ipq-pll.c +@@ -55,6 +55,29 @@ static struct clk_alpha_pll ipq_pll_huay + }, + }; + ++static struct clk_alpha_pll ipq_pll_stromer = { ++ .offset = 0x0, ++ /* ++ * Reuse CLK_ALPHA_PLL_TYPE_STROMER_PLUS register offsets. ++ * Although this is a bit confusing, but the offset values ++ * are correct nevertheless. ++ */ ++ .regs = ipq_pll_offsets[CLK_ALPHA_PLL_TYPE_STROMER_PLUS], ++ .flags = SUPPORTS_DYNAMIC_UPDATE, ++ .clkr = { ++ .enable_reg = 0x0, ++ .enable_mask = BIT(0), ++ .hw.init = &(const struct clk_init_data) { ++ .name = "a53pll", ++ .parent_data = &(const struct clk_parent_data) { ++ .fw_name = "xo", ++ }, ++ .num_parents = 1, ++ .ops = &clk_alpha_pll_stromer_ops, ++ }, ++ }, ++}; ++ + static struct clk_alpha_pll ipq_pll_stromer_plus = { + .offset = 0x0, + .regs = ipq_pll_offsets[CLK_ALPHA_PLL_TYPE_STROMER_PLUS], +@@ -144,8 +167,8 @@ struct apss_pll_data { + }; + + static const struct apss_pll_data ipq5018_pll_data = { +- .pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS, +- .pll = &ipq_pll_stromer_plus, ++ .pll_type = CLK_ALPHA_PLL_TYPE_STROMER, ++ .pll = &ipq_pll_stromer, + .pll_config = &ipq5018_pll_config, + }; + +@@ -203,7 +226,8 @@ static int apss_ipq_pll_probe(struct pla + + if (data->pll_type == CLK_ALPHA_PLL_TYPE_HUAYRA) + clk_alpha_pll_configure(data->pll, regmap, data->pll_config); +- else if (data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER_PLUS) ++ else if (data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER || ++ data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER_PLUS) + clk_stromer_pll_configure(data->pll, regmap, data->pll_config); + + ret = devm_clk_register_regmap(dev, &data->pll->clkr); diff --git a/target/linux/qualcommax/patches-6.6/0081-v6.10-clk-qcom-apss-ipq-pll-fix-PLL-rate-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0081-v6.10-clk-qcom-apss-ipq-pll-fix-PLL-rate-for-IPQ5018.patch new file mode 100644 index 0000000000..4bf0598c36 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0081-v6.10-clk-qcom-apss-ipq-pll-fix-PLL-rate-for-IPQ5018.patch @@ -0,0 +1,32 @@ +From: Gabor Juhos +Subject: [PATCH] clk: qcom: apss-ipq-pll: fix PLL rate for IPQ5018 +Date: Tue, 26 Mar 2024 14:34:11 +0100 + +According to ipq5018.dtsi, the maximum supported rate by the +CPU is 1.008 GHz on the IPQ5018 platform, however the current +configuration of the PLL results in 1.2 GHz rate. + +Change the 'L' value in the PLL configuration to limit the +rate to 1.008 GHz. The downstream kernel also uses the same +value [1]. Also add a comment to indicate the desired +frequency. + +[1] https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/blob/NHSS.QSDK.12.4/drivers/clk/qcom/apss-ipq5018.c?ref_type=heads#L151 + +Fixes: 50492f929486 ("clk: qcom: apss-ipq-pll: add support for IPQ5018") +Signed-off-by: Gabor Juhos +--- + drivers/clk/qcom/apss-ipq-pll.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/clk/qcom/apss-ipq-pll.c ++++ b/drivers/clk/qcom/apss-ipq-pll.c +@@ -97,7 +97,7 @@ static struct clk_alpha_pll ipq_pll_stro + }; + + static const struct alpha_pll_config ipq5018_pll_config = { +- .l = 0x32, ++ .l = 0x2a, + .config_ctl_val = 0x4001075b, + .config_ctl_hi_val = 0x304, + .main_output_mask = BIT(0), diff --git a/target/linux/qualcommax/patches-6.6/0140-dt-bindings-pwm-add-IPQ6018-binding.patch b/target/linux/qualcommax/patches-6.6/0140-dt-bindings-pwm-add-IPQ6018-binding.patch new file mode 100644 index 0000000000..bba8b3457a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0140-dt-bindings-pwm-add-IPQ6018-binding.patch @@ -0,0 +1,60 @@ +From: Devi Priya +Date: Thu, 5 Oct 2023 21:35:48 +0530 +Subject: [PATCH] dt-bindings: pwm: add IPQ6018 binding + +DT binding for the PWM block in Qualcomm IPQ6018 SoC. + +Reviewed-by: Bjorn Andersson +Reviewed-by: Krzysztof Kozlowski +Co-developed-by: Baruch Siach +Signed-off-by: Baruch Siach +Signed-off-by: Devi Priya +--- +--- /dev/null ++++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml +@@ -0,0 +1,45 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/pwm/qcom,ipq6018-pwm.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Qualcomm IPQ6018 PWM controller ++ ++maintainers: ++ - Baruch Siach ++ ++properties: ++ compatible: ++ const: qcom,ipq6018-pwm ++ ++ reg: ++ description: Offset of PWM register in the TCSR block. ++ maxItems: 1 ++ ++ clocks: ++ maxItems: 1 ++ ++ "#pwm-cells": ++ const: 2 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - "#pwm-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ ++ pwm: pwm@a010 { ++ compatible = "qcom,ipq6018-pwm"; ++ reg = <0xa010 0x20>; ++ clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clock-rates = <100000000>; ++ #pwm-cells = <2>; ++ }; diff --git a/target/linux/qualcommax/patches-6.6/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch b/target/linux/qualcommax/patches-6.6/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch new file mode 100644 index 0000000000..f4f396a2c1 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch @@ -0,0 +1,330 @@ +From: Devi Priya +Date: Thu, 5 Oct 2023 21:35:47 +0530 +Subject: [PATCH] pwm: driver for qualcomm ipq6018 pwm block + +Driver for the PWM block in Qualcomm IPQ6018 line of SoCs. Based on +driver from downstream Codeaurora kernel tree. Removed support for older +(V1) variants because I have no access to that hardware. + +Tested on IPQ6010 based hardware. + +Co-developed-by: Baruch Siach +Signed-off-by: Baruch Siach +Signed-off-by: Devi Priya +--- +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -282,6 +282,18 @@ config PWM_INTEL_LGM + To compile this driver as a module, choose M here: the module + will be called pwm-intel-lgm. + ++config PWM_IPQ ++ tristate "IPQ PWM support" ++ depends on ARCH_QCOM || COMPILE_TEST ++ depends on HAVE_CLK && HAS_IOMEM ++ help ++ Generic PWM framework driver for IPQ PWM block which supports ++ 4 pwm channels. Each of the these channels can be configured ++ independent of each other. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-ipq. ++ + config PWM_IQS620A + tristate "Azoteq IQS620A PWM support" + depends on MFD_IQS62X || COMPILE_TEST +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -24,6 +24,7 @@ obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o + obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o + obj-$(CONFIG_PWM_IMX_TPM) += pwm-imx-tpm.o + obj-$(CONFIG_PWM_INTEL_LGM) += pwm-intel-lgm.o ++obj-$(CONFIG_PWM_IPQ) += pwm-ipq.o + obj-$(CONFIG_PWM_IQS620A) += pwm-iqs620a.o + obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o + obj-$(CONFIG_PWM_KEEMBAY) += pwm-keembay.o +--- /dev/null ++++ b/drivers/pwm/pwm-ipq.c +@@ -0,0 +1,282 @@ ++// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 ++/* ++ * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* The frequency range supported is 1 Hz to clock rate */ ++#define IPQ_PWM_MAX_PERIOD_NS ((u64)NSEC_PER_SEC) ++ ++/* ++ * The max value specified for each field is based on the number of bits ++ * in the pwm control register for that field ++ */ ++#define IPQ_PWM_MAX_DIV 0xFFFF ++ ++/* ++ * Two 32-bit registers for each PWM: REG0, and REG1. ++ * Base offset for PWM #i is at 8 * #i. ++ */ ++#define IPQ_PWM_REG0 0 ++#define IPQ_PWM_REG0_PWM_DIV GENMASK(15, 0) ++#define IPQ_PWM_REG0_HI_DURATION GENMASK(31, 16) ++ ++#define IPQ_PWM_REG1 4 ++#define IPQ_PWM_REG1_PRE_DIV GENMASK(15, 0) ++/* ++ * Enable bit is set to enable output toggling in pwm device. ++ * Update bit is set to reflect the changed divider and high duration ++ * values in register. ++ */ ++#define IPQ_PWM_REG1_UPDATE BIT(30) ++#define IPQ_PWM_REG1_ENABLE BIT(31) ++ ++struct ipq_pwm_chip { ++ struct pwm_chip chip; ++ struct clk *clk; ++ void __iomem *mem; ++}; ++ ++static struct ipq_pwm_chip *ipq_pwm_from_chip(struct pwm_chip *chip) ++{ ++ return container_of(chip, struct ipq_pwm_chip, chip); ++} ++ ++static unsigned int ipq_pwm_reg_read(struct pwm_device *pwm, unsigned int reg) ++{ ++ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip); ++ unsigned int off = 8 * pwm->hwpwm + reg; ++ ++ return readl(ipq_chip->mem + off); ++} ++ ++static void ipq_pwm_reg_write(struct pwm_device *pwm, unsigned int reg, ++ unsigned int val) ++{ ++ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip); ++ unsigned int off = 8 * pwm->hwpwm + reg; ++ ++ writel(val, ipq_chip->mem + off); ++} ++ ++static void config_div_and_duty(struct pwm_device *pwm, unsigned int pre_div, ++ unsigned int pwm_div, unsigned long rate, u64 duty_ns, ++ bool enable) ++{ ++ unsigned long hi_dur; ++ unsigned long val = 0; ++ ++ /* ++ * high duration = pwm duty * (pwm div + 1) ++ * pwm duty = duty_ns / period_ns ++ */ ++ hi_dur = div64_u64(duty_ns * rate, (pre_div + 1) * NSEC_PER_SEC); ++ ++ val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) | ++ FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div); ++ ipq_pwm_reg_write(pwm, IPQ_PWM_REG0, val); ++ ++ val = FIELD_PREP(IPQ_PWM_REG1_PRE_DIV, pre_div); ++ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); ++ ++ /* PWM enable toggle needs a separate write to REG1 */ ++ val |= IPQ_PWM_REG1_UPDATE; ++ if (enable) ++ val |= IPQ_PWM_REG1_ENABLE; ++ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); ++} ++ ++static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); ++ unsigned int pre_div, pwm_div, best_pre_div, best_pwm_div; ++ unsigned long rate = clk_get_rate(ipq_chip->clk); ++ u64 period_ns, duty_ns, period_rate; ++ u64 min_diff; ++ ++ if (state->polarity != PWM_POLARITY_NORMAL) ++ return -EINVAL; ++ ++ if (state->period < DIV64_U64_ROUND_UP(NSEC_PER_SEC, rate)) ++ return -ERANGE; ++ ++ period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS); ++ duty_ns = min(state->duty_cycle, period_ns); ++ ++ /* ++ * period_ns is 1G or less. As long as rate is less than 16 GHz, ++ * period_rate does not overflow. Make that explicit. ++ */ ++ if ((unsigned long long)rate > 16ULL * GIGA) ++ return -EINVAL; ++ period_rate = period_ns * rate; ++ best_pre_div = IPQ_PWM_MAX_DIV; ++ best_pwm_div = IPQ_PWM_MAX_DIV; ++ /* ++ * We don't need to consider pre_div values smaller than ++ * ++ * period_rate ++ * pre_div_min := ------------------------------------ ++ * NSEC_PER_SEC * (IPQ_PWM_MAX_DIV + 1) ++ * ++ * because pre_div = pre_div_min results in a better ++ * approximation. ++ */ ++ pre_div = div64_u64(period_rate, ++ (u64)NSEC_PER_SEC * (IPQ_PWM_MAX_DIV + 1)); ++ min_diff = period_rate; ++ ++ for (; pre_div <= IPQ_PWM_MAX_DIV; pre_div++) { ++ u64 remainder; ++ ++ pwm_div = div64_u64_rem(period_rate, ++ (u64)NSEC_PER_SEC * (pre_div + 1), &remainder); ++ /* pwm_div is unsigned; the check below catches underflow */ ++ pwm_div--; ++ ++ /* ++ * Swapping values for pre_div and pwm_div produces the same ++ * period length. So we can skip all settings with pre_div > ++ * pwm_div which results in bigger constraints for selecting ++ * the duty_cycle than with the two values swapped. ++ */ ++ if (pre_div > pwm_div) ++ break; ++ ++ /* ++ * Make sure we can do 100% duty cycle where ++ * hi_dur == pwm_div + 1 ++ */ ++ if (pwm_div > IPQ_PWM_MAX_DIV - 1) ++ continue; ++ ++ if (remainder < min_diff) { ++ best_pre_div = pre_div; ++ best_pwm_div = pwm_div; ++ min_diff = remainder; ++ ++ if (min_diff == 0) /* bingo */ ++ break; ++ } ++ } ++ ++ /* config divider values for the closest possible frequency */ ++ config_div_and_duty(pwm, best_pre_div, best_pwm_div, ++ rate, duty_ns, state->enabled); ++ ++ return 0; ++} ++ ++static int ipq_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state) ++{ ++ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); ++ unsigned long rate = clk_get_rate(ipq_chip->clk); ++ unsigned int pre_div, pwm_div, hi_dur; ++ u64 effective_div, hi_div; ++ u32 reg0, reg1; ++ ++ reg0 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG0); ++ reg1 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG1); ++ ++ state->polarity = PWM_POLARITY_NORMAL; ++ state->enabled = reg1 & IPQ_PWM_REG1_ENABLE; ++ ++ pwm_div = FIELD_GET(IPQ_PWM_REG0_PWM_DIV, reg0); ++ hi_dur = FIELD_GET(IPQ_PWM_REG0_HI_DURATION, reg0); ++ pre_div = FIELD_GET(IPQ_PWM_REG1_PRE_DIV, reg1); ++ ++ /* No overflow here, both pre_div and pwm_div <= 0xffff */ ++ effective_div = (u64)(pre_div + 1) * (pwm_div + 1); ++ state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC, rate); ++ ++ hi_div = hi_dur * (pre_div + 1); ++ state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC, rate); ++ ++ return 0; ++} ++ ++static const struct pwm_ops ipq_pwm_ops = { ++ .apply = ipq_pwm_apply, ++ .get_state = ipq_pwm_get_state, ++ .owner = THIS_MODULE, ++}; ++ ++static int ipq_pwm_probe(struct platform_device *pdev) ++{ ++ struct ipq_pwm_chip *pwm; ++ struct device *dev = &pdev->dev; ++ int ret; ++ ++ pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL); ++ if (!pwm) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, pwm); ++ ++ pwm->mem = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(pwm->mem)) ++ return dev_err_probe(dev, PTR_ERR(pwm->mem), ++ "regs map failed"); ++ ++ pwm->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(pwm->clk)) ++ return dev_err_probe(dev, PTR_ERR(pwm->clk), ++ "failed to get clock"); ++ ++ ret = clk_prepare_enable(pwm->clk); ++ if (ret) ++ return dev_err_probe(dev, ret, "clock enable failed"); ++ ++ pwm->chip.dev = dev; ++ pwm->chip.ops = &ipq_pwm_ops; ++ pwm->chip.npwm = 4; ++ ++ ret = pwmchip_add(&pwm->chip); ++ if (ret < 0) { ++ dev_err_probe(dev, ret, "pwmchip_add() failed\n"); ++ clk_disable_unprepare(pwm->clk); ++ } ++ ++ return ret; ++} ++ ++static int ipq_pwm_remove(struct platform_device *pdev) ++{ ++ struct ipq_pwm_chip *pwm = platform_get_drvdata(pdev); ++ ++ pwmchip_remove(&pwm->chip); ++ clk_disable_unprepare(pwm->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id pwm_ipq_dt_match[] = { ++ { .compatible = "qcom,ipq6018-pwm", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, pwm_ipq_dt_match); ++ ++static struct platform_driver ipq_pwm_driver = { ++ .driver = { ++ .name = "ipq-pwm", ++ .of_match_table = pwm_ipq_dt_match, ++ }, ++ .probe = ipq_pwm_probe, ++ .remove = ipq_pwm_remove, ++}; ++ ++module_platform_driver(ipq_pwm_driver); ++ ++MODULE_LICENSE("Dual BSD/GPL"); diff --git a/target/linux/qualcommax/patches-6.6/0142-dt-bindings-mfd-qcom-tcsr-Add-simple-mfd-support-for-IPQ6018.patch b/target/linux/qualcommax/patches-6.6/0142-dt-bindings-mfd-qcom-tcsr-Add-simple-mfd-support-for-IPQ6018.patch new file mode 100644 index 0000000000..1714fc4e6f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0142-dt-bindings-mfd-qcom-tcsr-Add-simple-mfd-support-for-IPQ6018.patch @@ -0,0 +1,148 @@ +From: Devi Priya +Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add simple-mfd support for IPQ6018 +Date: Thu, 5 Oct 2023 21:35:49 +0530 + +Update the binding to include pwm as the child node to TCSR block and +add simple-mfd support for IPQ6018. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Devi Priya +--- + .../devicetree/bindings/mfd/qcom,tcsr.yaml | 112 +++++++++++++----- + 1 file changed, 81 insertions(+), 31 deletions(-) + +--- a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml ++++ b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml +@@ -15,49 +15,101 @@ description: + + properties: + compatible: +- items: +- - enum: +- - qcom,msm8976-tcsr +- - qcom,msm8998-tcsr +- - qcom,qcs404-tcsr +- - qcom,sc7180-tcsr +- - qcom,sc7280-tcsr +- - qcom,sc8280xp-tcsr +- - qcom,sdm630-tcsr +- - qcom,sdm845-tcsr +- - qcom,sdx55-tcsr +- - qcom,sdx65-tcsr +- - qcom,sm8150-tcsr +- - qcom,sm8450-tcsr +- - qcom,tcsr-apq8064 +- - qcom,tcsr-apq8084 +- - qcom,tcsr-ipq5332 +- - qcom,tcsr-ipq6018 +- - qcom,tcsr-ipq8064 +- - qcom,tcsr-ipq8074 +- - qcom,tcsr-ipq9574 +- - qcom,tcsr-mdm9615 +- - qcom,tcsr-msm8226 +- - qcom,tcsr-msm8660 +- - qcom,tcsr-msm8916 +- - qcom,tcsr-msm8953 +- - qcom,tcsr-msm8960 +- - qcom,tcsr-msm8974 +- - qcom,tcsr-msm8996 +- - const: syscon ++ oneOf: ++ - items: ++ - enum: ++ - qcom,msm8976-tcsr ++ - qcom,msm8998-tcsr ++ - qcom,qcs404-tcsr ++ - qcom,sc7180-tcsr ++ - qcom,sc7280-tcsr ++ - qcom,sc8280xp-tcsr ++ - qcom,sdm630-tcsr ++ - qcom,sdm845-tcsr ++ - qcom,sdx55-tcsr ++ - qcom,sdx65-tcsr ++ - qcom,sm4450-tcsr ++ - qcom,sm8150-tcsr ++ - qcom,sm8450-tcsr ++ - qcom,tcsr-apq8064 ++ - qcom,tcsr-apq8084 ++ - qcom,tcsr-ipq5332 ++ - qcom,tcsr-ipq8064 ++ - qcom,tcsr-ipq8074 ++ - qcom,tcsr-ipq9574 ++ - qcom,tcsr-mdm9615 ++ - qcom,tcsr-msm8226 ++ - qcom,tcsr-msm8660 ++ - qcom,tcsr-msm8916 ++ - qcom,tcsr-msm8953 ++ - qcom,tcsr-msm8960 ++ - qcom,tcsr-msm8974 ++ - qcom,tcsr-msm8996 ++ - const: syscon ++ - items: ++ - const: qcom,tcsr-ipq6018 ++ - const: syscon ++ - const: simple-mfd + + reg: + maxItems: 1 + ++ ranges: true ++ ++ "#address-cells": ++ const: 1 ++ ++ "#size-cells": ++ const: 1 ++ ++patternProperties: ++ "pwm@[a-f0-9]+$": ++ type: object ++ $ref: /schemas/pwm/qcom,ipq6018-pwm.yaml ++ ++ + required: + - compatible + - reg + ++allOf: ++ - if: ++ not: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - qcom,tcsr-ipq6018 ++ then: ++ patternProperties: ++ "pwm@[a-f0-9]+$": false ++ + additionalProperties: false + + examples: ++ # Example 1 - Syscon node found on MSM8960 + - | + syscon@1a400000 { + compatible = "qcom,tcsr-msm8960", "syscon"; + reg = <0x1a400000 0x100>; + }; ++ # Example 2 - Syscon node found on IPQ6018 ++ - | ++ #include ++ ++ syscon@1937000 { ++ compatible = "qcom,tcsr-ipq6018", "syscon", "simple-mfd"; ++ reg = <0x01937000 0x21000>; ++ ranges = <0 0x1937000 0x21000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ pwm: pwm@a010 { ++ compatible = "qcom,ipq6018-pwm"; ++ reg = <0xa010 0x20>; ++ clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clock-rates = <100000000>; ++ #pwm-cells = <2>; ++ }; ++ }; +\ No newline at end of file diff --git a/target/linux/qualcommax/patches-6.6/0150-dt-bindings-nvmem-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0150-dt-bindings-nvmem-add-IPQ5018-compatible.patch new file mode 100644 index 0000000000..c31b2ba497 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0150-dt-bindings-nvmem-add-IPQ5018-compatible.patch @@ -0,0 +1,22 @@ +From: Sricharan Ramabadhran +Subject: [PATCH V2 1/1] dt-bindings: nvmem: Add compatible for IPQ5018 +Date: Fri, 15 Sep 2023 17:31:20 +0530 + +Document the QFPROM on IPQ5018. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Sricharan Ramabadhran +--- + Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 + + 1 file changed, 1 insertion(+) + +--- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml ++++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +@@ -18,6 +18,7 @@ properties: + - enum: + - qcom,apq8064-qfprom + - qcom,apq8084-qfprom ++ - qcom,ipq5018-qfprom + - qcom,ipq5332-qfprom + - qcom,ipq6018-qfprom + - qcom,ipq8064-qfprom diff --git a/target/linux/qualcommax/patches-6.6/0151-dt-bindings-thermal-qcom-tsens-Add-ipq5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0151-dt-bindings-thermal-qcom-tsens-Add-ipq5018-compatible.patch new file mode 100644 index 0000000000..36ab4abbdf --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0151-dt-bindings-thermal-qcom-tsens-Add-ipq5018-compatible.patch @@ -0,0 +1,26 @@ +From: Sricharan Ramabadhran +Date: Fri, 22 Sep 2023 17:21:13 +0530 +Subject: [PATCH] dt-bindings: thermal: qcom-tsens: Add ipq5018 compatible + +IPQ5018 has tsens v1.0 block with 4 sensors and 1 interrupt. + +Signed-off-by: Sricharan Ramabadhran +--- +--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml ++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +@@ -39,6 +39,7 @@ properties: + - description: v1 of TSENS + items: + - enum: ++ - qcom,ipq5018-tsens + - qcom,msm8956-tsens + - qcom,msm8976-tsens + - qcom,qcs404-tsens +@@ -232,6 +233,7 @@ allOf: + compatible: + contains: + enum: ++ - qcom,ipq5018-tsens + - qcom,ipq8064-tsens + - qcom,msm8960-tsens + - qcom,tsens-v0_1 diff --git a/target/linux/qualcommax/patches-6.6/0152-thermal-qcom-add-new-feat-for-soc-without-rpm.patch b/target/linux/qualcommax/patches-6.6/0152-thermal-qcom-add-new-feat-for-soc-without-rpm.patch new file mode 100644 index 0000000000..c8f393e2b6 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0152-thermal-qcom-add-new-feat-for-soc-without-rpm.patch @@ -0,0 +1,45 @@ +From: Sricharan Ramabadhran +Subject: [PATCH] thermal/drivers/qcom: Add new feat for soc without rpm +Date: Fri, 22 Sep 2023 17:21:14 +0530 + +In IPQ5018, Tsens IP doesn't have RPM. Hence the early init to +enable tsens would not be done. So add a flag for that in feat +and skip enable checks. Without this, tsens probe fails. + +Reviewed-by: Dmitry Baryshkov +Signed-off-by: Sricharan Ramabadhran +--- + drivers/thermal/qcom/tsens.c | 2 +- + drivers/thermal/qcom/tsens.h | 3 +++ + 2 files changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -974,7 +974,7 @@ int __init init_common(struct tsens_priv + ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); + if (ret) + goto err_put_device; +- if (!enabled) { ++ if (!enabled && !(priv->feat->ignore_enable)) { + dev_err(dev, "%s: device not enabled\n", __func__); + ret = -ENODEV; + goto err_put_device; +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -505,6 +505,8 @@ enum regfield_ids { + * @srot_split: does the IP neatly splits the register space into SROT and TM, + * with SROT only being available to secure boot firmware? + * @has_watchdog: does this IP support watchdog functionality? ++ * @ignore_enable: does this IP reside in a soc that does not have rpm to ++ * do pre-init. + * @max_sensors: maximum sensors supported by this version of the IP + * @trip_min_temp: minimum trip temperature supported by this version of the IP + * @trip_max_temp: maximum trip temperature supported by this version of the IP +@@ -516,6 +518,7 @@ struct tsens_features { + unsigned int adc:1; + unsigned int srot_split:1; + unsigned int has_watchdog:1; ++ unsigned int ignore_enable:1; + unsigned int max_sensors; + int trip_min_temp; + int trip_max_temp; diff --git a/target/linux/qualcommax/patches-6.6/0153-thermal-qcom-tsens-add-support-for-IPQ5018-tsens.patch b/target/linux/qualcommax/patches-6.6/0153-thermal-qcom-tsens-add-support-for-IPQ5018-tsens.patch new file mode 100644 index 0000000000..0de11daf72 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0153-thermal-qcom-tsens-add-support-for-IPQ5018-tsens.patch @@ -0,0 +1,118 @@ +From: Sricharan Ramabadhran +Subject: [PATCH] thermal/drivers/tsens: Add support for IPQ5018 tsens +Date: Fri, 22 Sep 2023 17:21:15 +0530 + +IPQ5018 has tsens IP V1.0, 4 sensors and 1 interrupt. +The soc does not have a RPM, hence tsens has to be reset and +enabled in the driver init. Adding the driver support for same. + +Signed-off-by: Sricharan Ramabadhran +--- + drivers/thermal/qcom/tsens-v1.c | 60 +++++++++++++++++++++++++++++++++ + drivers/thermal/qcom/tsens.c | 3 ++ + drivers/thermal/qcom/tsens.h | 2 +- + 3 files changed, 64 insertions(+), 1 deletion(-) + +--- a/drivers/thermal/qcom/tsens-v1.c ++++ b/drivers/thermal/qcom/tsens-v1.c +@@ -79,6 +79,18 @@ static struct tsens_features tsens_v1_fe + .trip_max_temp = 120000, + }; + ++static struct tsens_features tsens_v1_ipq5018_feat = { ++ .ver_major = VER_1_X, ++ .crit_int = 0, ++ .combo_int = 0, ++ .adc = 1, ++ .srot_split = 1, ++ .max_sensors = 11, ++ .trip_min_temp = -40000, ++ .trip_max_temp = 120000, ++ .ignore_enable = 1, ++}; ++ + static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = { + /* ----- SROT ------ */ + /* VERSION */ +@@ -150,6 +162,41 @@ static int __init init_8956(struct tsens + return init_common(priv); + } + ++static int __init init_ipq5018(struct tsens_priv *priv) ++{ ++ int ret; ++ u32 mask; ++ ++ ret = init_common(priv); ++ if (ret < 0) { ++ dev_err(priv->dev, "Init common failed %d\n", ret); ++ return ret; ++ } ++ ++ ret = regmap_field_write(priv->rf[TSENS_SW_RST], 1); ++ if (ret) { ++ dev_err(priv->dev, "Reset failed\n"); ++ return ret; ++ } ++ ++ mask = GENMASK(priv->num_sensors, 0); ++ ret = regmap_field_update_bits(priv->rf[SENSOR_EN], mask, mask); ++ if (ret) { ++ dev_err(priv->dev, "Sensor Enable failed\n"); ++ return ret; ++ } ++ ++ ret = regmap_field_write(priv->rf[TSENS_EN], 1); ++ if (ret) { ++ dev_err(priv->dev, "Enable failed\n"); ++ return ret; ++ } ++ ++ ret = regmap_field_write(priv->rf[TSENS_SW_RST], 0); ++ ++ return ret; ++} ++ + static const struct tsens_ops ops_generic_v1 = { + .init = init_common, + .calibrate = calibrate_v1, +@@ -194,3 +241,16 @@ struct tsens_plat_data data_8976 = { + .feat = &tsens_v1_feat, + .fields = tsens_v1_regfields, + }; ++ ++const struct tsens_ops ops_ipq5018 = { ++ .init = init_ipq5018, ++ .calibrate = tsens_calibrate_common, ++ .get_temp = get_temp_tsens_valid, ++}; ++ ++struct tsens_plat_data data_ipq5018 = { ++ .num_sensors = 5, ++ .ops = &ops_ipq5018, ++ .feat = &tsens_v1_ipq5018_feat, ++ .fields = tsens_v1_regfields, ++}; +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -1101,6 +1101,9 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t + + static const struct of_device_id tsens_table[] = { + { ++ .compatible = "qcom,ipq5018-tsens", ++ .data = &data_ipq5018, ++ }, { + .compatible = "qcom,ipq8064-tsens", + .data = &data_8960, + }, { +--- a/drivers/thermal/qcom/tsens.h ++++ b/drivers/thermal/qcom/tsens.h +@@ -645,7 +645,7 @@ extern struct tsens_plat_data data_8960; + extern struct tsens_plat_data data_8226, data_8909, data_8916, data_8939, data_8974, data_9607; + + /* TSENS v1 targets */ +-extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956; ++extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956, data_ipq5018; + + /* TSENS v2 targets */ + extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2; diff --git a/target/linux/qualcommax/patches-6.6/0154-dts-qcom-IPQ5018-add-tsens-node.patch b/target/linux/qualcommax/patches-6.6/0154-dts-qcom-IPQ5018-add-tsens-node.patch new file mode 100644 index 0000000000..4f9b8abfa5 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0154-dts-qcom-IPQ5018-add-tsens-node.patch @@ -0,0 +1,200 @@ +From: Sricharan Ramabadhran +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add tsens node +Date: Fri, 22 Sep 2023 17:21:16 +0530 + +IPQ5018 has tsens V1.0 IP with 4 sensors. +There is no RPM, so tsens has to be manually enabled. Adding the tsens +and nvmem node and IPQ5018 has 4 thermal sensors (zones). With the +critical temperature being 120'C and action is to reboot. Adding all +the 4 zones here. + +Signed-off-by: Sricharan Ramabadhran +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 169 ++++++++++++++++++++++++++ + 1 file changed, 169 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -149,6 +149,117 @@ + status = "disabled"; + }; + ++ qfprom: qfprom@a0000 { ++ compatible = "qcom,ipq5018-qfprom", "qcom,qfprom"; ++ reg = <0xa0000 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ tsens_mode: mode@249 { ++ reg = <0x249 1>; ++ bits = <0 3>; ++ }; ++ ++ tsens_base1: base1@249 { ++ reg = <0x249 2>; ++ bits = <3 8>; ++ }; ++ ++ tsens_base2: base2@24a { ++ reg = <0x24a 2>; ++ bits = <3 8>; ++ }; ++ ++ tsens_s0_p1: s0-p1@24b { ++ reg = <0x24b 0x2>; ++ bits = <2 6>; ++ }; ++ ++ tsens_s0_p2: s0-p2@24c { ++ reg = <0x24c 0x1>; ++ bits = <1 6>; ++ }; ++ ++ tsens_s1_p1: s1-p1@24c { ++ reg = <0x24c 0x2>; ++ bits = <7 6>; ++ }; ++ ++ tsens_s1_p2: s1-p2@24d { ++ reg = <0x24d 0x2>; ++ bits = <5 6>; ++ }; ++ ++ tsens_s2_p1: s2-p1@24e { ++ reg = <0x24e 0x2>; ++ bits = <3 6>; ++ }; ++ ++ tsens_s2_p2: s2-p2@24f { ++ reg = <0x24f 0x1>; ++ bits = <1 6>; ++ }; ++ ++ tsens_s3_p1: s3-p1@24f { ++ reg = <0x24f 0x2>; ++ bits = <7 6>; ++ }; ++ ++ tsens_s3_p2: s3-p2@250 { ++ reg = <0x250 0x2>; ++ bits = <5 6>; ++ }; ++ ++ tsens_s4_p1: s4-p1@251 { ++ reg = <0x251 0x2>; ++ bits = <3 6>; ++ }; ++ ++ tsens_s4_p2: s4-p2@254 { ++ reg = <0x254 0x1>; ++ bits = <0 6>; ++ }; ++ }; ++ ++ tsens: thermal-sensor@4a9000 { ++ compatible = "qcom,ipq5018-tsens"; ++ reg = <0x4a9000 0x1000>, /* TM */ ++ <0x4a8000 0x1000>; /* SROT */ ++ ++ nvmem-cells = <&tsens_mode>, ++ <&tsens_base1>, ++ <&tsens_base2>, ++ <&tsens_s0_p1>, ++ <&tsens_s0_p2>, ++ <&tsens_s1_p1>, ++ <&tsens_s1_p2>, ++ <&tsens_s2_p1>, ++ <&tsens_s2_p2>, ++ <&tsens_s3_p1>, ++ <&tsens_s3_p2>, ++ <&tsens_s4_p1>, ++ <&tsens_s4_p2>; ++ ++ nvmem-cell-names = "mode", ++ "base1", ++ "base2", ++ "s0_p1", ++ "s0_p2", ++ "s1_p1", ++ "s1_p2", ++ "s2_p1", ++ "s2_p2", ++ "s3_p1", ++ "s3_p2", ++ "s4_p1", ++ "s4_p2"; ++ ++ interrupts = ; ++ interrupt-names = "uplow"; ++ #qcom,sensors = <5>; ++ #thermal-sensor-cells = <1>; ++ }; ++ + tlmm: pinctrl@1000000 { + compatible = "qcom,ipq5018-tlmm"; + reg = <0x01000000 0x300000>; +@@ -391,6 +502,64 @@ + }; + }; + }; ++ ++ thermal-zones { ++ cpu-thermal { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 2>; ++ ++ trips { ++ cpu-critical { ++ temperature = <120000>; ++ hysteresis = <2>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ gephy-thermal { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 4>; ++ ++ trips { ++ gephy-critical { ++ temperature = <120000>; ++ hysteresis = <2>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ top-glue-thermal { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 3>; ++ ++ trips { ++ top_glue-critical { ++ temperature = <120000>; ++ hysteresis = <2>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ ++ ubi32-thermal { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&tsens 1>; ++ ++ trips { ++ ubi32-critical { ++ temperature = <120000>; ++ hysteresis = <2>; ++ type = "critical"; ++ }; ++ }; ++ }; ++ }; + + timer { + compatible = "arm,armv8-timer"; diff --git a/target/linux/qualcommax/patches-6.6/0155-dt-bindings-phy-qcom-uniphy-pcie-Document-PCIe-uniphy.patch b/target/linux/qualcommax/patches-6.6/0155-dt-bindings-phy-qcom-uniphy-pcie-Document-PCIe-uniphy.patch new file mode 100644 index 0000000000..7997976d7c --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0155-dt-bindings-phy-qcom-uniphy-pcie-Document-PCIe-uniphy.patch @@ -0,0 +1,85 @@ +From: Varadarajan Narayanan +Date: Thu, 2 Jan 2025 17:00:15 +0530 +Subject: [PATCH] dt-bindings: phy: qcom,uniphy-pcie: Document PCIe uniphy + +From: Nitheesh Sekar + +Document the Qualcomm UNIPHY PCIe 28LP present in IPQ5332. + +Signed-off-by: Nitheesh Sekar +Signed-off-by: Varadarajan Narayanan +--- +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-uniphy-pcie-phy.yaml +@@ -0,0 +1,71 @@ ++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/phy/qcom,ipq5332-uniphy-pcie-phy.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Qualcomm UNIPHY PCIe 28LP PHY ++ ++maintainers: ++ - Nitheesh Sekar ++ - Varadarajan Narayanan ++ ++description: ++ PCIe and USB combo PHY found in Qualcomm IPQ5332 SoC ++ ++properties: ++ compatible: ++ enum: ++ - qcom,ipq5332-uniphy-pcie-phy ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: pcie pipe clock ++ - description: pcie ahb clock ++ ++ resets: ++ items: ++ - description: phy reset ++ - description: ahb reset ++ - description: cfg reset ++ ++ "#phy-cells": ++ const: 0 ++ ++ "#clock-cells": ++ const: 0 ++ ++ num-lanes: true ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - resets ++ - "#phy-cells" ++ - "#clock-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ ++ pcie0_phy: phy@4b0000 { ++ compatible = "qcom,ipq5332-uniphy-pcie-phy"; ++ reg = <0x004b0000 0x800>; ++ ++ clocks = <&gcc GCC_PCIE3X1_0_PIPE_CLK>, ++ <&gcc GCC_PCIE3X1_PHY_AHB_CLK>; ++ ++ resets = <&gcc GCC_PCIE3X1_0_PHY_BCR>, ++ <&gcc GCC_PCIE3X1_PHY_AHB_CLK_ARES>, ++ <&gcc GCC_PCIE3X1_0_PHY_PHY_BCR>; ++ ++ #clock-cells = <0>; ++ ++ #phy-cells = <0>; ++ }; diff --git a/target/linux/qualcommax/patches-6.6/0156-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch b/target/linux/qualcommax/patches-6.6/0156-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch new file mode 100644 index 0000000000..f30db1463f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0156-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch @@ -0,0 +1,332 @@ +From: Varadarajan Narayanan +Date: Thu, 2 Jan 2025 17:00:16 +0530 +Subject: [PATCH] phy: qcom: Introduce PCIe UNIPHY 28LP driver + +From: Nitheesh Sekar + +Add Qualcomm PCIe UNIPHY 28LP driver support present +in Qualcomm IPQ5332 SoC and the phy init sequence. + +Reviewed-by: Dmitry Baryshkov +Signed-off-by: Nitheesh Sekar +Signed-off-by: Varadarajan Narayanan +--- +--- a/drivers/phy/qualcomm/Kconfig ++++ b/drivers/phy/qualcomm/Kconfig +@@ -154,6 +154,18 @@ config PHY_QCOM_M31_USB + management. This driver is required even for peripheral only or + host only mode configurations. + ++config PHY_QCOM_UNIPHY_PCIE_28LP ++ bool "PCIE UNIPHY 28LP PHY driver" ++ depends on ARCH_QCOM ++ depends on HAS_IOMEM ++ depends on OF ++ select GENERIC_PHY ++ help ++ Enable this to support the PCIe UNIPHY 28LP phy transceiver that ++ is used with PCIe controllers on Qualcomm IPQ5332 chips. It ++ handles PHY initialization, clock management required after ++ resetting the hardware and power management. ++ + config PHY_QCOM_USB_HS + tristate "Qualcomm USB HS PHY module" + depends on USB_ULPI_BUS +--- a/drivers/phy/qualcomm/Makefile ++++ b/drivers/phy/qualcomm/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_PHY_QCOM_QMP_USB_LEGACY) += + obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o + obj-$(CONFIG_PHY_QCOM_SNPS_EUSB2) += phy-qcom-snps-eusb2.o + obj-$(CONFIG_PHY_QCOM_EUSB2_REPEATER) += phy-qcom-eusb2-repeater.o ++obj-$(CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP) += phy-qcom-uniphy-pcie-28lp.o + obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o + obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o + obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c +@@ -0,0 +1,285 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (c) 2025, The Linux Foundation. All rights reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RST_ASSERT_DELAY_MIN_US 100 ++#define RST_ASSERT_DELAY_MAX_US 150 ++#define PIPE_CLK_DELAY_MIN_US 5000 ++#define PIPE_CLK_DELAY_MAX_US 5100 ++#define CLK_EN_DELAY_MIN_US 30 ++#define CLK_EN_DELAY_MAX_US 50 ++#define CDR_CTRL_REG_1 0x80 ++#define CDR_CTRL_REG_2 0x84 ++#define CDR_CTRL_REG_3 0x88 ++#define CDR_CTRL_REG_4 0x8c ++#define CDR_CTRL_REG_5 0x90 ++#define CDR_CTRL_REG_6 0x94 ++#define CDR_CTRL_REG_7 0x98 ++#define SSCG_CTRL_REG_1 0x9c ++#define SSCG_CTRL_REG_2 0xa0 ++#define SSCG_CTRL_REG_3 0xa4 ++#define SSCG_CTRL_REG_4 0xa8 ++#define SSCG_CTRL_REG_5 0xac ++#define SSCG_CTRL_REG_6 0xb0 ++#define PCS_INTERNAL_CONTROL_2 0x2d8 ++ ++#define PHY_CFG_PLLCFG 0x220 ++#define PHY_CFG_EIOS_DTCT_REG 0x3e4 ++#define PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME 0x3e8 ++ ++#define PHY_MODE_FIXED 0x1 ++ ++enum qcom_uniphy_pcie_type { ++ PHY_TYPE_PCIE = 1, ++ PHY_TYPE_PCIE_GEN2, ++ PHY_TYPE_PCIE_GEN3, ++}; ++ ++struct qcom_uniphy_pcie_regs { ++ u32 offset; ++ u32 val; ++}; ++ ++struct qcom_uniphy_pcie_data { ++ int lane_offset; /* offset between the lane register bases */ ++ u32 phy_type; ++ const struct qcom_uniphy_pcie_regs *init_seq; ++ u32 init_seq_num; ++ u32 pipe_clk_rate; ++}; ++ ++struct qcom_uniphy_pcie { ++ struct phy phy; ++ struct device *dev; ++ const struct qcom_uniphy_pcie_data *data; ++ struct clk_bulk_data *clks; ++ int num_clks; ++ struct reset_control *resets; ++ void __iomem *base; ++ int lanes; ++}; ++ ++#define phy_to_dw_phy(x) container_of((x), struct qca_uni_pcie_phy, phy) ++ ++static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = { ++ { ++ .offset = PHY_CFG_PLLCFG, ++ .val = 0x30, ++ }, { ++ .offset = PHY_CFG_EIOS_DTCT_REG, ++ .val = 0x53ef, ++ }, { ++ .offset = PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME, ++ .val = 0xcf, ++ }, ++}; ++ ++static const struct qcom_uniphy_pcie_data ipq5332_data = { ++ .lane_offset = 0x800, ++ .phy_type = PHY_TYPE_PCIE_GEN3, ++ .init_seq = ipq5332_regs, ++ .init_seq_num = ARRAY_SIZE(ipq5332_regs), ++ .pipe_clk_rate = 250000000, ++}; ++ ++static void qcom_uniphy_pcie_init(struct qcom_uniphy_pcie *phy) ++{ ++ const struct qcom_uniphy_pcie_data *data = phy->data; ++ const struct qcom_uniphy_pcie_regs *init_seq; ++ void __iomem *base = phy->base; ++ int lane, i; ++ ++ for (lane = 0; lane < phy->lanes; lane++) { ++ init_seq = data->init_seq; ++ ++ for (i = 0; i < data->init_seq_num; i++) ++ writel(init_seq[i].val, base + init_seq[i].offset); ++ ++ base += data->lane_offset; ++ } ++} ++ ++static int qcom_uniphy_pcie_power_off(struct phy *x) ++{ ++ struct qcom_uniphy_pcie *phy = phy_get_drvdata(x); ++ ++ clk_bulk_disable_unprepare(phy->num_clks, phy->clks); ++ ++ return reset_control_assert(phy->resets); ++} ++ ++static int qcom_uniphy_pcie_power_on(struct phy *x) ++{ ++ struct qcom_uniphy_pcie *phy = phy_get_drvdata(x); ++ int ret; ++ ++ ret = reset_control_assert(phy->resets); ++ if (ret) { ++ dev_err(phy->dev, "reset assert failed (%d)\n", ret); ++ return ret; ++ } ++ ++ usleep_range(RST_ASSERT_DELAY_MIN_US, RST_ASSERT_DELAY_MAX_US); ++ ++ ret = reset_control_deassert(phy->resets); ++ if (ret) { ++ dev_err(phy->dev, "reset deassert failed (%d)\n", ret); ++ return ret; ++ } ++ ++ usleep_range(PIPE_CLK_DELAY_MIN_US, PIPE_CLK_DELAY_MAX_US); ++ ++ ret = clk_bulk_prepare_enable(phy->num_clks, phy->clks); ++ if (ret) { ++ dev_err(phy->dev, "clk prepare and enable failed %d\n", ret); ++ return ret; ++ } ++ ++ usleep_range(CLK_EN_DELAY_MIN_US, CLK_EN_DELAY_MAX_US); ++ ++ qcom_uniphy_pcie_init(phy); ++ return 0; ++} ++ ++static inline int qcom_uniphy_pcie_get_resources(struct platform_device *pdev, ++ struct qcom_uniphy_pcie *phy) ++{ ++ struct resource *res; ++ ++ phy->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(phy->base)) ++ return PTR_ERR(phy->base); ++ ++ phy->num_clks = devm_clk_bulk_get_all(phy->dev, &phy->clks); ++ if (phy->num_clks < 0) ++ return phy->num_clks; ++ ++ phy->resets = devm_reset_control_array_get_exclusive(phy->dev); ++ if (IS_ERR(phy->resets)) ++ return PTR_ERR(phy->resets); ++ ++ return 0; ++} ++ ++/* ++ * Register a fixed rate pipe clock. ++ * ++ * The _pipe_clksrc generated by PHY goes to the GCC that gate ++ * controls it. The _pipe_clk coming out of the GCC is requested ++ * by the PHY driver for its operations. ++ * We register the _pipe_clksrc here. The gcc driver takes care ++ * of assigning this _pipe_clksrc as parent to _pipe_clk. ++ * Below picture shows this relationship. ++ * ++ * +---------------+ ++ * | PHY block |<<---------------------------------------+ ++ * | | | ++ * | +-------+ | +-----+ | ++ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ ++ * clk | +-------+ | +-----+ ++ * +---------------+ ++ */ ++static inline int phy_pipe_clk_register(struct qcom_uniphy_pcie *phy, int id) ++{ ++ const struct qcom_uniphy_pcie_data *data = phy->data; ++ struct clk_hw *hw; ++ char name[64]; ++ ++ snprintf(name, sizeof(name), "phy%d_pipe_clk_src", id); ++ hw = devm_clk_hw_register_fixed_rate(phy->dev, name, NULL, 0, ++ data->pipe_clk_rate); ++ if (IS_ERR(hw)) ++ return dev_err_probe(phy->dev, PTR_ERR(hw), ++ "Unable to register %s\n", name); ++ ++ return devm_of_clk_add_hw_provider(phy->dev, of_clk_hw_simple_get, hw); ++} ++ ++static const struct of_device_id qcom_uniphy_pcie_id_table[] = { ++ { ++ .compatible = "qcom,ipq5332-uniphy-pcie-phy", ++ .data = &ipq5332_data, ++ }, { ++ /* Sentinel */ ++ }, ++}; ++MODULE_DEVICE_TABLE(of, qcom_uniphy_pcie_id_table); ++ ++static const struct phy_ops pcie_ops = { ++ .power_on = qcom_uniphy_pcie_power_on, ++ .power_off = qcom_uniphy_pcie_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int qcom_uniphy_pcie_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct device *dev = &pdev->dev; ++ struct qcom_uniphy_pcie *phy; ++ struct phy *generic_phy; ++ int ret; ++ ++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, phy); ++ phy->dev = &pdev->dev; ++ ++ phy->data = of_device_get_match_data(dev); ++ if (!phy->data) ++ return -EINVAL; ++ ++ phy->lanes = 1; ++ ret = of_property_read_u32(dev->of_node, "num-lanes", &phy->lanes); ++ ++ ret = qcom_uniphy_pcie_get_resources(pdev, phy); ++ if (ret < 0) ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to get resources: %d\n", ret); ++ ++ generic_phy = devm_phy_create(phy->dev, NULL, &pcie_ops); ++ if (IS_ERR(generic_phy)) ++ return PTR_ERR(generic_phy); ++ ++ phy_set_drvdata(generic_phy, phy); ++ ++ ret = phy_pipe_clk_register(phy, generic_phy->id); ++ if (ret) ++ dev_err(&pdev->dev, "failed to register phy pipe clk\n"); ++ ++ phy_provider = devm_of_phy_provider_register(phy->dev, ++ of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) ++ return PTR_ERR(phy_provider); ++ ++ return 0; ++} ++ ++static struct platform_driver qcom_uniphy_pcie_driver = { ++ .probe = qcom_uniphy_pcie_probe, ++ .driver = { ++ .name = "qcom-uniphy-pcie", ++ .of_match_table = qcom_uniphy_pcie_id_table, ++ }, ++}; ++ ++module_platform_driver(qcom_uniphy_pcie_driver); ++ ++MODULE_DESCRIPTION("PCIE QCOM UNIPHY driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/qualcommax/patches-6.6/0157-dt-bindings-phy-qcom-uniphy-pcie-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0157-dt-bindings-phy-qcom-uniphy-pcie-add-IPQ5018-compatible.patch new file mode 100644 index 0000000000..9951daa8f7 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0157-dt-bindings-phy-qcom-uniphy-pcie-add-IPQ5018-compatible.patch @@ -0,0 +1,25 @@ +From: George Moussalem +Date: Tue, 07 Jan 2025 17:34:13 +0400 +Subject: [PATCH] phy: qualcomm: qcom-uniphy-pcie add IPQ5018 compatible + +The Qualcomm UNIPHY PCIe PHY 28lp part of the IPQ5332 SoC is also present on +the IPQ5018 SoC, so adding the compatible for IPQ5018. + +Signed-off-by: George Moussalem +--- +--- a/Documentation/devicetree/bindings/phy/qcom,ipq5332-uniphy-pcie-phy.yaml ++++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-uniphy-pcie-phy.yaml +@@ -11,11 +11,12 @@ maintainers: + - Varadarajan Narayanan + + description: +- PCIe and USB combo PHY found in Qualcomm IPQ5332 SoC ++ PCIe and USB combo PHY found in Qualcomm IPQ5018 and IPQ5332 SoCs + + properties: + compatible: + enum: ++ - qcom,ipq5018-uniphy-pcie-phy + - qcom,ipq5332-uniphy-pcie-phy + + reg: diff --git a/target/linux/qualcommax/patches-6.6/0158-phy-qcom-uniphy-pcie-28lp-add-support-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0158-phy-qcom-uniphy-pcie-28lp-add-support-for-IPQ5018.patch new file mode 100644 index 0000000000..aedb959873 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0158-phy-qcom-uniphy-pcie-28lp-add-support-for-IPQ5018.patch @@ -0,0 +1,77 @@ +From: George Moussalem +Date: Tue, 07 Jan 2025 17:34:13 +0400 +Subject: [PATCH] phy: qualcomm: qcom-uniphy-pcie 28lp add support for IPQ5018 + +The Qualcomm UNIPHY PCIe PHY 28lp is found on both IPQ5332 and IPQ5018. +Adding the PHY init sequence, pipe clock rate, and compatible for IPQ5018. + +Signed-off-by: George Moussalem +--- +--- a/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c ++++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c +@@ -76,6 +76,40 @@ struct qcom_uniphy_pcie { + + #define phy_to_dw_phy(x) container_of((x), struct qca_uni_pcie_phy, phy) + ++static const struct qcom_uniphy_pcie_regs ipq5018_regs[] = { ++ { ++ .offset = SSCG_CTRL_REG_4, ++ .val = 0x1cb9, ++ }, { ++ .offset = SSCG_CTRL_REG_5, ++ .val = 0x023a, ++ }, { ++ .offset = SSCG_CTRL_REG_3, ++ .val = 0xd360, ++ }, { ++ .offset = SSCG_CTRL_REG_1, ++ .val = 0x1, ++ }, { ++ .offset = SSCG_CTRL_REG_2, ++ .val = 0xeb, ++ }, { ++ .offset = CDR_CTRL_REG_4, ++ .val = 0x3f9, ++ }, { ++ .offset = CDR_CTRL_REG_5, ++ .val = 0x1c9, ++ }, { ++ .offset = CDR_CTRL_REG_2, ++ .val = 0x419, ++ }, { ++ .offset = CDR_CTRL_REG_1, ++ .val = 0x200, ++ }, { ++ .offset = PCS_INTERNAL_CONTROL_2, ++ .val = 0xf101, ++ }, ++}; ++ + static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = { + { + .offset = PHY_CFG_PLLCFG, +@@ -89,6 +123,14 @@ static const struct qcom_uniphy_pcie_reg + }, + }; + ++static const struct qcom_uniphy_pcie_data ipq5018_data = { ++ .lane_offset = 0x800, ++ .phy_type = PHY_TYPE_PCIE_GEN2, ++ .init_seq = ipq5018_regs, ++ .init_seq_num = ARRAY_SIZE(ipq5018_regs), ++ .pipe_clk_rate = 125000000, ++}; ++ + static const struct qcom_uniphy_pcie_data ipq5332_data = { + .lane_offset = 0x800, + .phy_type = PHY_TYPE_PCIE_GEN3, +@@ -212,6 +254,9 @@ static inline int phy_pipe_clk_register( + + static const struct of_device_id qcom_uniphy_pcie_id_table[] = { + { ++ .compatible = "qcom,ipq5018-uniphy-pcie-phy", ++ .data = &ipq5018_data, ++ }, { + .compatible = "qcom,ipq5332-uniphy-pcie-phy", + .data = &ipq5332_data, + }, { diff --git a/target/linux/qualcommax/patches-6.6/0160-dt-bindings-PCI-qcom-Add-IPQ5018-SoC.patch b/target/linux/qualcommax/patches-6.6/0160-dt-bindings-PCI-qcom-Add-IPQ5018-SoC.patch new file mode 100644 index 0000000000..caa9889109 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0160-dt-bindings-PCI-qcom-Add-IPQ5018-SoC.patch @@ -0,0 +1,78 @@ +From: Nitheesh Sekar +Date: Tue, 3 Oct 2023 17:38:42 +0530 +Subject: [PATCH] dt-bindings: PCI: qcom: Add IPQ5108 SoC + +Add support for the PCIe controller on the Qualcomm +IPQ5108 SoC to the bindings. + +Signed-off-by: Nitheesh Sekar +--- + .../devicetree/bindings/pci/qcom,pcie.yaml | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + +--- a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml ++++ b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml +@@ -21,6 +21,7 @@ properties: + - qcom,pcie-apq8064 + - qcom,pcie-apq8084 + - qcom,pcie-ipq4019 ++ - qcom,pcie-ipq5018 + - qcom,pcie-ipq6018 + - qcom,pcie-ipq8064 + - qcom,pcie-ipq8064-v2 +@@ -170,6 +171,7 @@ allOf: + compatible: + contains: + enum: ++ - qcom,pcie-ipq5018 + - qcom,pcie-ipq6018 + - qcom,pcie-ipq8074-gen3 + then: +@@ -337,6 +339,39 @@ allOf: + compatible: + contains: + enum: ++ - qcom,pcie-ipq5018 ++ then: ++ properties: ++ clocks: ++ minItems: 6 ++ maxItems: 6 ++ clock-names: ++ items: ++ - const: iface # PCIe to SysNOC BIU clock ++ - const: axi_m # AXI Master clock ++ - const: axi_s # AXI Slave clock ++ - const: ahb # AHB clock ++ - const: aux # Auxiliary clock ++ - const: axi_bridge # AXI bridge clock ++ resets: ++ minItems: 8 ++ maxItems: 8 ++ reset-names: ++ items: ++ - const: pipe # PIPE reset ++ - const: sleep # Sleep reset ++ - const: sticky # Core sticky reset ++ - const: axi_m # AXI master reset ++ - const: axi_s # AXI slave reset ++ - const: ahb # AHB reset ++ - const: axi_m_sticky # AXI master sticky reset ++ - const: axi_s_sticky # AXI slave sticky reset ++ ++ - if: ++ properties: ++ compatible: ++ contains: ++ enum: + - qcom,pcie-msm8996 + then: + properties: +@@ -875,6 +910,7 @@ allOf: + - qcom,pcie-apq8064 + - qcom,pcie-apq8084 + - qcom,pcie-ipq4019 ++ - qcom,pcie-ipq5018 + - qcom,pcie-ipq6018 + - qcom,pcie-ipq8064 + - qcom,pcie-ipq8064-v2 diff --git a/target/linux/qualcommax/patches-6.6/0161-PCI-qcom-add-support-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0161-PCI-qcom-add-support-for-IPQ5018.patch new file mode 100644 index 0000000000..bd8f51b84c --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0161-PCI-qcom-add-support-for-IPQ5018.patch @@ -0,0 +1,77 @@ +From: Nitheesh Sekar +Subject: [PATCH] PCI: qcom: Add support for IPQ5018 +Date: Tue, 3 Oct 2023 17:38:44 +0530 + +Added a new compatible 'qcom,pcie-ipq5018' and modified +get_resources of 'ops 2_9_0' to get the clocks from the +device-tree. + +Co-developed-by: Anusha Rao +Signed-off-by: Anusha Rao +Co-developed-by: Devi Priya +Signed-off-by: Devi Priya +Signed-off-by: Nitheesh Sekar +--- + drivers/pci/controller/dwc/pcie-qcom.c | 22 ++++++++-------------- + 1 file changed, 8 insertions(+), 14 deletions(-) + +--- a/drivers/pci/controller/dwc/pcie-qcom.c ++++ b/drivers/pci/controller/dwc/pcie-qcom.c +@@ -202,8 +202,9 @@ struct qcom_pcie_resources_2_7_0 { + + #define QCOM_PCIE_2_9_0_MAX_CLOCKS 5 + struct qcom_pcie_resources_2_9_0 { +- struct clk_bulk_data clks[QCOM_PCIE_2_9_0_MAX_CLOCKS]; ++ struct clk_bulk_data *clks; + struct reset_control *rst; ++ int num_clks; + }; + + union qcom_pcie_resources { +@@ -1073,17 +1074,10 @@ static int qcom_pcie_get_resources_2_9_0 + struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; +- int ret; +- +- res->clks[0].id = "iface"; +- res->clks[1].id = "axi_m"; +- res->clks[2].id = "axi_s"; +- res->clks[3].id = "axi_bridge"; +- res->clks[4].id = "rchng"; + +- ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); +- if (ret < 0) +- return ret; ++ res->num_clks = devm_clk_bulk_get_all(dev, &res->clks); ++ if (res->num_clks < 0) ++ return res->num_clks; + + res->rst = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(res->rst)) +@@ -1096,7 +1090,7 @@ static void qcom_pcie_deinit_2_9_0(struc + { + struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0; + +- clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); ++ clk_bulk_disable_unprepare(res->num_clks, res->clks); + } + + static int qcom_pcie_init_2_9_0(struct qcom_pcie *pcie) +@@ -1125,7 +1119,7 @@ static int qcom_pcie_init_2_9_0(struct q + + usleep_range(2000, 2500); + +- return clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); ++ return clk_bulk_prepare_enable(res->num_clks, res->clks); + } + + static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) +@@ -1641,6 +1635,7 @@ static const struct of_device_id qcom_pc + { .compatible = "qcom,pcie-apq8064", .data = &cfg_2_1_0 }, + { .compatible = "qcom,pcie-apq8084", .data = &cfg_1_0_0 }, + { .compatible = "qcom,pcie-ipq4019", .data = &cfg_2_4_0 }, ++ { .compatible = "qcom,pcie-ipq5018", .data = &cfg_2_9_0 }, + { .compatible = "qcom,pcie-ipq6018", .data = &cfg_2_9_0 }, + { .compatible = "qcom,pcie-ipq8064", .data = &cfg_2_1_0 }, + { .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 }, diff --git a/target/linux/qualcommax/patches-6.6/0162-arm64-dts-qcom-IPQ5018-add-PCIe-related-nodes.patch b/target/linux/qualcommax/patches-6.6/0162-arm64-dts-qcom-IPQ5018-add-PCIe-related-nodes.patch new file mode 100644 index 0000000000..f51d103508 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0162-arm64-dts-qcom-IPQ5018-add-PCIe-related-nodes.patch @@ -0,0 +1,216 @@ +From: Nitheesh Sekar +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PCIe related nodes +Date: Tue, 3 Oct 2023 17:38:45 +0530 + +Add phy and controller nodes for PCIe0 and PCIe1. +PCIe0 is 2-lane Gen2 and PCIe1 is 1-lane Gen2. + +Signed-off-by: Nitheesh Sekar +Signed-off-by: George Moussalem +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 186 +++++++++++++++++++++++++- + 1 file changed, 184 insertions(+), 2 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -149,6 +149,42 @@ + status = "disabled"; + }; + ++ pcie1_phy: phy@7e000{ ++ compatible = "qcom,ipq5018-uniphy-pcie-phy"; ++ reg = <0x0007e000 0x800>; ++ ++ clocks = <&gcc GCC_PCIE1_PIPE_CLK>; ++ ++ resets = <&gcc GCC_PCIE1_PHY_BCR>, ++ <&gcc GCC_PCIE1PHY_PHY_BCR>; ++ ++ #clock-cells = <0>; ++ ++ #phy-cells = <0>; ++ ++ num-lanes = <1>; ++ ++ status = "disabled"; ++ }; ++ ++ pcie0_phy: phy@86000{ ++ compatible = "qcom,ipq5018-uniphy-pcie-phy"; ++ reg = <0x00086000 0x800>; ++ ++ clocks = <&gcc GCC_PCIE0_PIPE_CLK>; ++ ++ resets = <&gcc GCC_PCIE0_PHY_BCR>, ++ <&gcc GCC_PCIE0PHY_PHY_BCR>; ++ ++ #clock-cells = <0>; ++ ++ #phy-cells = <0>; ++ ++ num-lanes = <2>; ++ ++ status = "disabled"; ++ }; ++ + qfprom: qfprom@a0000 { + compatible = "qcom,ipq5018-qfprom", "qcom,qfprom"; + reg = <0xa0000 0x1000>; +@@ -283,8 +319,8 @@ + reg = <0x01800000 0x80000>; + clocks = <&xo_board_clk>, + <&sleep_clk>, +- <0>, +- <0>, ++ <&pcie0_phy>, ++ <&pcie1_phy>, + <0>, + <0>, + <0>, +@@ -501,6 +537,146 @@ + status = "disabled"; + }; + }; ++ ++ pcie1: pcie@80000000 { ++ compatible = "qcom,pcie-ipq5018"; ++ reg = <0x80000000 0xf1d>, ++ <0x80000f20 0xa8>, ++ <0x80001000 0x1000>, ++ <0x00078000 0x3000>, ++ <0x80100000 0x1000>; ++ reg-names = "dbi", ++ "elbi", ++ "atu", ++ "parf", ++ "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ max-link-speed = <2>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ phys = <&pcie1_phy>; ++ phy-names ="pciephy"; ++ ++ ranges = <0x81000000 0 0x80200000 0x80200000 0 0x00100000>, /* I/O */ ++ <0x82000000 0 0x80300000 0x80300000 0 0x10000000>; /* MEM */ ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 142 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 143 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 144 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 145 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ interrupts = ; ++ interrupt-names = "global_irq"; ++ ++ clocks = <&gcc GCC_SYS_NOC_PCIE1_AXI_CLK>, ++ <&gcc GCC_PCIE1_AXI_M_CLK>, ++ <&gcc GCC_PCIE1_AXI_S_CLK>, ++ <&gcc GCC_PCIE1_AHB_CLK>, ++ <&gcc GCC_PCIE1_AUX_CLK>, ++ <&gcc GCC_PCIE1_AXI_S_BRIDGE_CLK>; ++ clock-names = "iface", ++ "axi_m", ++ "axi_s", ++ "ahb", ++ "aux", ++ "axi_bridge"; ++ ++ resets = <&gcc GCC_PCIE1_PIPE_ARES>, ++ <&gcc GCC_PCIE1_SLEEP_ARES>, ++ <&gcc GCC_PCIE1_CORE_STICKY_ARES>, ++ <&gcc GCC_PCIE1_AXI_MASTER_ARES>, ++ <&gcc GCC_PCIE1_AXI_SLAVE_ARES>, ++ <&gcc GCC_PCIE1_AHB_ARES>, ++ <&gcc GCC_PCIE1_AXI_MASTER_STICKY_ARES>, ++ <&gcc GCC_PCIE1_AXI_SLAVE_STICKY_ARES>; ++ reset-names = "pipe", ++ "sleep", ++ "sticky", ++ "axi_m", ++ "axi_s", ++ "ahb", ++ "axi_m_sticky", ++ "axi_s_sticky"; ++ ++ msi-map = <0x0 &v2m0 0x0 0xff8>; ++ status = "disabled"; ++ }; ++ ++ pcie0: pcie@a0000000 { ++ compatible = "qcom,pcie-ipq5018"; ++ reg = <0xa0000000 0xf1d>, ++ <0xa0000f20 0xa8>, ++ <0xa0001000 0x1000>, ++ <0x00080000 0x3000>, ++ <0xa0100000 0x1000>; ++ reg-names = "dbi", ++ "elbi", ++ "atu", ++ "parf", ++ "config"; ++ device_type = "pci"; ++ linux,pci-domain = <1>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <2>; ++ max-link-speed = <2>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ phys = <&pcie0_phy>; ++ phy-names ="pciephy"; ++ ++ ranges = <0x81000000 0 0xa0200000 0xa0200000 0 0x00100000>, /* I/O */ ++ <0x82000000 0 0xa0300000 0xa0300000 0 0x10000000>; /* MEM */ ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 78 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 79 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 83 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ interrupts = ; ++ interrupt-names = "global_irq"; ++ ++ clocks = <&gcc GCC_SYS_NOC_PCIE0_AXI_CLK>, ++ <&gcc GCC_PCIE0_AXI_M_CLK>, ++ <&gcc GCC_PCIE0_AXI_S_CLK>, ++ <&gcc GCC_PCIE0_AHB_CLK>, ++ <&gcc GCC_PCIE0_AUX_CLK>, ++ <&gcc GCC_PCIE0_AXI_S_BRIDGE_CLK>; ++ clock-names = "iface", ++ "axi_m", ++ "axi_s", ++ "ahb", ++ "aux", ++ "axi_bridge"; ++ ++ resets = <&gcc GCC_PCIE0_PIPE_ARES>, ++ <&gcc GCC_PCIE0_SLEEP_ARES>, ++ <&gcc GCC_PCIE0_CORE_STICKY_ARES>, ++ <&gcc GCC_PCIE0_AXI_MASTER_ARES>, ++ <&gcc GCC_PCIE0_AXI_SLAVE_ARES>, ++ <&gcc GCC_PCIE0_AHB_ARES>, ++ <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>, ++ <&gcc GCC_PCIE0_AXI_SLAVE_STICKY_ARES>; ++ reset-names = "pipe", ++ "sleep", ++ "sticky", ++ "axi_m", ++ "axi_s", ++ "ahb", ++ "axi_m_sticky", ++ "axi_s_sticky"; ++ ++ msi-map = <0x0 &v2m0 0x0 0xff8>; ++ status = "disabled"; ++ }; + }; + + thermal-zones { diff --git a/target/linux/qualcommax/patches-6.6/0301-dt-bindings-mfd-qcom-tcsr-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0301-dt-bindings-mfd-qcom-tcsr-add-IPQ5018-compatible.patch new file mode 100644 index 0000000000..2faaa1d10e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0301-dt-bindings-mfd-qcom-tcsr-add-IPQ5018-compatible.patch @@ -0,0 +1,18 @@ +From: George Moussalem +Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add IPQ5018 compatible +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +Document the qcom,tcsr-ipq5018 compatible. + +Signed-off-by: George Moussalem +--- +--- a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml ++++ b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml +@@ -33,6 +33,7 @@ properties: + - qcom,sm8450-tcsr + - qcom,tcsr-apq8064 + - qcom,tcsr-apq8084 ++ - qcom,tcsr-ipq5018 + - qcom,tcsr-ipq5332 + - qcom,tcsr-ipq8064 + - qcom,tcsr-ipq8074 diff --git a/target/linux/qualcommax/patches-6.6/0302-arm64-dts-qcom-IPQ5018-add-TCSR-node.patch b/target/linux/qualcommax/patches-6.6/0302-arm64-dts-qcom-IPQ5018-add-TCSR-node.patch new file mode 100644 index 0000000000..7a0031666d --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0302-arm64-dts-qcom-IPQ5018-add-TCSR-node.patch @@ -0,0 +1,22 @@ +From: George Moussalem +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add TCSR node +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +Add TCSR node. + +Signed-off-by: George Moussalem +--- +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -337,6 +337,11 @@ + #hwlock-cells = <1>; + }; + ++ tcsr: syscon@1937000 { ++ compatible = "qcom,tcsr-ipq5018", "syscon", "simple-mfd"; ++ reg = <0x01937000 0x21000>; ++ }; ++ + sdhc_1: mmc@7804000 { + compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5"; + reg = <0x7804000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.6/0303-arm64-dts-qcom-IPQ5018-enable-the-download-mode-support.patch b/target/linux/qualcommax/patches-6.6/0303-arm64-dts-qcom-IPQ5018-enable-the-download-mode-support.patch new file mode 100644 index 0000000000..265695d1c4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0303-arm64-dts-qcom-IPQ5018-enable-the-download-mode-support.patch @@ -0,0 +1,19 @@ +From: George Moussalem +Subject: [PATCH] arm64: dts: qcom: ipq5018: enable the download mode support +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +IPQ5018 also supports the download mode to collect the RAM dumps if system crashes, to perform +the post mortem analysis. Add support for the same. + +Signed-off-by: George Moussalem +--- +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -82,6 +82,7 @@ + scm { + compatible = "qcom,scm-ipq5018", "qcom,scm"; + qcom,sdi-enabled; ++ qcom,dload-mode = <&tcsr 0x6100>; + }; + }; + diff --git a/target/linux/qualcommax/patches-6.6/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch new file mode 100644 index 0000000000..5705028931 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch @@ -0,0 +1,22 @@ +From: George Moussalem +Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add IPQ5018 compatible +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +Add compatible for IPQ5018. + +Signed-off-by: George Moussalem +--- +--- a/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml ++++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml +@@ -11,7 +11,10 @@ maintainers: + + properties: + compatible: +- const: qcom,ipq6018-pwm ++ items: ++ - enum: ++ - qcom,ipq5018-pwm ++ - const: qcom,ipq6018-pwm + + reg: + description: Offset of PWM register in the TCSR block. diff --git a/target/linux/qualcommax/patches-6.6/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch b/target/linux/qualcommax/patches-6.6/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch new file mode 100644 index 0000000000..476e5f6ba1 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch @@ -0,0 +1,65 @@ +From: George Moussalem +Subject: [PATCH] pinctrl: qcom: IPQ5018: update pwm groups +Date: Wed, 27 Nov 2024 09:14:11 +0400 + +GPIO 1, 30, and 46 are used to control PWM1, PWM3, and PWM0 respectively which +in turn drive the PWM led, so let's update the pwm# and pingroups accordingly. + +Signed-off-by: George Moussalem +--- +--- a/drivers/pinctrl/qcom/pinctrl-ipq5018.c ++++ b/drivers/pinctrl/qcom/pinctrl-ipq5018.c +@@ -541,7 +541,7 @@ static const char * const qdss_tracectl_ + }; + + static const char * const pwm0_groups[] = { +- "gpio42", ++ "gpio42", "gpio46", + }; + + static const char * const qdss_cti_trig_out_b0_groups[] = { +@@ -549,7 +549,7 @@ static const char * const qdss_cti_trig_ + }; + + static const char * const pwm1_groups[] = { +- "gpio43", ++ "gpio43", "gpio1", + }; + + static const char * const qdss_cti_trig_in_b0_groups[] = { +@@ -565,7 +565,7 @@ static const char * const qdss_cti_trig_ + }; + + static const char * const pwm3_groups[] = { +- "gpio45", ++ "gpio45", "gpio30", + }; + + static const char * const qdss_cti_trig_in_b1_groups[] = { +@@ -679,7 +679,7 @@ static const struct pinfunction ipq5018_ + + static const struct msm_pingroup ipq5018_groups[] = { + PINGROUP(0, atest_char, _, qdss_cti_trig_out_a0, wci_txd, wci_rxd, xfem, _, _, _), +- PINGROUP(1, atest_char, _, qdss_cti_trig_in_a0, wci_txd, wci_rxd, xfem, _, _, _), ++ PINGROUP(1, atest_char, pwm1, qdss_cti_trig_in_a0, wci_txd, wci_rxd, xfem, _, _, _), + PINGROUP(2, atest_char, _, qdss_cti_trig_out_a1, wci_txd, wci_rxd, xfem, _, _, _), + PINGROUP(3, atest_char, _, qdss_cti_trig_in_a1, wci_txd, wci_rxd, xfem, _, _, _), + PINGROUP(4, sdc1_data, qspi_data, blsp1_spi1, btss, dbg_out, qdss_traceclk_a, _, burn0, _), +@@ -708,7 +708,7 @@ static const struct msm_pingroup ipq5018 + PINGROUP(27, audio_txmclk, wsa_swrm, audio_txmclk, blsp2_spi, btss, _, qdss_tracedata_b, _, _), + PINGROUP(28, audio_txbclk, wsa_swrm, blsp0_uart1, btss, qdss_tracedata_b, _, _, _, _), + PINGROUP(29, audio_txfsync, _, blsp0_uart1, _, qdss_tracedata_b, _, _, _, _), +- PINGROUP(30, audio_txd, led2, led0, _, _, _, _, _, _), ++ PINGROUP(30, audio_txd, led2, led0, pwm3, _, _, _, _, _), + PINGROUP(31, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _, _), + PINGROUP(32, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _, _), + PINGROUP(33, blsp2_i2c0, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _), +@@ -724,7 +724,7 @@ static const struct msm_pingroup ipq5018 + PINGROUP(43, pwm1, qdss_cti_trig_in_b0, wci_txd, wci_rxd, xfem, _, _, _, _), + PINGROUP(44, pwm2, qdss_cti_trig_out_b1, wci_txd, wci_rxd, xfem, _, _, _, _), + PINGROUP(45, pwm3, qdss_cti_trig_in_b1, wci_txd, wci_rxd, xfem, _, _, _, _), +- PINGROUP(46, led0, _, _, _, _, _, _, _, _), ++ PINGROUP(46, led0, pwm0, _, _, _, _, _, _, _), + }; + + static const struct msm_pinctrl_soc_data ipq5018_pinctrl = { diff --git a/target/linux/qualcommax/patches-6.6/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch b/target/linux/qualcommax/patches-6.6/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch new file mode 100644 index 0000000000..d60e916971 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch @@ -0,0 +1,27 @@ +From: George Moussalem +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PWM node +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +Add PWM node. + +Signed-off-by: George Moussalem +--- +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -343,6 +343,16 @@ + reg = <0x01937000 0x21000>; + }; + ++ pwm: pwm@1941010 { ++ compatible = "qcom,ipq5018-pwm", "qcom,ipq6018-pwm"; ++ reg = <0x01941010 0x20>; ++ clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clock-rates = <100000000>; ++ #pwm-cells = <2>; ++ status = "disabled"; ++ }; ++ + sdhc_1: mmc@7804000 { + compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5"; + reg = <0x7804000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.6/0324-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch b/target/linux/qualcommax/patches-6.6/0324-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch new file mode 100644 index 0000000000..57d434271f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0324-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch @@ -0,0 +1,41 @@ +From: George Moussalem +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add crypto nodes +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +Add dma controller and crypto nodes. + +Signed-off-by: George Moussalem +--- +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -297,6 +297,30 @@ + #thermal-sensor-cells = <1>; + }; + ++ cryptobam: dma-controller@704000 { ++ compatible = "qcom,bam-v1.7.0"; ++ reg = <0x00704000 0x20000>; ++ interrupts = ; ++ clocks = <&gcc GCC_CRYPTO_AHB_CLK>; ++ clock-names = "bam_clk"; ++ #dma-cells = <1>; ++ qcom,ee = <1>; ++ qcom,controlled-remotely; ++ status = "disabled"; ++ }; ++ ++ crypto: crypto@73a000 { ++ compatible = "qcom,crypto-v5.1"; ++ reg = <0x0073a000 0x6000>; ++ clocks = <&gcc GCC_CRYPTO_AHB_CLK>, ++ <&gcc GCC_CRYPTO_AXI_CLK>, ++ <&gcc GCC_CRYPTO_CLK>; ++ clock-names = "iface", "bus", "core"; ++ dmas = <&cryptobam 2>, <&cryptobam 3>; ++ dma-names = "rx", "tx"; ++ status = "disabled"; ++ }; ++ + tlmm: pinctrl@1000000 { + compatible = "qcom,ipq5018-tlmm"; + reg = <0x01000000 0x300000>; diff --git a/target/linux/qualcommax/patches-6.6/0337-arm64-dts-qcom-ipq5018-Add-PRNG-node.patch b/target/linux/qualcommax/patches-6.6/0337-arm64-dts-qcom-ipq5018-Add-PRNG-node.patch new file mode 100644 index 0000000000..87d9bdb270 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0337-arm64-dts-qcom-ipq5018-Add-PRNG-node.patch @@ -0,0 +1,25 @@ +From: George Moussalem +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PRNG node +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +Add PRNG node. + +Signed-off-by: George Moussalem +--- +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -258,6 +258,14 @@ + }; + }; + ++ prng: rng@e3000 { ++ compatible = "qcom,prng-ee"; ++ reg = <0x000e3000 0x1000>; ++ clocks = <&gcc GCC_PRNG_AHB_CLK>; ++ clock-names = "core"; ++ status = "disabled"; ++ }; ++ + tsens: thermal-sensor@4a9000 { + compatible = "qcom,ipq5018-tsens"; + reg = <0x4a9000 0x1000>, /* TM */ diff --git a/target/linux/qualcommax/patches-6.6/0339-arm64-dts-qcom-ipq5018-Add-QUP1-UART2-node.patch b/target/linux/qualcommax/patches-6.6/0339-arm64-dts-qcom-ipq5018-Add-QUP1-UART2-node.patch new file mode 100644 index 0000000000..72085061f8 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0339-arm64-dts-qcom-ipq5018-Add-QUP1-UART2-node.patch @@ -0,0 +1,27 @@ +From: George Moussalem +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add QUP1-UART2 node +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +Add QUP1-UART2 node. + +Signed-off-by: George Moussalem +--- +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -422,6 +422,16 @@ + status = "disabled"; + }; + ++ blsp1_uart2: serial@78b0000 { ++ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; ++ reg = <0x078b0000 0x200>; ++ interrupts = ; ++ clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, ++ <&gcc GCC_BLSP1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ status = "disabled"; ++ }; ++ + blsp1_spi1: spi@78b5000 { + compatible = "qcom,spi-qup-v2.2.1"; + #address-cells = <1>; diff --git a/target/linux/qualcommax/patches-6.6/0340-arm64-dts-qcom-ipq5018-Add-QUP3-I2C-node.patch b/target/linux/qualcommax/patches-6.6/0340-arm64-dts-qcom-ipq5018-Add-QUP3-I2C-node.patch new file mode 100644 index 0000000000..5447e78fab --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0340-arm64-dts-qcom-ipq5018-Add-QUP3-I2C-node.patch @@ -0,0 +1,32 @@ +From: George Moussalem +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add QUP3 I2C node +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +Add QUP3-I2C node. + +Signed-off-by: George Moussalem +--- +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -446,6 +446,21 @@ + status = "disabled"; + }; + ++ blsp1_i2c3: i2c@78b7000 { ++ compatible = "qcom,i2c-qup-v2.2.1"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x078b7000 0x600>; ++ interrupts = ; ++ clocks = <&gcc GCC_BLSP1_QUP3_I2C_APPS_CLK>, ++ <&gcc GCC_BLSP1_AHB_CLK>; ++ clock-names = "core", "iface"; ++ clock-frequency = <400000>; ++ dmas = <&blsp_dma 9>, <&blsp_dma 8>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ + usb: usb@8af8800 { + compatible = "qcom,ipq5018-dwc3", "qcom,dwc3"; + reg = <0x08af8800 0x400>; diff --git a/target/linux/qualcommax/patches-6.6/0401-spi-dt-bindings-Introduce-qcom-spi-qpic-snand.patch b/target/linux/qualcommax/patches-6.6/0401-spi-dt-bindings-Introduce-qcom-spi-qpic-snand.patch new file mode 100644 index 0000000000..433db36871 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0401-spi-dt-bindings-Introduce-qcom-spi-qpic-snand.patch @@ -0,0 +1,97 @@ +From: Md Sadre Alam +Date: Sun, 22 Sep 2024 17:03:44 +0530 +Subject: [PATCH] spi: dt-bindings: Introduce qcom,spi-qpic-snand + +Document the QPIC-SPI-NAND flash controller present in the IPQ SoCs. +It can work both in serial and parallel mode and supports typical +SPI-NAND page cache operations. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Md Sadre Alam +--- +--- /dev/null ++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml +@@ -0,0 +1,83 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/spi/qcom,spi-qpic-snand.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Qualcomm QPIC NAND controller ++ ++maintainers: ++ - Md sadre Alam ++ ++description: ++ The QCOM QPIC-SPI-NAND flash controller is an extended version of ++ the QCOM QPIC NAND flash controller. It can work both in serial ++ and parallel mode. It supports typical SPI-NAND page cache ++ operations in single, dual or quad IO mode with pipelined ECC ++ encoding/decoding using the QPIC ECC HW engine. ++ ++allOf: ++ - $ref: /schemas/spi/spi-controller.yaml# ++ ++properties: ++ compatible: ++ enum: ++ - qcom,spi-qpic-snand ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ maxItems: 3 ++ ++ clock-names: ++ items: ++ - const: core ++ - const: aon ++ - const: iom ++ ++ dmas: ++ items: ++ - description: tx DMA channel ++ - description: rx DMA channel ++ - description: cmd DMA channel ++ ++ dma-names: ++ items: ++ - const: tx ++ - const: rx ++ - const: cmd ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ #include ++ spi@79b0000 { ++ compatible = "qcom,spi-qpic-snand"; ++ reg = <0x1ac00000 0x800>; ++ ++ clocks = <&gcc GCC_QPIC_CLK>, ++ <&gcc GCC_QPIC_AHB_CLK>, ++ <&gcc GCC_QPIC_IO_MACRO_CLK>; ++ clock-names = "core", "aon", "iom"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ flash@0 { ++ compatible = "spi-nand"; ++ reg = <0>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ nand-ecc-engine = <&qpic_nand>; ++ nand-ecc-strength = <4>; ++ nand-ecc-step-size = <512>; ++ }; ++ }; diff --git a/target/linux/qualcommax/patches-6.6/0402-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch b/target/linux/qualcommax/patches-6.6/0402-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch new file mode 100644 index 0000000000..91dceaa3a6 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0402-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch @@ -0,0 +1,983 @@ +From: Md Sadre Alam +Date: Sun, 22 Sep 2024 17:03:45 +0530 +Subject: [PATCH] mtd: rawnand: qcom: cleanup qcom_nandc driver + +cleanup qcom_nandc driver as below + +- Remove register value indirection api + +- Remove set_reg() api + +- Convert read_loc_first & read_loc_last macro to function + +- Renamed multiple variables + +Signed-off-by: Md Sadre Alam +--- +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -189,17 +189,6 @@ + #define ECC_BCH_4BIT BIT(2) + #define ECC_BCH_8BIT BIT(3) + +-#define nandc_set_read_loc_first(chip, reg, cw_offset, read_size, is_last_read_loc) \ +-nandc_set_reg(chip, reg, \ +- ((cw_offset) << READ_LOCATION_OFFSET) | \ +- ((read_size) << READ_LOCATION_SIZE) | \ +- ((is_last_read_loc) << READ_LOCATION_LAST)) +- +-#define nandc_set_read_loc_last(chip, reg, cw_offset, read_size, is_last_read_loc) \ +-nandc_set_reg(chip, reg, \ +- ((cw_offset) << READ_LOCATION_OFFSET) | \ +- ((read_size) << READ_LOCATION_SIZE) | \ +- ((is_last_read_loc) << READ_LOCATION_LAST)) + /* + * Returns the actual register address for all NAND_DEV_ registers + * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) +@@ -257,8 +246,6 @@ nandc_set_reg(chip, reg, \ + * @tx_sgl_start - start index in data sgl for tx. + * @rx_sgl_pos - current index in data sgl for rx. + * @rx_sgl_start - start index in data sgl for rx. +- * @wait_second_completion - wait for second DMA desc completion before making +- * the NAND transfer completion. + */ + struct bam_transaction { + struct bam_cmd_element *bam_ce; +@@ -275,7 +262,6 @@ struct bam_transaction { + u32 tx_sgl_start; + u32 rx_sgl_pos; + u32 rx_sgl_start; +- bool wait_second_completion; + }; + + /* +@@ -471,9 +457,9 @@ struct qcom_op { + unsigned int data_instr_idx; + unsigned int rdy_timeout_ms; + unsigned int rdy_delay_ns; +- u32 addr1_reg; +- u32 addr2_reg; +- u32 cmd_reg; ++ __le32 addr1_reg; ++ __le32 addr2_reg; ++ __le32 cmd_reg; + u8 flag; + }; + +@@ -549,17 +535,17 @@ struct qcom_nand_host { + * among different NAND controllers. + * @ecc_modes - ecc mode for NAND + * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset +- * @is_bam - whether NAND controller is using BAM +- * @is_qpic - whether NAND CTRL is part of qpic IP +- * @qpic_v2 - flag to indicate QPIC IP version 2 ++ * @supports_bam - whether NAND controller is using BAM ++ * @nandc_part_of_qpic - whether NAND controller is part of qpic IP ++ * @qpic_version2 - flag to indicate QPIC IP version 2 + * @use_codeword_fixup - whether NAND has different layout for boot partitions + */ + struct qcom_nandc_props { + u32 ecc_modes; + u32 dev_cmd_reg_start; +- bool is_bam; +- bool is_qpic; +- bool qpic_v2; ++ bool supports_bam; ++ bool nandc_part_of_qpic; ++ bool qpic_version2; + bool use_codeword_fixup; + }; + +@@ -613,19 +599,11 @@ static void clear_bam_transaction(struct + { + struct bam_transaction *bam_txn = nandc->bam_txn; + +- if (!nandc->props->is_bam) ++ if (!nandc->props->supports_bam) + return; + +- bam_txn->bam_ce_pos = 0; +- bam_txn->bam_ce_start = 0; +- bam_txn->cmd_sgl_pos = 0; +- bam_txn->cmd_sgl_start = 0; +- bam_txn->tx_sgl_pos = 0; +- bam_txn->tx_sgl_start = 0; +- bam_txn->rx_sgl_pos = 0; +- bam_txn->rx_sgl_start = 0; ++ memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8); + bam_txn->last_data_desc = NULL; +- bam_txn->wait_second_completion = false; + + sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * + QPIC_PER_CW_CMD_SGL); +@@ -640,17 +618,7 @@ static void qpic_bam_dma_done(void *data + { + struct bam_transaction *bam_txn = data; + +- /* +- * In case of data transfer with NAND, 2 callbacks will be generated. +- * One for command channel and another one for data channel. +- * If current transaction has data descriptors +- * (i.e. wait_second_completion is true), then set this to false +- * and wait for second DMA descriptor completion. +- */ +- if (bam_txn->wait_second_completion) +- bam_txn->wait_second_completion = false; +- else +- complete(&bam_txn->txn_done); ++ complete(&bam_txn->txn_done); + } + + static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) +@@ -676,10 +644,9 @@ static inline void nandc_write(struct qc + iowrite32(val, nandc->base + offset); + } + +-static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, +- bool is_cpu) ++static inline void nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) + { +- if (!nandc->props->is_bam) ++ if (!nandc->props->supports_bam) + return; + + if (is_cpu) +@@ -694,93 +661,90 @@ static inline void nandc_read_buffer_syn + DMA_FROM_DEVICE); + } + +-static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) +-{ +- switch (offset) { +- case NAND_FLASH_CMD: +- return ®s->cmd; +- case NAND_ADDR0: +- return ®s->addr0; +- case NAND_ADDR1: +- return ®s->addr1; +- case NAND_FLASH_CHIP_SELECT: +- return ®s->chip_sel; +- case NAND_EXEC_CMD: +- return ®s->exec; +- case NAND_FLASH_STATUS: +- return ®s->clrflashstatus; +- case NAND_DEV0_CFG0: +- return ®s->cfg0; +- case NAND_DEV0_CFG1: +- return ®s->cfg1; +- case NAND_DEV0_ECC_CFG: +- return ®s->ecc_bch_cfg; +- case NAND_READ_STATUS: +- return ®s->clrreadstatus; +- case NAND_DEV_CMD1: +- return ®s->cmd1; +- case NAND_DEV_CMD1_RESTORE: +- return ®s->orig_cmd1; +- case NAND_DEV_CMD_VLD: +- return ®s->vld; +- case NAND_DEV_CMD_VLD_RESTORE: +- return ®s->orig_vld; +- case NAND_EBI2_ECC_BUF_CFG: +- return ®s->ecc_buf_cfg; +- case NAND_READ_LOCATION_0: +- return ®s->read_location0; +- case NAND_READ_LOCATION_1: +- return ®s->read_location1; +- case NAND_READ_LOCATION_2: +- return ®s->read_location2; +- case NAND_READ_LOCATION_3: +- return ®s->read_location3; +- case NAND_READ_LOCATION_LAST_CW_0: +- return ®s->read_location_last0; +- case NAND_READ_LOCATION_LAST_CW_1: +- return ®s->read_location_last1; +- case NAND_READ_LOCATION_LAST_CW_2: +- return ®s->read_location_last2; +- case NAND_READ_LOCATION_LAST_CW_3: +- return ®s->read_location_last3; +- default: +- return NULL; +- } +-} +- +-static void nandc_set_reg(struct nand_chip *chip, int offset, +- u32 val) +-{ +- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); +- struct nandc_regs *regs = nandc->regs; +- __le32 *reg; +- +- reg = offset_to_nandc_reg(regs, offset); +- +- if (reg) +- *reg = cpu_to_le32(val); +-} +- + /* Helper to check the code word, whether it is last cw or not */ + static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) + { + return cw == (ecc->steps - 1); + } + ++/** ++ * nandc_set_read_loc_first() - to set read location first register ++ * @chip: NAND Private Flash Chip Data ++ * @reg_base: location register base ++ * @cw_offset: code word offset ++ * @read_size: code word read length ++ * @is_last_read_loc: is this the last read location ++ * ++ * This function will set location register value ++ */ ++static void nandc_set_read_loc_first(struct nand_chip *chip, ++ int reg_base, u32 cw_offset, ++ u32 read_size, u32 is_last_read_loc) ++{ ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ __le32 locreg_val; ++ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | ++ ((read_size) << READ_LOCATION_SIZE) | ++ ((is_last_read_loc) << READ_LOCATION_LAST)); ++ ++ locreg_val = cpu_to_le32(val); ++ ++ if (reg_base == NAND_READ_LOCATION_0) ++ nandc->regs->read_location0 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_1) ++ nandc->regs->read_location1 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_2) ++ nandc->regs->read_location2 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_3) ++ nandc->regs->read_location3 = locreg_val; ++} ++ ++/** ++ * nandc_set_read_loc_last - to set read location last register ++ * @chip: NAND Private Flash Chip Data ++ * @reg_base: location register base ++ * @cw_offset: code word offset ++ * @read_size: code word read length ++ * @is_last_read_loc: is this the last read location ++ * ++ * This function will set location last register value ++ */ ++static void nandc_set_read_loc_last(struct nand_chip *chip, ++ int reg_base, u32 cw_offset, ++ u32 read_size, u32 is_last_read_loc) ++{ ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); ++ __le32 locreg_val; ++ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | ++ ((read_size) << READ_LOCATION_SIZE) | ++ ((is_last_read_loc) << READ_LOCATION_LAST)); ++ ++ locreg_val = cpu_to_le32(val); ++ ++ if (reg_base == NAND_READ_LOCATION_LAST_CW_0) ++ nandc->regs->read_location_last0 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_LAST_CW_1) ++ nandc->regs->read_location_last1 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_LAST_CW_2) ++ nandc->regs->read_location_last2 = locreg_val; ++ else if (reg_base == NAND_READ_LOCATION_LAST_CW_3) ++ nandc->regs->read_location_last3 = locreg_val; ++} ++ + /* helper to configure location register values */ + static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg, +- int cw_offset, int read_size, int is_last_read_loc) ++ u32 cw_offset, u32 read_size, u32 is_last_read_loc) + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int reg_base = NAND_READ_LOCATION_0; + +- if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw)) ++ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) + reg_base = NAND_READ_LOCATION_LAST_CW_0; + + reg_base += reg * 4; + +- if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw)) ++ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) + return nandc_set_read_loc_last(chip, reg_base, cw_offset, + read_size, is_last_read_loc); + else +@@ -792,12 +756,13 @@ static void nandc_set_read_loc(struct na + static void set_address(struct qcom_nand_host *host, u16 column, int page) + { + struct nand_chip *chip = &host->chip; ++ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + +- nandc_set_reg(chip, NAND_ADDR0, page << 16 | column); +- nandc_set_reg(chip, NAND_ADDR1, page >> 16 & 0xff); ++ nandc->regs->addr0 = cpu_to_le32(page << 16 | column); ++ nandc->regs->addr1 = cpu_to_le32(page >> 16 & 0xff); + } + + /* +@@ -811,41 +776,43 @@ static void set_address(struct qcom_nand + static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, int cw) + { + struct nand_chip *chip = &host->chip; +- u32 cmd, cfg0, cfg1, ecc_bch_cfg; ++ __le32 cmd, cfg0, cfg1, ecc_bch_cfg; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + + if (read) { + if (host->use_ecc) +- cmd = OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; ++ cmd = cpu_to_le32(OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE); + else +- cmd = OP_PAGE_READ | PAGE_ACC | LAST_PAGE; ++ cmd = cpu_to_le32(OP_PAGE_READ | PAGE_ACC | LAST_PAGE); + } else { +- cmd = OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; ++ cmd = cpu_to_le32(OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE); + } + + if (host->use_ecc) { +- cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) | +- (num_cw - 1) << CW_PER_PAGE; ++ cfg0 = cpu_to_le32((host->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE); + +- cfg1 = host->cfg1; +- ecc_bch_cfg = host->ecc_bch_cfg; ++ cfg1 = cpu_to_le32(host->cfg1); ++ ecc_bch_cfg = cpu_to_le32(host->ecc_bch_cfg); + } else { +- cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) | +- (num_cw - 1) << CW_PER_PAGE; ++ cfg0 = cpu_to_le32((host->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE); + +- cfg1 = host->cfg1_raw; +- ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ cfg1 = cpu_to_le32(host->cfg1_raw); ++ ecc_bch_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE); + } + +- nandc_set_reg(chip, NAND_FLASH_CMD, cmd); +- nandc_set_reg(chip, NAND_DEV0_CFG0, cfg0); +- nandc_set_reg(chip, NAND_DEV0_CFG1, cfg1); +- nandc_set_reg(chip, NAND_DEV0_ECC_CFG, ecc_bch_cfg); +- if (!nandc->props->qpic_v2) +- nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg); +- nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus); +- nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus); +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); ++ nandc->regs->cmd = cmd; ++ nandc->regs->cfg0 = cfg0; ++ nandc->regs->cfg1 = cfg1; ++ nandc->regs->ecc_bch_cfg = ecc_bch_cfg; ++ ++ if (!nandc->props->qpic_version2) ++ nandc->regs->ecc_buf_cfg = cpu_to_le32(host->ecc_buf_cfg); ++ ++ nandc->regs->clrflashstatus = cpu_to_le32(host->clrflashstatus); ++ nandc->regs->clrreadstatus = cpu_to_le32(host->clrreadstatus); ++ nandc->regs->exec = cpu_to_le32(1); + + if (read) + nandc_set_read_loc(chip, cw, 0, 0, host->use_ecc ? +@@ -1121,7 +1088,7 @@ static int read_reg_dma(struct qcom_nand + if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) + first = dev_cmd_reg_addr(nandc, first); + +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, + num_regs, flags); + +@@ -1136,25 +1103,16 @@ static int read_reg_dma(struct qcom_nand + * write_reg_dma: prepares a descriptor to write a given number of + * contiguous registers + * ++ * @vaddr: contnigeous memory from where register value will ++ * be written + * @first: offset of the first register in the contiguous block + * @num_regs: number of registers to write + * @flags: flags to control DMA descriptor preparation + */ +-static int write_reg_dma(struct qcom_nand_controller *nandc, int first, +- int num_regs, unsigned int flags) ++static int write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, ++ int first, int num_regs, unsigned int flags) + { + bool flow_control = false; +- struct nandc_regs *regs = nandc->regs; +- void *vaddr; +- +- vaddr = offset_to_nandc_reg(regs, first); +- +- if (first == NAND_ERASED_CW_DETECT_CFG) { +- if (flags & NAND_ERASED_CW_SET) +- vaddr = ®s->erased_cw_detect_cfg_set; +- else +- vaddr = ®s->erased_cw_detect_cfg_clr; +- } + + if (first == NAND_EXEC_CMD) + flags |= NAND_BAM_NWD; +@@ -1165,7 +1123,7 @@ static int write_reg_dma(struct qcom_nan + if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); + +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, + num_regs, flags); + +@@ -1188,7 +1146,7 @@ static int write_reg_dma(struct qcom_nan + static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, + const u8 *vaddr, int size, unsigned int flags) + { +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); + + return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); +@@ -1206,7 +1164,7 @@ static int read_data_dma(struct qcom_nan + static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, + const u8 *vaddr, int size, unsigned int flags) + { +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); + + return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); +@@ -1220,13 +1178,14 @@ static void config_nand_page_read(struct + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, NAND_ADDR0, 2, 0); +- write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); +- if (!nandc->props->qpic_v2) +- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); +- write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); +- write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, +- NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); ++ write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ if (!nandc->props->qpic_version2) ++ write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); + } + + /* +@@ -1239,16 +1198,16 @@ config_nand_cw_read(struct nand_chip *ch + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + +- int reg = NAND_READ_LOCATION_0; ++ __le32 *reg = &nandc->regs->read_location0; + +- if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw)) +- reg = NAND_READ_LOCATION_LAST_CW_0; ++ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) ++ reg = &nandc->regs->read_location_last0; + +- if (nandc->props->is_bam) +- write_reg_dma(nandc, reg, 4, NAND_BAM_NEXT_SGL); ++ if (nandc->props->supports_bam) ++ write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + if (use_ecc) { + read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); +@@ -1279,10 +1238,10 @@ static void config_nand_page_write(struc + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, NAND_ADDR0, 2, 0); +- write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); +- if (!nandc->props->qpic_v2) +- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, ++ write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); ++ write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ if (!nandc->props->qpic_version2) ++ write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, + NAND_BAM_NEXT_SGL); + } + +@@ -1294,13 +1253,13 @@ static void config_nand_cw_write(struct + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); +- write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); + } + + /* helpers to submit/free our list of dma descriptors */ +@@ -1311,7 +1270,7 @@ static int submit_descs(struct qcom_nand + struct bam_transaction *bam_txn = nandc->bam_txn; + int ret = 0; + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { + ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); + if (ret) +@@ -1336,14 +1295,9 @@ static int submit_descs(struct qcom_nand + list_for_each_entry(desc, &nandc->desc_list, node) + cookie = dmaengine_submit(desc->dma_desc); + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; + bam_txn->last_cmd_desc->callback_param = bam_txn; +- if (bam_txn->last_data_desc) { +- bam_txn->last_data_desc->callback = qpic_bam_dma_done; +- bam_txn->last_data_desc->callback_param = bam_txn; +- bam_txn->wait_second_completion = true; +- } + + dma_async_issue_pending(nandc->tx_chan); + dma_async_issue_pending(nandc->rx_chan); +@@ -1365,7 +1319,7 @@ err_unmap_free_desc: + list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { + list_del(&desc->node); + +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + dma_unmap_sg(nandc->dev, desc->bam_sgl, + desc->sgl_cnt, desc->dir); + else +@@ -1382,7 +1336,7 @@ err_unmap_free_desc: + static void clear_read_regs(struct qcom_nand_controller *nandc) + { + nandc->reg_read_pos = 0; +- nandc_read_buffer_sync(nandc, false); ++ nandc_dev_to_mem(nandc, false); + } + + /* +@@ -1446,7 +1400,7 @@ static int check_flash_errors(struct qco + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int i; + +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); +@@ -1476,7 +1430,7 @@ qcom_nandc_read_cw_raw(struct mtd_info * + clear_read_regs(nandc); + host->use_ecc = false; + +- if (nandc->props->qpic_v2) ++ if (nandc->props->qpic_version2) + raw_cw = ecc->steps - 1; + + clear_bam_transaction(nandc); +@@ -1497,7 +1451,7 @@ qcom_nandc_read_cw_raw(struct mtd_info * + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + nandc_set_read_loc(chip, cw, 0, read_loc, data_size1, 0); + read_loc += data_size1; + +@@ -1621,7 +1575,7 @@ static int parse_read_errors(struct qcom + u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; + + buf = (struct read_stats *)nandc->reg_read_buf; +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + + for (i = 0; i < ecc->steps; i++, buf++) { + u32 flash, buffer, erased_cw; +@@ -1734,7 +1688,7 @@ static int read_page_ecc(struct qcom_nan + oob_size = host->ecc_bytes_hw + host->spare_bytes; + } + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + if (data_buf && oob_buf) { + nandc_set_read_loc(chip, i, 0, 0, data_size, 0); + nandc_set_read_loc(chip, i, 1, data_size, +@@ -2455,14 +2409,14 @@ static int qcom_nand_attach_chip(struct + + mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); + /* Free the initially allocated BAM transaction for reading the ONFI params */ +- if (nandc->props->is_bam) ++ if (nandc->props->supports_bam) + free_bam_transaction(nandc); + + nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, + cwperpage); + + /* Now allocate the BAM transaction based on updated max_cwperpage */ +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + nandc->bam_txn = alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, +@@ -2522,7 +2476,7 @@ static int qcom_nand_attach_chip(struct + | ecc_mode << ECC_MODE + | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH; + +- if (!nandc->props->qpic_v2) ++ if (!nandc->props->qpic_version2) + host->ecc_buf_cfg = 0x203 << NUM_STEPS; + + host->clrflashstatus = FS_READY_BSY_N; +@@ -2556,7 +2510,7 @@ static int qcom_op_cmd_mapping(struct na + cmd = OP_FETCH_ID; + break; + case NAND_CMD_PARAM: +- if (nandc->props->qpic_v2) ++ if (nandc->props->qpic_version2) + cmd = OP_PAGE_READ_ONFI_READ; + else + cmd = OP_PAGE_READ; +@@ -2609,7 +2563,7 @@ static int qcom_parse_instructions(struc + if (ret < 0) + return ret; + +- q_op->cmd_reg = ret; ++ q_op->cmd_reg = cpu_to_le32(ret); + q_op->rdy_delay_ns = instr->delay_ns; + break; + +@@ -2619,10 +2573,10 @@ static int qcom_parse_instructions(struc + addrs = &instr->ctx.addr.addrs[offset]; + + for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) +- q_op->addr1_reg |= addrs[i] << (i * 8); ++ q_op->addr1_reg |= cpu_to_le32(addrs[i] << (i * 8)); + + if (naddrs > 4) +- q_op->addr2_reg |= addrs[4]; ++ q_op->addr2_reg |= cpu_to_le32(addrs[4]); + + q_op->rdy_delay_ns = instr->delay_ns; + break; +@@ -2663,7 +2617,7 @@ static int qcom_wait_rdy_poll(struct nan + unsigned long start = jiffies + msecs_to_jiffies(time_ms); + u32 flash; + +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + + do { + flash = le32_to_cpu(nandc->reg_read_buf[0]); +@@ -2706,11 +2660,11 @@ static int qcom_read_status_exec(struct + clear_read_regs(nandc); + clear_bam_transaction(nandc); + +- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); ++ nandc->regs->cmd = q_op.cmd_reg; ++ nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); +@@ -2719,7 +2673,7 @@ static int qcom_read_status_exec(struct + goto err_out; + } + +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + + for (i = 0; i < num_cw; i++) { + flash_status = le32_to_cpu(nandc->reg_read_buf[i]); +@@ -2763,16 +2717,14 @@ static int qcom_read_id_type_exec(struct + clear_read_regs(nandc); + clear_bam_transaction(nandc); + +- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); +- nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg); +- nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg); +- nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT, +- nandc->props->is_bam ? 0 : DM_EN); ++ nandc->regs->cmd = q_op.cmd_reg; ++ nandc->regs->addr0 = q_op.addr1_reg; ++ nandc->regs->addr1 = q_op.addr2_reg; ++ nandc->regs->chip_sel = cpu_to_le32(nandc->props->supports_bam ? 0 : DM_EN); ++ nandc->regs->exec = cpu_to_le32(1); + +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); +- +- write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); + +@@ -2786,7 +2738,7 @@ static int qcom_read_id_type_exec(struct + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + +- nandc_read_buffer_sync(nandc, true); ++ nandc_dev_to_mem(nandc, true); + memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len); + + err_out: +@@ -2807,15 +2759,14 @@ static int qcom_misc_cmd_type_exec(struc + + if (q_op.flag == OP_PROGRAM_PAGE) { + goto wait_rdy; +- } else if (q_op.cmd_reg == OP_BLOCK_ERASE) { +- q_op.cmd_reg |= PAGE_ACC | LAST_PAGE; +- nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg); +- nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg); +- nandc_set_reg(chip, NAND_DEV0_CFG0, +- host->cfg0_raw & ~(7 << CW_PER_PAGE)); +- nandc_set_reg(chip, NAND_DEV0_CFG1, host->cfg1_raw); ++ } else if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE)) { ++ q_op.cmd_reg |= cpu_to_le32(PAGE_ACC | LAST_PAGE); ++ nandc->regs->addr0 = q_op.addr1_reg; ++ nandc->regs->addr1 = q_op.addr2_reg; ++ nandc->regs->cfg0 = cpu_to_le32(host->cfg0_raw & ~(7 << CW_PER_PAGE)); ++ nandc->regs->cfg1 = cpu_to_le32(host->cfg1_raw); + instrs = 3; +- } else if (q_op.cmd_reg != OP_RESET_DEVICE) { ++ } else if (q_op.cmd_reg != cpu_to_le32(OP_RESET_DEVICE)) { + return 0; + } + +@@ -2826,14 +2777,14 @@ static int qcom_misc_cmd_type_exec(struc + clear_read_regs(nandc); + clear_bam_transaction(nandc); + +- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); ++ nandc->regs->cmd = q_op.cmd_reg; ++ nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL); +- if (q_op.cmd_reg == OP_BLOCK_ERASE) +- write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL); ++ if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE)) ++ write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); +@@ -2864,7 +2815,7 @@ static int qcom_param_page_type_exec(str + if (ret) + return ret; + +- q_op.cmd_reg |= PAGE_ACC | LAST_PAGE; ++ q_op.cmd_reg |= cpu_to_le32(PAGE_ACC | LAST_PAGE); + + nandc->buf_count = 0; + nandc->buf_start = 0; +@@ -2872,38 +2823,38 @@ static int qcom_param_page_type_exec(str + clear_read_regs(nandc); + clear_bam_transaction(nandc); + +- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); ++ nandc->regs->cmd = q_op.cmd_reg; ++ nandc->regs->addr0 = 0; ++ nandc->regs->addr1 = 0; ++ ++ nandc->regs->cfg0 = cpu_to_le32(0 << CW_PER_PAGE ++ | 512 << UD_SIZE_BYTES ++ | 5 << NUM_ADDR_CYCLES ++ | 0 << SPARE_SIZE_BYTES); ++ ++ nandc->regs->cfg1 = cpu_to_le32(7 << NAND_RECOVERY_CYCLES ++ | 0 << CS_ACTIVE_BSY ++ | 17 << BAD_BLOCK_BYTE_NUM ++ | 1 << BAD_BLOCK_IN_SPARE_AREA ++ | 2 << WR_RD_BSY_GAP ++ | 0 << WIDE_FLASH ++ | 1 << DEV0_CFG1_ECC_DISABLE); + +- nandc_set_reg(chip, NAND_ADDR0, 0); +- nandc_set_reg(chip, NAND_ADDR1, 0); +- nandc_set_reg(chip, NAND_DEV0_CFG0, 0 << CW_PER_PAGE +- | 512 << UD_SIZE_BYTES +- | 5 << NUM_ADDR_CYCLES +- | 0 << SPARE_SIZE_BYTES); +- nandc_set_reg(chip, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES +- | 0 << CS_ACTIVE_BSY +- | 17 << BAD_BLOCK_BYTE_NUM +- | 1 << BAD_BLOCK_IN_SPARE_AREA +- | 2 << WR_RD_BSY_GAP +- | 0 << WIDE_FLASH +- | 1 << DEV0_CFG1_ECC_DISABLE); +- if (!nandc->props->qpic_v2) +- nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); ++ if (!nandc->props->qpic_version2) ++ nandc->regs->ecc_buf_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE); + + /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */ +- if (!nandc->props->qpic_v2) { +- nandc_set_reg(chip, NAND_DEV_CMD_VLD, +- (nandc->vld & ~READ_START_VLD)); +- nandc_set_reg(chip, NAND_DEV_CMD1, +- (nandc->cmd1 & ~(0xFF << READ_ADDR)) +- | NAND_CMD_PARAM << READ_ADDR); ++ if (!nandc->props->qpic_version2) { ++ nandc->regs->vld = cpu_to_le32((nandc->vld & ~READ_START_VLD)); ++ nandc->regs->cmd1 = cpu_to_le32((nandc->cmd1 & ~(0xFF << READ_ADDR)) ++ | NAND_CMD_PARAM << READ_ADDR); + } + +- nandc_set_reg(chip, NAND_EXEC_CMD, 1); +- +- if (!nandc->props->qpic_v2) { +- nandc_set_reg(chip, NAND_DEV_CMD1_RESTORE, nandc->cmd1); +- nandc_set_reg(chip, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); ++ nandc->regs->exec = cpu_to_le32(1); ++ ++ if (!nandc->props->qpic_version2) { ++ nandc->regs->orig_cmd1 = cpu_to_le32(nandc->cmd1); ++ nandc->regs->orig_vld = cpu_to_le32(nandc->vld); + } + + instr = q_op.data_instr; +@@ -2912,9 +2863,9 @@ static int qcom_param_page_type_exec(str + + nandc_set_read_loc(chip, 0, 0, 0, len, 1); + +- if (!nandc->props->qpic_v2) { +- write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); +- write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); ++ if (!nandc->props->qpic_version2) { ++ write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); + } + + nandc->buf_count = len; +@@ -2926,9 +2877,10 @@ static int qcom_param_page_type_exec(str + nandc->buf_count, 0); + + /* restore CMD1 and VLD regs */ +- if (!nandc->props->qpic_v2) { +- write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); +- write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); ++ if (!nandc->props->qpic_version2) { ++ write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0); ++ write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1, ++ NAND_BAM_NEXT_SGL); + } + + ret = submit_descs(nandc); +@@ -3017,7 +2969,7 @@ static const struct nand_controller_ops + + static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) + { +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) + dma_unmap_single(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * +@@ -3070,7 +3022,7 @@ static int qcom_nandc_alloc(struct qcom_ + if (!nandc->reg_read_buf) + return -ENOMEM; + +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + nandc->reg_read_dma = + dma_map_single(nandc->dev, nandc->reg_read_buf, + MAX_REG_RD * +@@ -3151,15 +3103,15 @@ static int qcom_nandc_setup(struct qcom_ + u32 nand_ctrl; + + /* kill onenand */ +- if (!nandc->props->is_qpic) ++ if (!nandc->props->nandc_part_of_qpic) + nandc_write(nandc, SFLASHC_BURST_CFG, 0); + +- if (!nandc->props->qpic_v2) ++ if (!nandc->props->qpic_version2) + nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), + NAND_DEV_CMD_VLD_VAL); + + /* enable ADM or BAM DMA */ +- if (nandc->props->is_bam) { ++ if (nandc->props->supports_bam) { + nand_ctrl = nandc_read(nandc, NAND_CTRL); + + /* +@@ -3176,7 +3128,7 @@ static int qcom_nandc_setup(struct qcom_ + } + + /* save the original values of these registers */ +- if (!nandc->props->qpic_v2) { ++ if (!nandc->props->qpic_version2) { + nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1)); + nandc->vld = NAND_DEV_CMD_VLD_VAL; + } +@@ -3349,7 +3301,7 @@ static int qcom_nandc_parse_dt(struct pl + struct device_node *np = nandc->dev->of_node; + int ret; + +- if (!nandc->props->is_bam) { ++ if (!nandc->props->supports_bam) { + ret = of_property_read_u32(np, "qcom,cmd-crci", + &nandc->cmd_crci); + if (ret) { +@@ -3474,30 +3426,30 @@ static void qcom_nandc_remove(struct pla + + static const struct qcom_nandc_props ipq806x_nandc_props = { + .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), +- .is_bam = false, ++ .supports_bam = false, + .use_codeword_fixup = true, + .dev_cmd_reg_start = 0x0, + }; + + static const struct qcom_nandc_props ipq4019_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), +- .is_bam = true, +- .is_qpic = true, ++ .supports_bam = true, ++ .nandc_part_of_qpic = true, + .dev_cmd_reg_start = 0x0, + }; + + static const struct qcom_nandc_props ipq8074_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), +- .is_bam = true, +- .is_qpic = true, ++ .supports_bam = true, ++ .nandc_part_of_qpic = true, + .dev_cmd_reg_start = 0x7000, + }; + + static const struct qcom_nandc_props sdx55_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), +- .is_bam = true, +- .is_qpic = true, +- .qpic_v2 = true, ++ .supports_bam = true, ++ .nandc_part_of_qpic = true, ++ .qpic_version2 = true, + .dev_cmd_reg_start = 0x7000, + }; + diff --git a/target/linux/qualcommax/patches-6.6/0403-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch b/target/linux/qualcommax/patches-6.6/0403-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch new file mode 100644 index 0000000000..0bce70c175 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0403-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch @@ -0,0 +1,874 @@ +From: Md Sadre Alam +Date: Sun, 22 Sep 2024 17:03:46 +0530 +Subject: [PATCH] mtd: rawnand: qcom: Add qcom prefix to common api + +Add qcom prefix to all the api which will be commonly +used by spi nand driver and raw nand driver. + +Signed-off-by: Md Sadre Alam +--- +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -53,7 +53,7 @@ + #define NAND_READ_LOCATION_LAST_CW_2 0xf48 + #define NAND_READ_LOCATION_LAST_CW_3 0xf4c + +-/* dummy register offsets, used by write_reg_dma */ ++/* dummy register offsets, used by qcom_write_reg_dma */ + #define NAND_DEV_CMD1_RESTORE 0xdead + #define NAND_DEV_CMD_VLD_RESTORE 0xbeef + +@@ -211,7 +211,7 @@ + + /* + * Flags used in DMA descriptor preparation helper functions +- * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) ++ * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma) + */ + /* Don't set the EOT in current tx BAM sgl */ + #define NAND_BAM_NO_EOT BIT(0) +@@ -550,7 +550,7 @@ struct qcom_nandc_props { + }; + + /* Frees the BAM transaction memory */ +-static void free_bam_transaction(struct qcom_nand_controller *nandc) ++static void qcom_free_bam_transaction(struct qcom_nand_controller *nandc) + { + struct bam_transaction *bam_txn = nandc->bam_txn; + +@@ -559,7 +559,7 @@ static void free_bam_transaction(struct + + /* Allocates and Initializes the BAM transaction */ + static struct bam_transaction * +-alloc_bam_transaction(struct qcom_nand_controller *nandc) ++qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc) + { + struct bam_transaction *bam_txn; + size_t bam_txn_size; +@@ -595,7 +595,7 @@ alloc_bam_transaction(struct qcom_nand_c + } + + /* Clears the BAM transaction indexes */ +-static void clear_bam_transaction(struct qcom_nand_controller *nandc) ++static void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc) + { + struct bam_transaction *bam_txn = nandc->bam_txn; + +@@ -614,7 +614,7 @@ static void clear_bam_transaction(struct + } + + /* Callback for DMA descriptor completion */ +-static void qpic_bam_dma_done(void *data) ++static void qcom_qpic_bam_dma_done(void *data) + { + struct bam_transaction *bam_txn = data; + +@@ -644,7 +644,7 @@ static inline void nandc_write(struct qc + iowrite32(val, nandc->base + offset); + } + +-static inline void nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) ++static inline void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) + { + if (!nandc->props->supports_bam) + return; +@@ -824,9 +824,9 @@ static void update_rw_regs(struct qcom_n + * for BAM. This descriptor will be added in the NAND DMA descriptor queue + * which will be submitted to DMA engine. + */ +-static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, +- struct dma_chan *chan, +- unsigned long flags) ++static int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc, ++ struct dma_chan *chan, ++ unsigned long flags) + { + struct desc_info *desc; + struct scatterlist *sgl; +@@ -903,9 +903,9 @@ static int prepare_bam_async_desc(struct + * NAND_BAM_NEXT_SGL will be used for starting the separate SGL + * after the current command element. + */ +-static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, +- int reg_off, const void *vaddr, +- int size, unsigned int flags) ++static int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, ++ int size, unsigned int flags) + { + int bam_ce_size; + int i, ret; +@@ -943,9 +943,9 @@ static int prep_bam_dma_desc_cmd(struct + bam_txn->bam_ce_start = bam_txn->bam_ce_pos; + + if (flags & NAND_BAM_NWD) { +- ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, +- DMA_PREP_FENCE | +- DMA_PREP_CMD); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, ++ DMA_PREP_FENCE | ++ DMA_PREP_CMD); + if (ret) + return ret; + } +@@ -958,9 +958,8 @@ static int prep_bam_dma_desc_cmd(struct + * Prepares the data descriptor for BAM DMA which will be used for NAND + * data reads and writes. + */ +-static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, +- const void *vaddr, +- int size, unsigned int flags) ++static int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, ++ const void *vaddr, int size, unsigned int flags) + { + int ret; + struct bam_transaction *bam_txn = nandc->bam_txn; +@@ -979,8 +978,8 @@ static int prep_bam_dma_desc_data(struct + * is not set, form the DMA descriptor + */ + if (!(flags & NAND_BAM_NO_EOT)) { +- ret = prepare_bam_async_desc(nandc, nandc->tx_chan, +- DMA_PREP_INTERRUPT); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); + if (ret) + return ret; + } +@@ -989,9 +988,9 @@ static int prep_bam_dma_desc_data(struct + return 0; + } + +-static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, +- int reg_off, const void *vaddr, int size, +- bool flow_control) ++static int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, int size, ++ bool flow_control) + { + struct desc_info *desc; + struct dma_async_tx_descriptor *dma_desc; +@@ -1069,15 +1068,15 @@ err: + } + + /* +- * read_reg_dma: prepares a descriptor to read a given number of ++ * qcom_read_reg_dma: prepares a descriptor to read a given number of + * contiguous registers to the reg_read_buf pointer + * + * @first: offset of the first register in the contiguous block + * @num_regs: number of registers to read + * @flags: flags to control DMA descriptor preparation + */ +-static int read_reg_dma(struct qcom_nand_controller *nandc, int first, +- int num_regs, unsigned int flags) ++static int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first, ++ int num_regs, unsigned int flags) + { + bool flow_control = false; + void *vaddr; +@@ -1089,18 +1088,18 @@ static int read_reg_dma(struct qcom_nand + first = dev_cmd_reg_addr(nandc, first); + + if (nandc->props->supports_bam) +- return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, ++ return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr, + num_regs, flags); + + if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) + flow_control = true; + +- return prep_adm_dma_desc(nandc, true, first, vaddr, ++ return qcom_prep_adm_dma_desc(nandc, true, first, vaddr, + num_regs * sizeof(u32), flow_control); + } + + /* +- * write_reg_dma: prepares a descriptor to write a given number of ++ * qcom_write_reg_dma: prepares a descriptor to write a given number of + * contiguous registers + * + * @vaddr: contnigeous memory from where register value will +@@ -1109,8 +1108,8 @@ static int read_reg_dma(struct qcom_nand + * @num_regs: number of registers to write + * @flags: flags to control DMA descriptor preparation + */ +-static int write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, +- int first, int num_regs, unsigned int flags) ++static int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, ++ int first, int num_regs, unsigned int flags) + { + bool flow_control = false; + +@@ -1124,18 +1123,18 @@ static int write_reg_dma(struct qcom_nan + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); + + if (nandc->props->supports_bam) +- return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, ++ return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr, + num_regs, flags); + + if (first == NAND_FLASH_CMD) + flow_control = true; + +- return prep_adm_dma_desc(nandc, false, first, vaddr, ++ return qcom_prep_adm_dma_desc(nandc, false, first, vaddr, + num_regs * sizeof(u32), flow_control); + } + + /* +- * read_data_dma: prepares a DMA descriptor to transfer data from the ++ * qcom_read_data_dma: prepares a DMA descriptor to transfer data from the + * controller's internal buffer to the buffer 'vaddr' + * + * @reg_off: offset within the controller's data buffer +@@ -1143,17 +1142,17 @@ static int write_reg_dma(struct qcom_nan + * @size: DMA transaction size in bytes + * @flags: flags to control DMA descriptor preparation + */ +-static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size, unsigned int flags) ++static int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) + { + if (nandc->props->supports_bam) +- return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); ++ return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); + +- return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); ++ return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); + } + + /* +- * write_data_dma: prepares a DMA descriptor to transfer data from ++ * qcom_write_data_dma: prepares a DMA descriptor to transfer data from + * 'vaddr' to the controller's internal buffer + * + * @reg_off: offset within the controller's data buffer +@@ -1161,13 +1160,13 @@ static int read_data_dma(struct qcom_nan + * @size: DMA transaction size in bytes + * @flags: flags to control DMA descriptor preparation + */ +-static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size, unsigned int flags) ++static int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) + { + if (nandc->props->supports_bam) +- return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); ++ return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); + +- return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); ++ return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); + } + + /* +@@ -1178,14 +1177,14 @@ static void config_nand_page_read(struct + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); +- write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); + if (!nandc->props->qpic_version2) +- write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); +- write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, +- NAND_ERASED_CW_DETECT_CFG, 1, 0); +- write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, +- NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); + } + + /* +@@ -1204,17 +1203,17 @@ config_nand_cw_read(struct nand_chip *ch + reg = &nandc->regs->read_location_last0; + + if (nandc->props->supports_bam) +- write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + if (use_ecc) { +- read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); +- read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, +- NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); ++ qcom_read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, ++ NAND_BAM_NEXT_SGL); + } else { +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + } + } + +@@ -1238,11 +1237,11 @@ static void config_nand_page_write(struc + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); +- write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); + if (!nandc->props->qpic_version2) +- write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, +- NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, ++ NAND_BAM_NEXT_SGL); + } + + /* +@@ -1253,17 +1252,18 @@ static void config_nand_cw_write(struct + { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); +- write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, ++ NAND_BAM_NEXT_SGL); + } + + /* helpers to submit/free our list of dma descriptors */ +-static int submit_descs(struct qcom_nand_controller *nandc) ++static int qcom_submit_descs(struct qcom_nand_controller *nandc) + { + struct desc_info *desc, *n; + dma_cookie_t cookie = 0; +@@ -1272,21 +1272,21 @@ static int submit_descs(struct qcom_nand + + if (nandc->props->supports_bam) { + if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { +- ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0); + if (ret) + goto err_unmap_free_desc; + } + + if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { +- ret = prepare_bam_async_desc(nandc, nandc->tx_chan, +- DMA_PREP_INTERRUPT); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); + if (ret) + goto err_unmap_free_desc; + } + + if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { +- ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, +- DMA_PREP_CMD); ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, ++ DMA_PREP_CMD); + if (ret) + goto err_unmap_free_desc; + } +@@ -1296,7 +1296,7 @@ static int submit_descs(struct qcom_nand + cookie = dmaengine_submit(desc->dma_desc); + + if (nandc->props->supports_bam) { +- bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; ++ bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done; + bam_txn->last_cmd_desc->callback_param = bam_txn; + + dma_async_issue_pending(nandc->tx_chan); +@@ -1314,7 +1314,7 @@ static int submit_descs(struct qcom_nand + err_unmap_free_desc: + /* + * Unmap the dma sg_list and free the desc allocated by both +- * prepare_bam_async_desc() and prep_adm_dma_desc() functions. ++ * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions. + */ + list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { + list_del(&desc->node); +@@ -1333,10 +1333,10 @@ err_unmap_free_desc: + } + + /* reset the register read buffer for next NAND operation */ +-static void clear_read_regs(struct qcom_nand_controller *nandc) ++static void qcom_clear_read_regs(struct qcom_nand_controller *nandc) + { + nandc->reg_read_pos = 0; +- nandc_dev_to_mem(nandc, false); ++ qcom_nandc_dev_to_mem(nandc, false); + } + + /* +@@ -1400,7 +1400,7 @@ static int check_flash_errors(struct qco + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int i; + +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); +@@ -1427,13 +1427,13 @@ qcom_nandc_read_cw_raw(struct mtd_info * + nand_read_page_op(chip, page, 0, NULL, 0); + nandc->buf_count = 0; + nandc->buf_start = 0; +- clear_read_regs(nandc); ++ qcom_clear_read_regs(nandc); + host->use_ecc = false; + + if (nandc->props->qpic_version2) + raw_cw = ecc->steps - 1; + +- clear_bam_transaction(nandc); ++ qcom_clear_bam_transaction(nandc); + set_address(host, host->cw_size * cw, page); + update_rw_regs(host, 1, true, raw_cw); + config_nand_page_read(chip); +@@ -1466,18 +1466,18 @@ qcom_nandc_read_cw_raw(struct mtd_info * + + config_nand_cw_read(chip, false, raw_cw); + +- read_data_dma(nandc, reg_off, data_buf, data_size1, 0); ++ qcom_read_data_dma(nandc, reg_off, data_buf, data_size1, 0); + reg_off += data_size1; + +- read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); ++ qcom_read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); + reg_off += oob_size1; + +- read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0); ++ qcom_read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0); + reg_off += data_size2; + +- read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0); ++ qcom_read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to read raw cw %d\n", cw); + return ret; +@@ -1575,7 +1575,7 @@ static int parse_read_errors(struct qcom + u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; + + buf = (struct read_stats *)nandc->reg_read_buf; +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + + for (i = 0; i < ecc->steps; i++, buf++) { + u32 flash, buffer, erased_cw; +@@ -1704,8 +1704,8 @@ static int read_page_ecc(struct qcom_nan + config_nand_cw_read(chip, true, i); + + if (data_buf) +- read_data_dma(nandc, FLASH_BUF_ACC, data_buf, +- data_size, 0); ++ qcom_read_data_dma(nandc, FLASH_BUF_ACC, data_buf, ++ data_size, 0); + + /* + * when ecc is enabled, the controller doesn't read the real +@@ -1720,8 +1720,8 @@ static int read_page_ecc(struct qcom_nan + for (j = 0; j < host->bbm_size; j++) + *oob_buf++ = 0xff; + +- read_data_dma(nandc, FLASH_BUF_ACC + data_size, +- oob_buf, oob_size, 0); ++ qcom_read_data_dma(nandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); + } + + if (data_buf) +@@ -1730,7 +1730,7 @@ static int read_page_ecc(struct qcom_nan + oob_buf += oob_size; + } + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to read page/oob\n"); + return ret; +@@ -1751,7 +1751,7 @@ static int copy_last_cw(struct qcom_nand + int size; + int ret; + +- clear_read_regs(nandc); ++ qcom_clear_read_regs(nandc); + + size = host->use_ecc ? host->cw_data : host->cw_size; + +@@ -1763,9 +1763,9 @@ static int copy_last_cw(struct qcom_nand + + config_nand_single_cw_page_read(chip, host->use_ecc, ecc->steps - 1); + +- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); ++ qcom_read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) + dev_err(nandc->dev, "failed to copy last codeword\n"); + +@@ -1851,14 +1851,14 @@ static int qcom_nandc_read_page(struct n + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = true; +- clear_read_regs(nandc); ++ qcom_clear_read_regs(nandc); + set_address(host, 0, page); + update_rw_regs(host, ecc->steps, true, 0); + + data_buf = buf; + oob_buf = oob_required ? chip->oob_poi : NULL; + +- clear_bam_transaction(nandc); ++ qcom_clear_bam_transaction(nandc); + + return read_page_ecc(host, data_buf, oob_buf, page); + } +@@ -1899,8 +1899,8 @@ static int qcom_nandc_read_oob(struct na + if (host->nr_boot_partitions) + qcom_nandc_codeword_fixup(host, page); + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + host->use_ecc = true; + set_address(host, 0, page); +@@ -1927,8 +1927,8 @@ static int qcom_nandc_write_page(struct + set_address(host, 0, page); + nandc->buf_count = 0; + nandc->buf_start = 0; +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + data_buf = (u8 *)buf; + oob_buf = chip->oob_poi; +@@ -1949,8 +1949,8 @@ static int qcom_nandc_write_page(struct + oob_size = ecc->bytes; + } + +- write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, +- i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); ++ qcom_write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, ++ i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); + + /* + * when ECC is enabled, we don't really need to write anything +@@ -1962,8 +1962,8 @@ static int qcom_nandc_write_page(struct + if (qcom_nandc_is_last_cw(ecc, i)) { + oob_buf += host->bbm_size; + +- write_data_dma(nandc, FLASH_BUF_ACC + data_size, +- oob_buf, oob_size, 0); ++ qcom_write_data_dma(nandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); + } + + config_nand_cw_write(chip); +@@ -1972,7 +1972,7 @@ static int qcom_nandc_write_page(struct + oob_buf += oob_size; + } + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to write page\n"); + return ret; +@@ -1997,8 +1997,8 @@ static int qcom_nandc_write_page_raw(str + qcom_nandc_codeword_fixup(host, page); + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + data_buf = (u8 *)buf; + oob_buf = chip->oob_poi; +@@ -2024,28 +2024,28 @@ static int qcom_nandc_write_page_raw(str + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + +- write_data_dma(nandc, reg_off, data_buf, data_size1, +- NAND_BAM_NO_EOT); ++ qcom_write_data_dma(nandc, reg_off, data_buf, data_size1, ++ NAND_BAM_NO_EOT); + reg_off += data_size1; + data_buf += data_size1; + +- write_data_dma(nandc, reg_off, oob_buf, oob_size1, +- NAND_BAM_NO_EOT); ++ qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size1, ++ NAND_BAM_NO_EOT); + reg_off += oob_size1; + oob_buf += oob_size1; + +- write_data_dma(nandc, reg_off, data_buf, data_size2, +- NAND_BAM_NO_EOT); ++ qcom_write_data_dma(nandc, reg_off, data_buf, data_size2, ++ NAND_BAM_NO_EOT); + reg_off += data_size2; + data_buf += data_size2; + +- write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); ++ qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); + oob_buf += oob_size2; + + config_nand_cw_write(chip); + } + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to write raw page\n"); + return ret; +@@ -2075,7 +2075,7 @@ static int qcom_nandc_write_oob(struct n + qcom_nandc_codeword_fixup(host, page); + + host->use_ecc = true; +- clear_bam_transaction(nandc); ++ qcom_clear_bam_transaction(nandc); + + /* calculate the data and oob size for the last codeword/step */ + data_size = ecc->size - ((ecc->steps - 1) << 2); +@@ -2090,11 +2090,11 @@ static int qcom_nandc_write_oob(struct n + update_rw_regs(host, 1, false, 0); + + config_nand_page_write(chip); +- write_data_dma(nandc, FLASH_BUF_ACC, +- nandc->data_buffer, data_size + oob_size, 0); ++ qcom_write_data_dma(nandc, FLASH_BUF_ACC, ++ nandc->data_buffer, data_size + oob_size, 0); + config_nand_cw_write(chip); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to write oob\n"); + return ret; +@@ -2121,7 +2121,7 @@ static int qcom_nandc_block_bad(struct n + */ + host->use_ecc = false; + +- clear_bam_transaction(nandc); ++ qcom_clear_bam_transaction(nandc); + ret = copy_last_cw(host, page); + if (ret) + goto err; +@@ -2148,8 +2148,8 @@ static int qcom_nandc_block_markbad(stru + struct nand_ecc_ctrl *ecc = &chip->ecc; + int page, ret; + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + /* + * to mark the BBM as bad, we flash the entire last codeword with 0s. +@@ -2166,11 +2166,11 @@ static int qcom_nandc_block_markbad(stru + update_rw_regs(host, 1, false, ecc->steps - 1); + + config_nand_page_write(chip); +- write_data_dma(nandc, FLASH_BUF_ACC, +- nandc->data_buffer, host->cw_size, 0); ++ qcom_write_data_dma(nandc, FLASH_BUF_ACC, ++ nandc->data_buffer, host->cw_size, 0); + config_nand_cw_write(chip); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to update BBM\n"); + return ret; +@@ -2410,14 +2410,14 @@ static int qcom_nand_attach_chip(struct + mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); + /* Free the initially allocated BAM transaction for reading the ONFI params */ + if (nandc->props->supports_bam) +- free_bam_transaction(nandc); ++ qcom_free_bam_transaction(nandc); + + nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, + cwperpage); + + /* Now allocate the BAM transaction based on updated max_cwperpage */ + if (nandc->props->supports_bam) { +- nandc->bam_txn = alloc_bam_transaction(nandc); ++ nandc->bam_txn = qcom_alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, + "failed to allocate bam transaction\n"); +@@ -2617,7 +2617,7 @@ static int qcom_wait_rdy_poll(struct nan + unsigned long start = jiffies + msecs_to_jiffies(time_ms); + u32 flash; + +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + + do { + flash = le32_to_cpu(nandc->reg_read_buf[0]); +@@ -2657,23 +2657,23 @@ static int qcom_read_status_exec(struct + nandc->buf_start = 0; + host->use_ecc = false; + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + nandc->regs->cmd = q_op.cmd_reg; + nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting status descriptor\n"); + goto err_out; + } + +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + + for (i = 0; i < num_cw; i++) { + flash_status = le32_to_cpu(nandc->reg_read_buf[i]); +@@ -2714,8 +2714,8 @@ static int qcom_read_id_type_exec(struct + nandc->buf_start = 0; + host->use_ecc = false; + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + nandc->regs->cmd = q_op.cmd_reg; + nandc->regs->addr0 = q_op.addr1_reg; +@@ -2723,12 +2723,12 @@ static int qcom_read_id_type_exec(struct + nandc->regs->chip_sel = cpu_to_le32(nandc->props->supports_bam ? 0 : DM_EN); + nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + +- read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting read id descriptor\n"); + goto err_out; +@@ -2738,7 +2738,7 @@ static int qcom_read_id_type_exec(struct + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + +- nandc_dev_to_mem(nandc, true); ++ qcom_nandc_dev_to_mem(nandc, true); + memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len); + + err_out: +@@ -2774,20 +2774,20 @@ static int qcom_misc_cmd_type_exec(struc + nandc->buf_start = 0; + host->use_ecc = false; + +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + nandc->regs->cmd = q_op.cmd_reg; + nandc->regs->exec = cpu_to_le32(1); + +- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL); + if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE)) +- write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); + +- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); +- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting misc descriptor\n"); + goto err_out; +@@ -2820,8 +2820,8 @@ static int qcom_param_page_type_exec(str + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; +- clear_read_regs(nandc); +- clear_bam_transaction(nandc); ++ qcom_clear_read_regs(nandc); ++ qcom_clear_bam_transaction(nandc); + + nandc->regs->cmd = q_op.cmd_reg; + nandc->regs->addr0 = 0; +@@ -2864,8 +2864,8 @@ static int qcom_param_page_type_exec(str + nandc_set_read_loc(chip, 0, 0, 0, len, 1); + + if (!nandc->props->qpic_version2) { +- write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0); +- write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); + } + + nandc->buf_count = len; +@@ -2873,17 +2873,17 @@ static int qcom_param_page_type_exec(str + + config_nand_single_cw_page_read(chip, false, 0); + +- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, +- nandc->buf_count, 0); ++ qcom_read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, ++ nandc->buf_count, 0); + + /* restore CMD1 and VLD regs */ + if (!nandc->props->qpic_version2) { +- write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0); +- write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1, +- NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0); ++ qcom_write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1, ++ NAND_BAM_NEXT_SGL); + } + +- ret = submit_descs(nandc); ++ ret = qcom_submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting param page descriptor\n"); + goto err_out; +@@ -3067,7 +3067,7 @@ static int qcom_nandc_alloc(struct qcom_ + * maximum codeword size + */ + nandc->max_cwperpage = 1; +- nandc->bam_txn = alloc_bam_transaction(nandc); ++ nandc->bam_txn = qcom_alloc_bam_transaction(nandc); + if (!nandc->bam_txn) { + dev_err(nandc->dev, + "failed to allocate bam transaction\n"); diff --git a/target/linux/qualcommax/patches-6.6/0404-mtd-nand-Add-qpic_common-API-file.patch b/target/linux/qualcommax/patches-6.6/0404-mtd-nand-Add-qpic_common-API-file.patch new file mode 100644 index 0000000000..e992e261af --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0404-mtd-nand-Add-qpic_common-API-file.patch @@ -0,0 +1,2418 @@ +From: Md Sadre Alam +Date: Sun, 22 Sep 2024 17:03:47 +0530 +Subject: [PATCH] mtd: nand: Add qpic_common API file + +Add qpic_common.c file which hold all the common +qpic APIs which will be used by both qpic raw nand +driver and qpic spi nand driver. + +Signed-off-by: Md Sadre Alam +--- +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -5,6 +5,10 @@ obj-$(CONFIG_MTD_NAND_CORE) += nandcore. + obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o + obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o + ++ifeq ($(CONFIG_MTD_NAND_QCOM),y) ++obj-y += qpic_common.o ++endif ++ + obj-y += onenand/ + obj-y += raw/ + obj-y += spi/ +--- /dev/null ++++ b/drivers/mtd/nand/qpic_common.c +@@ -0,0 +1,738 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2016, The Linux Foundation. All rights reserved. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * qcom_free_bam_transaction() - Frees the BAM transaction memory ++ * @nandc: qpic nand controller ++ * ++ * This function frees the bam transaction memory ++ */ ++void qcom_free_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ kfree(bam_txn); ++} ++ ++/** ++ * qcom_alloc_bam_transaction() - allocate BAM transaction ++ * @nandc: qpic nand controller ++ * ++ * This function will allocate and initialize the BAM transaction structure ++ */ ++struct bam_transaction * ++qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn; ++ size_t bam_txn_size; ++ unsigned int num_cw = nandc->max_cwperpage; ++ void *bam_txn_buf; ++ ++ bam_txn_size = ++ sizeof(*bam_txn) + num_cw * ++ ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + ++ (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + ++ (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); ++ ++ bam_txn_buf = kzalloc(bam_txn_size, GFP_KERNEL); ++ if (!bam_txn_buf) ++ return NULL; ++ ++ bam_txn = bam_txn_buf; ++ bam_txn_buf += sizeof(*bam_txn); ++ ++ bam_txn->bam_ce = bam_txn_buf; ++ bam_txn_buf += ++ sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; ++ ++ bam_txn->cmd_sgl = bam_txn_buf; ++ bam_txn_buf += ++ sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; ++ ++ bam_txn->data_sgl = bam_txn_buf; ++ ++ init_completion(&bam_txn->txn_done); ++ ++ return bam_txn; ++} ++ ++/** ++ * qcom_clear_bam_transaction() - Clears the BAM transaction ++ * @nandc: qpic nand controller ++ * ++ * This function will clear the BAM transaction indexes. ++ */ ++void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc) ++{ ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ if (!nandc->props->supports_bam) ++ return; ++ ++ memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8); ++ bam_txn->last_data_desc = NULL; ++ ++ sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * ++ QPIC_PER_CW_CMD_SGL); ++ sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * ++ QPIC_PER_CW_DATA_SGL); ++ ++ reinit_completion(&bam_txn->txn_done); ++} ++ ++/** ++ * qcom_qpic_bam_dma_done() - Callback for DMA descriptor completion ++ * @data: data pointer ++ * ++ * This function is a callback for DMA descriptor completion ++ */ ++void qcom_qpic_bam_dma_done(void *data) ++{ ++ struct bam_transaction *bam_txn = data; ++ ++ complete(&bam_txn->txn_done); ++} ++ ++/** ++ * qcom_nandc_dev_to_mem() - Check for dma sync for cpu or device ++ * @nandc: qpic nand controller ++ * @is_cpu: cpu or Device ++ * ++ * This function will check for dma sync for cpu or device ++ */ ++inline void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) ++{ ++ if (!nandc->props->supports_bam) ++ return; ++ ++ if (is_cpu) ++ dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++ else ++ dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++} ++ ++/** ++ * qcom_prepare_bam_async_desc() - Prepare DMA descriptor ++ * @nandc: qpic nand controller ++ * @chan: dma channel ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function maps the scatter gather list for DMA transfer and forms the ++ * DMA descriptor for BAM.This descriptor will be added in the NAND DMA ++ * descriptor queue which will be submitted to DMA engine. ++ */ ++int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc, ++ struct dma_chan *chan, unsigned long flags) ++{ ++ struct desc_info *desc; ++ struct scatterlist *sgl; ++ unsigned int sgl_cnt; ++ int ret; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ enum dma_transfer_direction dir_eng; ++ struct dma_async_tx_descriptor *dma_desc; ++ ++ desc = kzalloc(sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ if (chan == nandc->cmd_chan) { ++ sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; ++ sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; ++ bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; ++ dir_eng = DMA_MEM_TO_DEV; ++ desc->dir = DMA_TO_DEVICE; ++ } else if (chan == nandc->tx_chan) { ++ sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; ++ sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; ++ bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; ++ dir_eng = DMA_MEM_TO_DEV; ++ desc->dir = DMA_TO_DEVICE; ++ } else { ++ sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; ++ sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; ++ bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; ++ dir_eng = DMA_DEV_TO_MEM; ++ desc->dir = DMA_FROM_DEVICE; ++ } ++ ++ sg_mark_end(sgl + sgl_cnt - 1); ++ ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); ++ if (ret == 0) { ++ dev_err(nandc->dev, "failure in mapping desc\n"); ++ kfree(desc); ++ return -ENOMEM; ++ } ++ ++ desc->sgl_cnt = sgl_cnt; ++ desc->bam_sgl = sgl; ++ ++ dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, ++ flags); ++ ++ if (!dma_desc) { ++ dev_err(nandc->dev, "failure in prep desc\n"); ++ dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); ++ kfree(desc); ++ return -EINVAL; ++ } ++ ++ desc->dma_desc = dma_desc; ++ ++ /* update last data/command descriptor */ ++ if (chan == nandc->cmd_chan) ++ bam_txn->last_cmd_desc = dma_desc; ++ else ++ bam_txn->last_data_desc = dma_desc; ++ ++ list_add_tail(&desc->node, &nandc->desc_list); ++ ++ return 0; ++} ++ ++/** ++ * qcom_prep_bam_dma_desc_cmd() - Prepares the command descriptor for BAM DMA ++ * @nandc: qpic nand controller ++ * @read: read or write type ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares the command descriptor for BAM DMA ++ * which will be used for NAND register reads and writes. ++ */ ++int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, ++ int size, unsigned int flags) ++{ ++ int bam_ce_size; ++ int i, ret; ++ struct bam_cmd_element *bam_ce_buffer; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; ++ ++ /* fill the command desc */ ++ for (i = 0; i < size; i++) { ++ if (read) ++ bam_prep_ce(&bam_ce_buffer[i], ++ nandc_reg_phys(nandc, reg_off + 4 * i), ++ BAM_READ_COMMAND, ++ reg_buf_dma_addr(nandc, ++ (__le32 *)vaddr + i)); ++ else ++ bam_prep_ce_le32(&bam_ce_buffer[i], ++ nandc_reg_phys(nandc, reg_off + 4 * i), ++ BAM_WRITE_COMMAND, ++ *((__le32 *)vaddr + i)); ++ } ++ ++ bam_txn->bam_ce_pos += size; ++ ++ /* use the separate sgl after this command */ ++ if (flags & NAND_BAM_NEXT_SGL) { ++ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; ++ bam_ce_size = (bam_txn->bam_ce_pos - ++ bam_txn->bam_ce_start) * ++ sizeof(struct bam_cmd_element); ++ sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], ++ bam_ce_buffer, bam_ce_size); ++ bam_txn->cmd_sgl_pos++; ++ bam_txn->bam_ce_start = bam_txn->bam_ce_pos; ++ ++ if (flags & NAND_BAM_NWD) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, ++ DMA_PREP_FENCE | DMA_PREP_CMD); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * qcom_prep_bam_dma_desc_data() - Prepares the data descriptor for BAM DMA ++ * @nandc: qpic nand controller ++ * @read: read or write type ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares the data descriptor for BAM DMA which ++ * will be used for NAND data reads and writes. ++ */ ++int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, ++ const void *vaddr, int size, unsigned int flags) ++{ ++ int ret; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ ++ if (read) { ++ sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], ++ vaddr, size); ++ bam_txn->rx_sgl_pos++; ++ } else { ++ sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], ++ vaddr, size); ++ bam_txn->tx_sgl_pos++; ++ ++ /* ++ * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag ++ * is not set, form the DMA descriptor ++ */ ++ if (!(flags & NAND_BAM_NO_EOT)) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * qcom_prep_adm_dma_desc() - Prepare descriptor for adma ++ * @nandc: qpic nand controller ++ * @read: read or write type ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: adm dma transaction size in bytes ++ * @flow_control: flow controller ++ * ++ * This function will prepare descriptor for adma ++ */ ++int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, int size, ++ bool flow_control) ++{ ++ struct qcom_adm_peripheral_config periph_conf = {}; ++ struct dma_async_tx_descriptor *dma_desc; ++ struct dma_slave_config slave_conf = {0}; ++ enum dma_transfer_direction dir_eng; ++ struct desc_info *desc; ++ struct scatterlist *sgl; ++ int ret; ++ ++ desc = kzalloc(sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ sgl = &desc->adm_sgl; ++ ++ sg_init_one(sgl, vaddr, size); ++ ++ if (read) { ++ dir_eng = DMA_DEV_TO_MEM; ++ desc->dir = DMA_FROM_DEVICE; ++ } else { ++ dir_eng = DMA_MEM_TO_DEV; ++ desc->dir = DMA_TO_DEVICE; ++ } ++ ++ ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); ++ if (!ret) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ slave_conf.device_fc = flow_control; ++ if (read) { ++ slave_conf.src_maxburst = 16; ++ slave_conf.src_addr = nandc->base_dma + reg_off; ++ if (nandc->data_crci) { ++ periph_conf.crci = nandc->data_crci; ++ slave_conf.peripheral_config = &periph_conf; ++ slave_conf.peripheral_size = sizeof(periph_conf); ++ } ++ } else { ++ slave_conf.dst_maxburst = 16; ++ slave_conf.dst_addr = nandc->base_dma + reg_off; ++ if (nandc->cmd_crci) { ++ periph_conf.crci = nandc->cmd_crci; ++ slave_conf.peripheral_config = &periph_conf; ++ slave_conf.peripheral_size = sizeof(periph_conf); ++ } ++ } ++ ++ ret = dmaengine_slave_config(nandc->chan, &slave_conf); ++ if (ret) { ++ dev_err(nandc->dev, "failed to configure dma channel\n"); ++ goto err; ++ } ++ ++ dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); ++ if (!dma_desc) { ++ dev_err(nandc->dev, "failed to prepare desc\n"); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ desc->dma_desc = dma_desc; ++ ++ list_add_tail(&desc->node, &nandc->desc_list); ++ ++ return 0; ++err: ++ kfree(desc); ++ ++ return ret; ++} ++ ++/** ++ * qcom_read_reg_dma() - read a given number of registers to the reg_read_buf pointer ++ * @nandc: qpic nand controller ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to read ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares a descriptor to read a given number of ++ * contiguous registers to the reg_read_buf pointer. ++ */ ++int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first, ++ int num_regs, unsigned int flags) ++{ ++ bool flow_control = false; ++ void *vaddr; ++ ++ vaddr = nandc->reg_read_buf + nandc->reg_read_pos; ++ nandc->reg_read_pos += num_regs; ++ ++ if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) ++ first = dev_cmd_reg_addr(nandc, first); ++ ++ if (nandc->props->supports_bam) ++ return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr, ++ num_regs, flags); ++ ++ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) ++ flow_control = true; ++ ++ return qcom_prep_adm_dma_desc(nandc, true, first, vaddr, ++ num_regs * sizeof(u32), flow_control); ++} ++ ++/** ++ * qcom_write_reg_dma() - write a given number of registers ++ * @nandc: qpic nand controller ++ * @vaddr: contnigeous memory from where register value will ++ * be written ++ * @first: offset of the first register in the contiguous block ++ * @num_regs: number of registers to write ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares a descriptor to write a given number of ++ * contiguous registers ++ */ ++int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, ++ int first, int num_regs, unsigned int flags) ++{ ++ bool flow_control = false; ++ ++ if (first == NAND_EXEC_CMD) ++ flags |= NAND_BAM_NWD; ++ ++ if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) ++ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); ++ ++ if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) ++ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); ++ ++ if (nandc->props->supports_bam) ++ return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr, ++ num_regs, flags); ++ ++ if (first == NAND_FLASH_CMD) ++ flow_control = true; ++ ++ return qcom_prep_adm_dma_desc(nandc, false, first, vaddr, ++ num_regs * sizeof(u32), flow_control); ++} ++ ++/** ++ * qcom_read_data_dma() - transfer data ++ * @nandc: qpic nand controller ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to write to ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares a DMA descriptor to transfer data from the ++ * controller's internal buffer to the buffer 'vaddr' ++ */ ++int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) ++{ ++ if (nandc->props->supports_bam) ++ return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); ++ ++ return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); ++} ++ ++/** ++ * qcom_write_data_dma() - transfer data ++ * @nandc: qpic nand controller ++ * @reg_off: offset within the controller's data buffer ++ * @vaddr: virtual address of the buffer we want to read from ++ * @size: DMA transaction size in bytes ++ * @flags: flags to control DMA descriptor preparation ++ * ++ * This function will prepares a DMA descriptor to transfer data from ++ * 'vaddr' to the controller's internal buffer ++ */ ++int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off, ++ const u8 *vaddr, int size, unsigned int flags) ++{ ++ if (nandc->props->supports_bam) ++ return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); ++ ++ return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); ++} ++ ++/** ++ * qcom_submit_descs() - submit dma descriptor ++ * @nandc: qpic nand controller ++ * ++ * This function will submit all the prepared dma descriptor ++ * cmd or data descriptor ++ */ ++int qcom_submit_descs(struct qcom_nand_controller *nandc) ++{ ++ struct desc_info *desc, *n; ++ dma_cookie_t cookie = 0; ++ struct bam_transaction *bam_txn = nandc->bam_txn; ++ int ret = 0; ++ ++ if (nandc->props->supports_bam) { ++ if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0); ++ if (ret) ++ goto err_unmap_free_desc; ++ } ++ ++ if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, ++ DMA_PREP_INTERRUPT); ++ if (ret) ++ goto err_unmap_free_desc; ++ } ++ ++ if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { ++ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, ++ DMA_PREP_CMD); ++ if (ret) ++ goto err_unmap_free_desc; ++ } ++ } ++ ++ list_for_each_entry(desc, &nandc->desc_list, node) ++ cookie = dmaengine_submit(desc->dma_desc); ++ ++ if (nandc->props->supports_bam) { ++ bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done; ++ bam_txn->last_cmd_desc->callback_param = bam_txn; ++ ++ dma_async_issue_pending(nandc->tx_chan); ++ dma_async_issue_pending(nandc->rx_chan); ++ dma_async_issue_pending(nandc->cmd_chan); ++ ++ if (!wait_for_completion_timeout(&bam_txn->txn_done, ++ QPIC_NAND_COMPLETION_TIMEOUT)) ++ ret = -ETIMEDOUT; ++ } else { ++ if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) ++ ret = -ETIMEDOUT; ++ } ++ ++err_unmap_free_desc: ++ /* ++ * Unmap the dma sg_list and free the desc allocated by both ++ * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions. ++ */ ++ list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { ++ list_del(&desc->node); ++ ++ if (nandc->props->supports_bam) ++ dma_unmap_sg(nandc->dev, desc->bam_sgl, ++ desc->sgl_cnt, desc->dir); ++ else ++ dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, ++ desc->dir); ++ ++ kfree(desc); ++ } ++ ++ return ret; ++} ++ ++/** ++ * qcom_clear_read_regs() - reset the read register buffer ++ * @nandc: qpic nand controller ++ * ++ * This function reset the register read buffer for next NAND operation ++ */ ++void qcom_clear_read_regs(struct qcom_nand_controller *nandc) ++{ ++ nandc->reg_read_pos = 0; ++ qcom_nandc_dev_to_mem(nandc, false); ++} ++ ++/** ++ * qcom_nandc_unalloc() - unallocate qpic nand controller ++ * @nandc: qpic nand controller ++ * ++ * This function will unallocate memory alloacted for qpic nand controller ++ */ ++void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) ++{ ++ if (nandc->props->supports_bam) { ++ if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) ++ dma_unmap_single(nandc->dev, nandc->reg_read_dma, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++ ++ if (nandc->tx_chan) ++ dma_release_channel(nandc->tx_chan); ++ ++ if (nandc->rx_chan) ++ dma_release_channel(nandc->rx_chan); ++ ++ if (nandc->cmd_chan) ++ dma_release_channel(nandc->cmd_chan); ++ } else { ++ if (nandc->chan) ++ dma_release_channel(nandc->chan); ++ } ++} ++ ++/** ++ * qcom_nandc_alloc() - Allocate qpic nand controller ++ * @nandc: qpic nand controller ++ * ++ * This function will allocate memory for qpic nand controller ++ */ ++int qcom_nandc_alloc(struct qcom_nand_controller *nandc) ++{ ++ int ret; ++ ++ ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); ++ if (ret) { ++ dev_err(nandc->dev, "failed to set DMA mask\n"); ++ return ret; ++ } ++ ++ /* ++ * we use the internal buffer for reading ONFI params, reading small ++ * data like ID and status, and preforming read-copy-write operations ++ * when writing to a codeword partially. 532 is the maximum possible ++ * size of a codeword for our nand controller ++ */ ++ nandc->buf_size = 532; ++ ++ nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, GFP_KERNEL); ++ if (!nandc->data_buffer) ++ return -ENOMEM; ++ ++ nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), GFP_KERNEL); ++ if (!nandc->regs) ++ return -ENOMEM; ++ ++ nandc->reg_read_buf = devm_kcalloc(nandc->dev, MAX_REG_RD, ++ sizeof(*nandc->reg_read_buf), ++ GFP_KERNEL); ++ if (!nandc->reg_read_buf) ++ return -ENOMEM; ++ ++ if (nandc->props->supports_bam) { ++ nandc->reg_read_dma = ++ dma_map_single(nandc->dev, nandc->reg_read_buf, ++ MAX_REG_RD * ++ sizeof(*nandc->reg_read_buf), ++ DMA_FROM_DEVICE); ++ if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { ++ dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); ++ return -EIO; ++ } ++ ++ nandc->tx_chan = dma_request_chan(nandc->dev, "tx"); ++ if (IS_ERR(nandc->tx_chan)) { ++ ret = PTR_ERR(nandc->tx_chan); ++ nandc->tx_chan = NULL; ++ dev_err_probe(nandc->dev, ret, ++ "tx DMA channel request failed\n"); ++ goto unalloc; ++ } ++ ++ nandc->rx_chan = dma_request_chan(nandc->dev, "rx"); ++ if (IS_ERR(nandc->rx_chan)) { ++ ret = PTR_ERR(nandc->rx_chan); ++ nandc->rx_chan = NULL; ++ dev_err_probe(nandc->dev, ret, ++ "rx DMA channel request failed\n"); ++ goto unalloc; ++ } ++ ++ nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd"); ++ if (IS_ERR(nandc->cmd_chan)) { ++ ret = PTR_ERR(nandc->cmd_chan); ++ nandc->cmd_chan = NULL; ++ dev_err_probe(nandc->dev, ret, ++ "cmd DMA channel request failed\n"); ++ goto unalloc; ++ } ++ ++ /* ++ * Initially allocate BAM transaction to read ONFI param page. ++ * After detecting all the devices, this BAM transaction will ++ * be freed and the next BAM transaction will be allocated with ++ * maximum codeword size ++ */ ++ nandc->max_cwperpage = 1; ++ nandc->bam_txn = qcom_alloc_bam_transaction(nandc); ++ if (!nandc->bam_txn) { ++ dev_err(nandc->dev, ++ "failed to allocate bam transaction\n"); ++ ret = -ENOMEM; ++ goto unalloc; ++ } ++ } else { ++ nandc->chan = dma_request_chan(nandc->dev, "rxtx"); ++ if (IS_ERR(nandc->chan)) { ++ ret = PTR_ERR(nandc->chan); ++ nandc->chan = NULL; ++ dev_err_probe(nandc->dev, ret, ++ "rxtx DMA channel request failed\n"); ++ return ret; ++ } ++ } ++ ++ INIT_LIST_HEAD(&nandc->desc_list); ++ INIT_LIST_HEAD(&nandc->host_list); ++ ++ return 0; ++unalloc: ++ qcom_nandc_unalloc(nandc); ++ return ret; ++} +--- a/drivers/mtd/nand/raw/Kconfig ++++ b/drivers/mtd/nand/raw/Kconfig +@@ -330,7 +330,7 @@ config MTD_NAND_HISI504 + Enables support for NAND controller on Hisilicon SoC Hip04. + + config MTD_NAND_QCOM +- tristate "QCOM NAND controller" ++ bool "QCOM NAND controller" + depends on ARCH_QCOM || COMPILE_TEST + depends on HAS_IOMEM + help +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -15,417 +15,7 @@ + #include + #include + #include +- +-/* NANDc reg offsets */ +-#define NAND_FLASH_CMD 0x00 +-#define NAND_ADDR0 0x04 +-#define NAND_ADDR1 0x08 +-#define NAND_FLASH_CHIP_SELECT 0x0c +-#define NAND_EXEC_CMD 0x10 +-#define NAND_FLASH_STATUS 0x14 +-#define NAND_BUFFER_STATUS 0x18 +-#define NAND_DEV0_CFG0 0x20 +-#define NAND_DEV0_CFG1 0x24 +-#define NAND_DEV0_ECC_CFG 0x28 +-#define NAND_AUTO_STATUS_EN 0x2c +-#define NAND_DEV1_CFG0 0x30 +-#define NAND_DEV1_CFG1 0x34 +-#define NAND_READ_ID 0x40 +-#define NAND_READ_STATUS 0x44 +-#define NAND_DEV_CMD0 0xa0 +-#define NAND_DEV_CMD1 0xa4 +-#define NAND_DEV_CMD2 0xa8 +-#define NAND_DEV_CMD_VLD 0xac +-#define SFLASHC_BURST_CFG 0xe0 +-#define NAND_ERASED_CW_DETECT_CFG 0xe8 +-#define NAND_ERASED_CW_DETECT_STATUS 0xec +-#define NAND_EBI2_ECC_BUF_CFG 0xf0 +-#define FLASH_BUF_ACC 0x100 +- +-#define NAND_CTRL 0xf00 +-#define NAND_VERSION 0xf08 +-#define NAND_READ_LOCATION_0 0xf20 +-#define NAND_READ_LOCATION_1 0xf24 +-#define NAND_READ_LOCATION_2 0xf28 +-#define NAND_READ_LOCATION_3 0xf2c +-#define NAND_READ_LOCATION_LAST_CW_0 0xf40 +-#define NAND_READ_LOCATION_LAST_CW_1 0xf44 +-#define NAND_READ_LOCATION_LAST_CW_2 0xf48 +-#define NAND_READ_LOCATION_LAST_CW_3 0xf4c +- +-/* dummy register offsets, used by qcom_write_reg_dma */ +-#define NAND_DEV_CMD1_RESTORE 0xdead +-#define NAND_DEV_CMD_VLD_RESTORE 0xbeef +- +-/* NAND_FLASH_CMD bits */ +-#define PAGE_ACC BIT(4) +-#define LAST_PAGE BIT(5) +- +-/* NAND_FLASH_CHIP_SELECT bits */ +-#define NAND_DEV_SEL 0 +-#define DM_EN BIT(2) +- +-/* NAND_FLASH_STATUS bits */ +-#define FS_OP_ERR BIT(4) +-#define FS_READY_BSY_N BIT(5) +-#define FS_MPU_ERR BIT(8) +-#define FS_DEVICE_STS_ERR BIT(16) +-#define FS_DEVICE_WP BIT(23) +- +-/* NAND_BUFFER_STATUS bits */ +-#define BS_UNCORRECTABLE_BIT BIT(8) +-#define BS_CORRECTABLE_ERR_MSK 0x1f +- +-/* NAND_DEVn_CFG0 bits */ +-#define DISABLE_STATUS_AFTER_WRITE 4 +-#define CW_PER_PAGE 6 +-#define UD_SIZE_BYTES 9 +-#define UD_SIZE_BYTES_MASK GENMASK(18, 9) +-#define ECC_PARITY_SIZE_BYTES_RS 19 +-#define SPARE_SIZE_BYTES 23 +-#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) +-#define NUM_ADDR_CYCLES 27 +-#define STATUS_BFR_READ 30 +-#define SET_RD_MODE_AFTER_STATUS 31 +- +-/* NAND_DEVn_CFG0 bits */ +-#define DEV0_CFG1_ECC_DISABLE 0 +-#define WIDE_FLASH 1 +-#define NAND_RECOVERY_CYCLES 2 +-#define CS_ACTIVE_BSY 5 +-#define BAD_BLOCK_BYTE_NUM 6 +-#define BAD_BLOCK_IN_SPARE_AREA 16 +-#define WR_RD_BSY_GAP 17 +-#define ENABLE_BCH_ECC 27 +- +-/* NAND_DEV0_ECC_CFG bits */ +-#define ECC_CFG_ECC_DISABLE 0 +-#define ECC_SW_RESET 1 +-#define ECC_MODE 4 +-#define ECC_PARITY_SIZE_BYTES_BCH 8 +-#define ECC_NUM_DATA_BYTES 16 +-#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) +-#define ECC_FORCE_CLK_OPEN 30 +- +-/* NAND_DEV_CMD1 bits */ +-#define READ_ADDR 0 +- +-/* NAND_DEV_CMD_VLD bits */ +-#define READ_START_VLD BIT(0) +-#define READ_STOP_VLD BIT(1) +-#define WRITE_START_VLD BIT(2) +-#define ERASE_START_VLD BIT(3) +-#define SEQ_READ_START_VLD BIT(4) +- +-/* NAND_EBI2_ECC_BUF_CFG bits */ +-#define NUM_STEPS 0 +- +-/* NAND_ERASED_CW_DETECT_CFG bits */ +-#define ERASED_CW_ECC_MASK 1 +-#define AUTO_DETECT_RES 0 +-#define MASK_ECC BIT(ERASED_CW_ECC_MASK) +-#define RESET_ERASED_DET BIT(AUTO_DETECT_RES) +-#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) +-#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) +-#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) +- +-/* NAND_ERASED_CW_DETECT_STATUS bits */ +-#define PAGE_ALL_ERASED BIT(7) +-#define CODEWORD_ALL_ERASED BIT(6) +-#define PAGE_ERASED BIT(5) +-#define CODEWORD_ERASED BIT(4) +-#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) +-#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) +- +-/* NAND_READ_LOCATION_n bits */ +-#define READ_LOCATION_OFFSET 0 +-#define READ_LOCATION_SIZE 16 +-#define READ_LOCATION_LAST 31 +- +-/* Version Mask */ +-#define NAND_VERSION_MAJOR_MASK 0xf0000000 +-#define NAND_VERSION_MAJOR_SHIFT 28 +-#define NAND_VERSION_MINOR_MASK 0x0fff0000 +-#define NAND_VERSION_MINOR_SHIFT 16 +- +-/* NAND OP_CMDs */ +-#define OP_PAGE_READ 0x2 +-#define OP_PAGE_READ_WITH_ECC 0x3 +-#define OP_PAGE_READ_WITH_ECC_SPARE 0x4 +-#define OP_PAGE_READ_ONFI_READ 0x5 +-#define OP_PROGRAM_PAGE 0x6 +-#define OP_PAGE_PROGRAM_WITH_ECC 0x7 +-#define OP_PROGRAM_PAGE_SPARE 0x9 +-#define OP_BLOCK_ERASE 0xa +-#define OP_CHECK_STATUS 0xc +-#define OP_FETCH_ID 0xb +-#define OP_RESET_DEVICE 0xd +- +-/* Default Value for NAND_DEV_CMD_VLD */ +-#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ +- ERASE_START_VLD | SEQ_READ_START_VLD) +- +-/* NAND_CTRL bits */ +-#define BAM_MODE_EN BIT(0) +- +-/* +- * the NAND controller performs reads/writes with ECC in 516 byte chunks. +- * the driver calls the chunks 'step' or 'codeword' interchangeably +- */ +-#define NANDC_STEP_SIZE 512 +- +-/* +- * the largest page size we support is 8K, this will have 16 steps/codewords +- * of 512 bytes each +- */ +-#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) +- +-/* we read at most 3 registers per codeword scan */ +-#define MAX_REG_RD (3 * MAX_NUM_STEPS) +- +-/* ECC modes supported by the controller */ +-#define ECC_NONE BIT(0) +-#define ECC_RS_4BIT BIT(1) +-#define ECC_BCH_4BIT BIT(2) +-#define ECC_BCH_8BIT BIT(3) +- +-/* +- * Returns the actual register address for all NAND_DEV_ registers +- * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) +- */ +-#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) +- +-/* Returns the NAND register physical address */ +-#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) +- +-/* Returns the dma address for reg read buffer */ +-#define reg_buf_dma_addr(chip, vaddr) \ +- ((chip)->reg_read_dma + \ +- ((u8 *)(vaddr) - (u8 *)(chip)->reg_read_buf)) +- +-#define QPIC_PER_CW_CMD_ELEMENTS 32 +-#define QPIC_PER_CW_CMD_SGL 32 +-#define QPIC_PER_CW_DATA_SGL 8 +- +-#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) +- +-/* +- * Flags used in DMA descriptor preparation helper functions +- * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma) +- */ +-/* Don't set the EOT in current tx BAM sgl */ +-#define NAND_BAM_NO_EOT BIT(0) +-/* Set the NWD flag in current BAM sgl */ +-#define NAND_BAM_NWD BIT(1) +-/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ +-#define NAND_BAM_NEXT_SGL BIT(2) +-/* +- * Erased codeword status is being used two times in single transfer so this +- * flag will determine the current value of erased codeword status register +- */ +-#define NAND_ERASED_CW_SET BIT(4) +- +-#define MAX_ADDRESS_CYCLE 5 +- +-/* +- * This data type corresponds to the BAM transaction which will be used for all +- * NAND transfers. +- * @bam_ce - the array of BAM command elements +- * @cmd_sgl - sgl for NAND BAM command pipe +- * @data_sgl - sgl for NAND BAM consumer/producer pipe +- * @last_data_desc - last DMA desc in data channel (tx/rx). +- * @last_cmd_desc - last DMA desc in command channel. +- * @txn_done - completion for NAND transfer. +- * @bam_ce_pos - the index in bam_ce which is available for next sgl +- * @bam_ce_start - the index in bam_ce which marks the start position ce +- * for current sgl. It will be used for size calculation +- * for current sgl +- * @cmd_sgl_pos - current index in command sgl. +- * @cmd_sgl_start - start index in command sgl. +- * @tx_sgl_pos - current index in data sgl for tx. +- * @tx_sgl_start - start index in data sgl for tx. +- * @rx_sgl_pos - current index in data sgl for rx. +- * @rx_sgl_start - start index in data sgl for rx. +- */ +-struct bam_transaction { +- struct bam_cmd_element *bam_ce; +- struct scatterlist *cmd_sgl; +- struct scatterlist *data_sgl; +- struct dma_async_tx_descriptor *last_data_desc; +- struct dma_async_tx_descriptor *last_cmd_desc; +- struct completion txn_done; +- u32 bam_ce_pos; +- u32 bam_ce_start; +- u32 cmd_sgl_pos; +- u32 cmd_sgl_start; +- u32 tx_sgl_pos; +- u32 tx_sgl_start; +- u32 rx_sgl_pos; +- u32 rx_sgl_start; +-}; +- +-/* +- * This data type corresponds to the nand dma descriptor +- * @dma_desc - low level DMA engine descriptor +- * @list - list for desc_info +- * +- * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by +- * ADM +- * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM +- * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM +- * @dir - DMA transfer direction +- */ +-struct desc_info { +- struct dma_async_tx_descriptor *dma_desc; +- struct list_head node; +- +- union { +- struct scatterlist adm_sgl; +- struct { +- struct scatterlist *bam_sgl; +- int sgl_cnt; +- }; +- }; +- enum dma_data_direction dir; +-}; +- +-/* +- * holds the current register values that we want to write. acts as a contiguous +- * chunk of memory which we use to write the controller registers through DMA. +- */ +-struct nandc_regs { +- __le32 cmd; +- __le32 addr0; +- __le32 addr1; +- __le32 chip_sel; +- __le32 exec; +- +- __le32 cfg0; +- __le32 cfg1; +- __le32 ecc_bch_cfg; +- +- __le32 clrflashstatus; +- __le32 clrreadstatus; +- +- __le32 cmd1; +- __le32 vld; +- +- __le32 orig_cmd1; +- __le32 orig_vld; +- +- __le32 ecc_buf_cfg; +- __le32 read_location0; +- __le32 read_location1; +- __le32 read_location2; +- __le32 read_location3; +- __le32 read_location_last0; +- __le32 read_location_last1; +- __le32 read_location_last2; +- __le32 read_location_last3; +- +- __le32 erased_cw_detect_cfg_clr; +- __le32 erased_cw_detect_cfg_set; +-}; +- +-/* +- * NAND controller data struct +- * +- * @dev: parent device +- * +- * @base: MMIO base +- * +- * @core_clk: controller clock +- * @aon_clk: another controller clock +- * +- * @regs: a contiguous chunk of memory for DMA register +- * writes. contains the register values to be +- * written to controller +- * +- * @props: properties of current NAND controller, +- * initialized via DT match data +- * +- * @controller: base controller structure +- * @host_list: list containing all the chips attached to the +- * controller +- * +- * @chan: dma channel +- * @cmd_crci: ADM DMA CRCI for command flow control +- * @data_crci: ADM DMA CRCI for data flow control +- * +- * @desc_list: DMA descriptor list (list of desc_infos) +- * +- * @data_buffer: our local DMA buffer for page read/writes, +- * used when we can't use the buffer provided +- * by upper layers directly +- * @reg_read_buf: local buffer for reading back registers via DMA +- * +- * @base_phys: physical base address of controller registers +- * @base_dma: dma base address of controller registers +- * @reg_read_dma: contains dma address for register read buffer +- * +- * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf +- * functions +- * @max_cwperpage: maximum QPIC codewords required. calculated +- * from all connected NAND devices pagesize +- * +- * @reg_read_pos: marker for data read in reg_read_buf +- * +- * @cmd1/vld: some fixed controller register values +- * +- * @exec_opwrite: flag to select correct number of code word +- * while reading status +- */ +-struct qcom_nand_controller { +- struct device *dev; +- +- void __iomem *base; +- +- struct clk *core_clk; +- struct clk *aon_clk; +- +- struct nandc_regs *regs; +- struct bam_transaction *bam_txn; +- +- const struct qcom_nandc_props *props; +- +- struct nand_controller controller; +- struct list_head host_list; +- +- union { +- /* will be used only by QPIC for BAM DMA */ +- struct { +- struct dma_chan *tx_chan; +- struct dma_chan *rx_chan; +- struct dma_chan *cmd_chan; +- }; +- +- /* will be used only by EBI2 for ADM DMA */ +- struct { +- struct dma_chan *chan; +- unsigned int cmd_crci; +- unsigned int data_crci; +- }; +- }; +- +- struct list_head desc_list; +- +- u8 *data_buffer; +- __le32 *reg_read_buf; +- +- phys_addr_t base_phys; +- dma_addr_t base_dma; +- dma_addr_t reg_read_dma; +- +- int buf_size; +- int buf_count; +- int buf_start; +- unsigned int max_cwperpage; +- +- int reg_read_pos; +- +- u32 cmd1, vld; +- bool exec_opwrite; +-}; ++#include + + /* + * NAND special boot partitions +@@ -530,97 +120,6 @@ struct qcom_nand_host { + bool bch_enabled; + }; + +-/* +- * This data type corresponds to the NAND controller properties which varies +- * among different NAND controllers. +- * @ecc_modes - ecc mode for NAND +- * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset +- * @supports_bam - whether NAND controller is using BAM +- * @nandc_part_of_qpic - whether NAND controller is part of qpic IP +- * @qpic_version2 - flag to indicate QPIC IP version 2 +- * @use_codeword_fixup - whether NAND has different layout for boot partitions +- */ +-struct qcom_nandc_props { +- u32 ecc_modes; +- u32 dev_cmd_reg_start; +- bool supports_bam; +- bool nandc_part_of_qpic; +- bool qpic_version2; +- bool use_codeword_fixup; +-}; +- +-/* Frees the BAM transaction memory */ +-static void qcom_free_bam_transaction(struct qcom_nand_controller *nandc) +-{ +- struct bam_transaction *bam_txn = nandc->bam_txn; +- +- devm_kfree(nandc->dev, bam_txn); +-} +- +-/* Allocates and Initializes the BAM transaction */ +-static struct bam_transaction * +-qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc) +-{ +- struct bam_transaction *bam_txn; +- size_t bam_txn_size; +- unsigned int num_cw = nandc->max_cwperpage; +- void *bam_txn_buf; +- +- bam_txn_size = +- sizeof(*bam_txn) + num_cw * +- ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + +- (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + +- (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); +- +- bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); +- if (!bam_txn_buf) +- return NULL; +- +- bam_txn = bam_txn_buf; +- bam_txn_buf += sizeof(*bam_txn); +- +- bam_txn->bam_ce = bam_txn_buf; +- bam_txn_buf += +- sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; +- +- bam_txn->cmd_sgl = bam_txn_buf; +- bam_txn_buf += +- sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; +- +- bam_txn->data_sgl = bam_txn_buf; +- +- init_completion(&bam_txn->txn_done); +- +- return bam_txn; +-} +- +-/* Clears the BAM transaction indexes */ +-static void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc) +-{ +- struct bam_transaction *bam_txn = nandc->bam_txn; +- +- if (!nandc->props->supports_bam) +- return; +- +- memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8); +- bam_txn->last_data_desc = NULL; +- +- sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * +- QPIC_PER_CW_CMD_SGL); +- sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * +- QPIC_PER_CW_DATA_SGL); +- +- reinit_completion(&bam_txn->txn_done); +-} +- +-/* Callback for DMA descriptor completion */ +-static void qcom_qpic_bam_dma_done(void *data) +-{ +- struct bam_transaction *bam_txn = data; +- +- complete(&bam_txn->txn_done); +-} +- + static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) + { + return container_of(chip, struct qcom_nand_host, chip); +@@ -629,8 +128,8 @@ static inline struct qcom_nand_host *to_ + static inline struct qcom_nand_controller * + get_qcom_nand_controller(struct nand_chip *chip) + { +- return container_of(chip->controller, struct qcom_nand_controller, +- controller); ++ return (struct qcom_nand_controller *) ++ ((u8 *)chip->controller - sizeof(struct qcom_nand_controller)); + } + + static inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset) +@@ -644,23 +143,6 @@ static inline void nandc_write(struct qc + iowrite32(val, nandc->base + offset); + } + +-static inline void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu) +-{ +- if (!nandc->props->supports_bam) +- return; +- +- if (is_cpu) +- dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, +- MAX_REG_RD * +- sizeof(*nandc->reg_read_buf), +- DMA_FROM_DEVICE); +- else +- dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, +- MAX_REG_RD * +- sizeof(*nandc->reg_read_buf), +- DMA_FROM_DEVICE); +-} +- + /* Helper to check the code word, whether it is last cw or not */ + static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) + { +@@ -820,356 +302,6 @@ static void update_rw_regs(struct qcom_n + } + + /* +- * Maps the scatter gather list for DMA transfer and forms the DMA descriptor +- * for BAM. This descriptor will be added in the NAND DMA descriptor queue +- * which will be submitted to DMA engine. +- */ +-static int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc, +- struct dma_chan *chan, +- unsigned long flags) +-{ +- struct desc_info *desc; +- struct scatterlist *sgl; +- unsigned int sgl_cnt; +- int ret; +- struct bam_transaction *bam_txn = nandc->bam_txn; +- enum dma_transfer_direction dir_eng; +- struct dma_async_tx_descriptor *dma_desc; +- +- desc = kzalloc(sizeof(*desc), GFP_KERNEL); +- if (!desc) +- return -ENOMEM; +- +- if (chan == nandc->cmd_chan) { +- sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; +- sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; +- bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; +- dir_eng = DMA_MEM_TO_DEV; +- desc->dir = DMA_TO_DEVICE; +- } else if (chan == nandc->tx_chan) { +- sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; +- sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; +- bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; +- dir_eng = DMA_MEM_TO_DEV; +- desc->dir = DMA_TO_DEVICE; +- } else { +- sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; +- sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; +- bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; +- dir_eng = DMA_DEV_TO_MEM; +- desc->dir = DMA_FROM_DEVICE; +- } +- +- sg_mark_end(sgl + sgl_cnt - 1); +- ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); +- if (ret == 0) { +- dev_err(nandc->dev, "failure in mapping desc\n"); +- kfree(desc); +- return -ENOMEM; +- } +- +- desc->sgl_cnt = sgl_cnt; +- desc->bam_sgl = sgl; +- +- dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, +- flags); +- +- if (!dma_desc) { +- dev_err(nandc->dev, "failure in prep desc\n"); +- dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); +- kfree(desc); +- return -EINVAL; +- } +- +- desc->dma_desc = dma_desc; +- +- /* update last data/command descriptor */ +- if (chan == nandc->cmd_chan) +- bam_txn->last_cmd_desc = dma_desc; +- else +- bam_txn->last_data_desc = dma_desc; +- +- list_add_tail(&desc->node, &nandc->desc_list); +- +- return 0; +-} +- +-/* +- * Prepares the command descriptor for BAM DMA which will be used for NAND +- * register reads and writes. The command descriptor requires the command +- * to be formed in command element type so this function uses the command +- * element from bam transaction ce array and fills the same with required +- * data. A single SGL can contain multiple command elements so +- * NAND_BAM_NEXT_SGL will be used for starting the separate SGL +- * after the current command element. +- */ +-static int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, +- int reg_off, const void *vaddr, +- int size, unsigned int flags) +-{ +- int bam_ce_size; +- int i, ret; +- struct bam_cmd_element *bam_ce_buffer; +- struct bam_transaction *bam_txn = nandc->bam_txn; +- +- bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; +- +- /* fill the command desc */ +- for (i = 0; i < size; i++) { +- if (read) +- bam_prep_ce(&bam_ce_buffer[i], +- nandc_reg_phys(nandc, reg_off + 4 * i), +- BAM_READ_COMMAND, +- reg_buf_dma_addr(nandc, +- (__le32 *)vaddr + i)); +- else +- bam_prep_ce_le32(&bam_ce_buffer[i], +- nandc_reg_phys(nandc, reg_off + 4 * i), +- BAM_WRITE_COMMAND, +- *((__le32 *)vaddr + i)); +- } +- +- bam_txn->bam_ce_pos += size; +- +- /* use the separate sgl after this command */ +- if (flags & NAND_BAM_NEXT_SGL) { +- bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; +- bam_ce_size = (bam_txn->bam_ce_pos - +- bam_txn->bam_ce_start) * +- sizeof(struct bam_cmd_element); +- sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], +- bam_ce_buffer, bam_ce_size); +- bam_txn->cmd_sgl_pos++; +- bam_txn->bam_ce_start = bam_txn->bam_ce_pos; +- +- if (flags & NAND_BAM_NWD) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, +- DMA_PREP_FENCE | +- DMA_PREP_CMD); +- if (ret) +- return ret; +- } +- } +- +- return 0; +-} +- +-/* +- * Prepares the data descriptor for BAM DMA which will be used for NAND +- * data reads and writes. +- */ +-static int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, +- const void *vaddr, int size, unsigned int flags) +-{ +- int ret; +- struct bam_transaction *bam_txn = nandc->bam_txn; +- +- if (read) { +- sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], +- vaddr, size); +- bam_txn->rx_sgl_pos++; +- } else { +- sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], +- vaddr, size); +- bam_txn->tx_sgl_pos++; +- +- /* +- * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag +- * is not set, form the DMA descriptor +- */ +- if (!(flags & NAND_BAM_NO_EOT)) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, +- DMA_PREP_INTERRUPT); +- if (ret) +- return ret; +- } +- } +- +- return 0; +-} +- +-static int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, +- int reg_off, const void *vaddr, int size, +- bool flow_control) +-{ +- struct desc_info *desc; +- struct dma_async_tx_descriptor *dma_desc; +- struct scatterlist *sgl; +- struct dma_slave_config slave_conf; +- struct qcom_adm_peripheral_config periph_conf = {}; +- enum dma_transfer_direction dir_eng; +- int ret; +- +- desc = kzalloc(sizeof(*desc), GFP_KERNEL); +- if (!desc) +- return -ENOMEM; +- +- sgl = &desc->adm_sgl; +- +- sg_init_one(sgl, vaddr, size); +- +- if (read) { +- dir_eng = DMA_DEV_TO_MEM; +- desc->dir = DMA_FROM_DEVICE; +- } else { +- dir_eng = DMA_MEM_TO_DEV; +- desc->dir = DMA_TO_DEVICE; +- } +- +- ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); +- if (ret == 0) { +- ret = -ENOMEM; +- goto err; +- } +- +- memset(&slave_conf, 0x00, sizeof(slave_conf)); +- +- slave_conf.device_fc = flow_control; +- if (read) { +- slave_conf.src_maxburst = 16; +- slave_conf.src_addr = nandc->base_dma + reg_off; +- if (nandc->data_crci) { +- periph_conf.crci = nandc->data_crci; +- slave_conf.peripheral_config = &periph_conf; +- slave_conf.peripheral_size = sizeof(periph_conf); +- } +- } else { +- slave_conf.dst_maxburst = 16; +- slave_conf.dst_addr = nandc->base_dma + reg_off; +- if (nandc->cmd_crci) { +- periph_conf.crci = nandc->cmd_crci; +- slave_conf.peripheral_config = &periph_conf; +- slave_conf.peripheral_size = sizeof(periph_conf); +- } +- } +- +- ret = dmaengine_slave_config(nandc->chan, &slave_conf); +- if (ret) { +- dev_err(nandc->dev, "failed to configure dma channel\n"); +- goto err; +- } +- +- dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); +- if (!dma_desc) { +- dev_err(nandc->dev, "failed to prepare desc\n"); +- ret = -EINVAL; +- goto err; +- } +- +- desc->dma_desc = dma_desc; +- +- list_add_tail(&desc->node, &nandc->desc_list); +- +- return 0; +-err: +- kfree(desc); +- +- return ret; +-} +- +-/* +- * qcom_read_reg_dma: prepares a descriptor to read a given number of +- * contiguous registers to the reg_read_buf pointer +- * +- * @first: offset of the first register in the contiguous block +- * @num_regs: number of registers to read +- * @flags: flags to control DMA descriptor preparation +- */ +-static int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first, +- int num_regs, unsigned int flags) +-{ +- bool flow_control = false; +- void *vaddr; +- +- vaddr = nandc->reg_read_buf + nandc->reg_read_pos; +- nandc->reg_read_pos += num_regs; +- +- if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) +- first = dev_cmd_reg_addr(nandc, first); +- +- if (nandc->props->supports_bam) +- return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr, +- num_regs, flags); +- +- if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) +- flow_control = true; +- +- return qcom_prep_adm_dma_desc(nandc, true, first, vaddr, +- num_regs * sizeof(u32), flow_control); +-} +- +-/* +- * qcom_write_reg_dma: prepares a descriptor to write a given number of +- * contiguous registers +- * +- * @vaddr: contnigeous memory from where register value will +- * be written +- * @first: offset of the first register in the contiguous block +- * @num_regs: number of registers to write +- * @flags: flags to control DMA descriptor preparation +- */ +-static int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, +- int first, int num_regs, unsigned int flags) +-{ +- bool flow_control = false; +- +- if (first == NAND_EXEC_CMD) +- flags |= NAND_BAM_NWD; +- +- if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) +- first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); +- +- if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) +- first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); +- +- if (nandc->props->supports_bam) +- return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr, +- num_regs, flags); +- +- if (first == NAND_FLASH_CMD) +- flow_control = true; +- +- return qcom_prep_adm_dma_desc(nandc, false, first, vaddr, +- num_regs * sizeof(u32), flow_control); +-} +- +-/* +- * qcom_read_data_dma: prepares a DMA descriptor to transfer data from the +- * controller's internal buffer to the buffer 'vaddr' +- * +- * @reg_off: offset within the controller's data buffer +- * @vaddr: virtual address of the buffer we want to write to +- * @size: DMA transaction size in bytes +- * @flags: flags to control DMA descriptor preparation +- */ +-static int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size, unsigned int flags) +-{ +- if (nandc->props->supports_bam) +- return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); +- +- return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); +-} +- +-/* +- * qcom_write_data_dma: prepares a DMA descriptor to transfer data from +- * 'vaddr' to the controller's internal buffer +- * +- * @reg_off: offset within the controller's data buffer +- * @vaddr: virtual address of the buffer we want to read from +- * @size: DMA transaction size in bytes +- * @flags: flags to control DMA descriptor preparation +- */ +-static int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off, +- const u8 *vaddr, int size, unsigned int flags) +-{ +- if (nandc->props->supports_bam) +- return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); +- +- return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); +-} +- +-/* + * Helper to prepare DMA descriptors for configuring registers + * before reading a NAND page. + */ +@@ -1262,83 +394,6 @@ static void config_nand_cw_write(struct + NAND_BAM_NEXT_SGL); + } + +-/* helpers to submit/free our list of dma descriptors */ +-static int qcom_submit_descs(struct qcom_nand_controller *nandc) +-{ +- struct desc_info *desc, *n; +- dma_cookie_t cookie = 0; +- struct bam_transaction *bam_txn = nandc->bam_txn; +- int ret = 0; +- +- if (nandc->props->supports_bam) { +- if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0); +- if (ret) +- goto err_unmap_free_desc; +- } +- +- if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan, +- DMA_PREP_INTERRUPT); +- if (ret) +- goto err_unmap_free_desc; +- } +- +- if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { +- ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan, +- DMA_PREP_CMD); +- if (ret) +- goto err_unmap_free_desc; +- } +- } +- +- list_for_each_entry(desc, &nandc->desc_list, node) +- cookie = dmaengine_submit(desc->dma_desc); +- +- if (nandc->props->supports_bam) { +- bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done; +- bam_txn->last_cmd_desc->callback_param = bam_txn; +- +- dma_async_issue_pending(nandc->tx_chan); +- dma_async_issue_pending(nandc->rx_chan); +- dma_async_issue_pending(nandc->cmd_chan); +- +- if (!wait_for_completion_timeout(&bam_txn->txn_done, +- QPIC_NAND_COMPLETION_TIMEOUT)) +- ret = -ETIMEDOUT; +- } else { +- if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) +- ret = -ETIMEDOUT; +- } +- +-err_unmap_free_desc: +- /* +- * Unmap the dma sg_list and free the desc allocated by both +- * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions. +- */ +- list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { +- list_del(&desc->node); +- +- if (nandc->props->supports_bam) +- dma_unmap_sg(nandc->dev, desc->bam_sgl, +- desc->sgl_cnt, desc->dir); +- else +- dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, +- desc->dir); +- +- kfree(desc); +- } +- +- return ret; +-} +- +-/* reset the register read buffer for next NAND operation */ +-static void qcom_clear_read_regs(struct qcom_nand_controller *nandc) +-{ +- nandc->reg_read_pos = 0; +- qcom_nandc_dev_to_mem(nandc, false); +-} +- + /* + * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read + * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS. +@@ -2967,141 +2022,14 @@ static const struct nand_controller_ops + .exec_op = qcom_nand_exec_op, + }; + +-static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) +-{ +- if (nandc->props->supports_bam) { +- if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) +- dma_unmap_single(nandc->dev, nandc->reg_read_dma, +- MAX_REG_RD * +- sizeof(*nandc->reg_read_buf), +- DMA_FROM_DEVICE); +- +- if (nandc->tx_chan) +- dma_release_channel(nandc->tx_chan); +- +- if (nandc->rx_chan) +- dma_release_channel(nandc->rx_chan); +- +- if (nandc->cmd_chan) +- dma_release_channel(nandc->cmd_chan); +- } else { +- if (nandc->chan) +- dma_release_channel(nandc->chan); +- } +-} +- +-static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) +-{ +- int ret; +- +- ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); +- if (ret) { +- dev_err(nandc->dev, "failed to set DMA mask\n"); +- return ret; +- } +- +- /* +- * we use the internal buffer for reading ONFI params, reading small +- * data like ID and status, and preforming read-copy-write operations +- * when writing to a codeword partially. 532 is the maximum possible +- * size of a codeword for our nand controller +- */ +- nandc->buf_size = 532; +- +- nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, GFP_KERNEL); +- if (!nandc->data_buffer) +- return -ENOMEM; +- +- nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), GFP_KERNEL); +- if (!nandc->regs) +- return -ENOMEM; +- +- nandc->reg_read_buf = devm_kcalloc(nandc->dev, MAX_REG_RD, +- sizeof(*nandc->reg_read_buf), +- GFP_KERNEL); +- if (!nandc->reg_read_buf) +- return -ENOMEM; +- +- if (nandc->props->supports_bam) { +- nandc->reg_read_dma = +- dma_map_single(nandc->dev, nandc->reg_read_buf, +- MAX_REG_RD * +- sizeof(*nandc->reg_read_buf), +- DMA_FROM_DEVICE); +- if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { +- dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); +- return -EIO; +- } +- +- nandc->tx_chan = dma_request_chan(nandc->dev, "tx"); +- if (IS_ERR(nandc->tx_chan)) { +- ret = PTR_ERR(nandc->tx_chan); +- nandc->tx_chan = NULL; +- dev_err_probe(nandc->dev, ret, +- "tx DMA channel request failed\n"); +- goto unalloc; +- } +- +- nandc->rx_chan = dma_request_chan(nandc->dev, "rx"); +- if (IS_ERR(nandc->rx_chan)) { +- ret = PTR_ERR(nandc->rx_chan); +- nandc->rx_chan = NULL; +- dev_err_probe(nandc->dev, ret, +- "rx DMA channel request failed\n"); +- goto unalloc; +- } +- +- nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd"); +- if (IS_ERR(nandc->cmd_chan)) { +- ret = PTR_ERR(nandc->cmd_chan); +- nandc->cmd_chan = NULL; +- dev_err_probe(nandc->dev, ret, +- "cmd DMA channel request failed\n"); +- goto unalloc; +- } +- +- /* +- * Initially allocate BAM transaction to read ONFI param page. +- * After detecting all the devices, this BAM transaction will +- * be freed and the next BAM transaction will be allocated with +- * maximum codeword size +- */ +- nandc->max_cwperpage = 1; +- nandc->bam_txn = qcom_alloc_bam_transaction(nandc); +- if (!nandc->bam_txn) { +- dev_err(nandc->dev, +- "failed to allocate bam transaction\n"); +- ret = -ENOMEM; +- goto unalloc; +- } +- } else { +- nandc->chan = dma_request_chan(nandc->dev, "rxtx"); +- if (IS_ERR(nandc->chan)) { +- ret = PTR_ERR(nandc->chan); +- nandc->chan = NULL; +- dev_err_probe(nandc->dev, ret, +- "rxtx DMA channel request failed\n"); +- return ret; +- } +- } +- +- INIT_LIST_HEAD(&nandc->desc_list); +- INIT_LIST_HEAD(&nandc->host_list); +- +- nand_controller_init(&nandc->controller); +- nandc->controller.ops = &qcom_nandc_ops; +- +- return 0; +-unalloc: +- qcom_nandc_unalloc(nandc); +- return ret; +-} +- + /* one time setup of a few nand controller registers */ + static int qcom_nandc_setup(struct qcom_nand_controller *nandc) + { + u32 nand_ctrl; + ++ nand_controller_init(nandc->controller); ++ nandc->controller->ops = &qcom_nandc_ops; ++ + /* kill onenand */ + if (!nandc->props->nandc_part_of_qpic) + nandc_write(nandc, SFLASHC_BURST_CFG, 0); +@@ -3240,7 +2168,7 @@ static int qcom_nand_host_init_and_regis + chip->legacy.block_bad = qcom_nandc_block_bad; + chip->legacy.block_markbad = qcom_nandc_block_markbad; + +- chip->controller = &nandc->controller; ++ chip->controller = nandc->controller; + chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA | + NAND_SKIP_BBTSCAN; + +@@ -3323,17 +2251,21 @@ static int qcom_nandc_parse_dt(struct pl + static int qcom_nandc_probe(struct platform_device *pdev) + { + struct qcom_nand_controller *nandc; ++ struct nand_controller *controller; + const void *dev_data; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + +- nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL); ++ nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc) + sizeof(*controller), ++ GFP_KERNEL); + if (!nandc) + return -ENOMEM; ++ controller = (struct nand_controller *)&nandc[1]; + + platform_set_drvdata(pdev, nandc); + nandc->dev = dev; ++ nandc->controller = controller; + + dev_data = of_device_get_match_data(dev); + if (!dev_data) { +--- /dev/null ++++ b/include/linux/mtd/nand-qpic-common.h +@@ -0,0 +1,468 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * QCOM QPIC common APIs header file ++ * ++ * Copyright (c) 2023 Qualcomm Inc. ++ * Authors: Md sadre Alam ++ * ++ */ ++#ifndef __MTD_NAND_QPIC_COMMON_H__ ++#define __MTD_NAND_QPIC_COMMON_H__ ++ ++/* NANDc reg offsets */ ++#define NAND_FLASH_CMD 0x00 ++#define NAND_ADDR0 0x04 ++#define NAND_ADDR1 0x08 ++#define NAND_FLASH_CHIP_SELECT 0x0c ++#define NAND_EXEC_CMD 0x10 ++#define NAND_FLASH_STATUS 0x14 ++#define NAND_BUFFER_STATUS 0x18 ++#define NAND_DEV0_CFG0 0x20 ++#define NAND_DEV0_CFG1 0x24 ++#define NAND_DEV0_ECC_CFG 0x28 ++#define NAND_AUTO_STATUS_EN 0x2c ++#define NAND_DEV1_CFG0 0x30 ++#define NAND_DEV1_CFG1 0x34 ++#define NAND_READ_ID 0x40 ++#define NAND_READ_STATUS 0x44 ++#define NAND_DEV_CMD0 0xa0 ++#define NAND_DEV_CMD1 0xa4 ++#define NAND_DEV_CMD2 0xa8 ++#define NAND_DEV_CMD_VLD 0xac ++#define SFLASHC_BURST_CFG 0xe0 ++#define NAND_ERASED_CW_DETECT_CFG 0xe8 ++#define NAND_ERASED_CW_DETECT_STATUS 0xec ++#define NAND_EBI2_ECC_BUF_CFG 0xf0 ++#define FLASH_BUF_ACC 0x100 ++ ++#define NAND_CTRL 0xf00 ++#define NAND_VERSION 0xf08 ++#define NAND_READ_LOCATION_0 0xf20 ++#define NAND_READ_LOCATION_1 0xf24 ++#define NAND_READ_LOCATION_2 0xf28 ++#define NAND_READ_LOCATION_3 0xf2c ++#define NAND_READ_LOCATION_LAST_CW_0 0xf40 ++#define NAND_READ_LOCATION_LAST_CW_1 0xf44 ++#define NAND_READ_LOCATION_LAST_CW_2 0xf48 ++#define NAND_READ_LOCATION_LAST_CW_3 0xf4c ++ ++/* dummy register offsets, used by qcom_write_reg_dma */ ++#define NAND_DEV_CMD1_RESTORE 0xdead ++#define NAND_DEV_CMD_VLD_RESTORE 0xbeef ++ ++/* NAND_FLASH_CMD bits */ ++#define PAGE_ACC BIT(4) ++#define LAST_PAGE BIT(5) ++ ++/* NAND_FLASH_CHIP_SELECT bits */ ++#define NAND_DEV_SEL 0 ++#define DM_EN BIT(2) ++ ++/* NAND_FLASH_STATUS bits */ ++#define FS_OP_ERR BIT(4) ++#define FS_READY_BSY_N BIT(5) ++#define FS_MPU_ERR BIT(8) ++#define FS_DEVICE_STS_ERR BIT(16) ++#define FS_DEVICE_WP BIT(23) ++ ++/* NAND_BUFFER_STATUS bits */ ++#define BS_UNCORRECTABLE_BIT BIT(8) ++#define BS_CORRECTABLE_ERR_MSK 0x1f ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DISABLE_STATUS_AFTER_WRITE 4 ++#define CW_PER_PAGE 6 ++#define UD_SIZE_BYTES 9 ++#define UD_SIZE_BYTES_MASK GENMASK(18, 9) ++#define ECC_PARITY_SIZE_BYTES_RS 19 ++#define SPARE_SIZE_BYTES 23 ++#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) ++#define NUM_ADDR_CYCLES 27 ++#define STATUS_BFR_READ 30 ++#define SET_RD_MODE_AFTER_STATUS 31 ++ ++/* NAND_DEVn_CFG0 bits */ ++#define DEV0_CFG1_ECC_DISABLE 0 ++#define WIDE_FLASH 1 ++#define NAND_RECOVERY_CYCLES 2 ++#define CS_ACTIVE_BSY 5 ++#define BAD_BLOCK_BYTE_NUM 6 ++#define BAD_BLOCK_IN_SPARE_AREA 16 ++#define WR_RD_BSY_GAP 17 ++#define ENABLE_BCH_ECC 27 ++ ++/* NAND_DEV0_ECC_CFG bits */ ++#define ECC_CFG_ECC_DISABLE 0 ++#define ECC_SW_RESET 1 ++#define ECC_MODE 4 ++#define ECC_PARITY_SIZE_BYTES_BCH 8 ++#define ECC_NUM_DATA_BYTES 16 ++#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) ++#define ECC_FORCE_CLK_OPEN 30 ++ ++/* NAND_DEV_CMD1 bits */ ++#define READ_ADDR 0 ++ ++/* NAND_DEV_CMD_VLD bits */ ++#define READ_START_VLD BIT(0) ++#define READ_STOP_VLD BIT(1) ++#define WRITE_START_VLD BIT(2) ++#define ERASE_START_VLD BIT(3) ++#define SEQ_READ_START_VLD BIT(4) ++ ++/* NAND_EBI2_ECC_BUF_CFG bits */ ++#define NUM_STEPS 0 ++ ++/* NAND_ERASED_CW_DETECT_CFG bits */ ++#define ERASED_CW_ECC_MASK 1 ++#define AUTO_DETECT_RES 0 ++#define MASK_ECC BIT(ERASED_CW_ECC_MASK) ++#define RESET_ERASED_DET BIT(AUTO_DETECT_RES) ++#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) ++#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) ++#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) ++ ++/* NAND_ERASED_CW_DETECT_STATUS bits */ ++#define PAGE_ALL_ERASED BIT(7) ++#define CODEWORD_ALL_ERASED BIT(6) ++#define PAGE_ERASED BIT(5) ++#define CODEWORD_ERASED BIT(4) ++#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) ++#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) ++ ++/* NAND_READ_LOCATION_n bits */ ++#define READ_LOCATION_OFFSET 0 ++#define READ_LOCATION_SIZE 16 ++#define READ_LOCATION_LAST 31 ++ ++/* Version Mask */ ++#define NAND_VERSION_MAJOR_MASK 0xf0000000 ++#define NAND_VERSION_MAJOR_SHIFT 28 ++#define NAND_VERSION_MINOR_MASK 0x0fff0000 ++#define NAND_VERSION_MINOR_SHIFT 16 ++ ++/* NAND OP_CMDs */ ++#define OP_PAGE_READ 0x2 ++#define OP_PAGE_READ_WITH_ECC 0x3 ++#define OP_PAGE_READ_WITH_ECC_SPARE 0x4 ++#define OP_PAGE_READ_ONFI_READ 0x5 ++#define OP_PROGRAM_PAGE 0x6 ++#define OP_PAGE_PROGRAM_WITH_ECC 0x7 ++#define OP_PROGRAM_PAGE_SPARE 0x9 ++#define OP_BLOCK_ERASE 0xa ++#define OP_CHECK_STATUS 0xc ++#define OP_FETCH_ID 0xb ++#define OP_RESET_DEVICE 0xd ++ ++/* Default Value for NAND_DEV_CMD_VLD */ ++#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ ++ ERASE_START_VLD | SEQ_READ_START_VLD) ++ ++/* NAND_CTRL bits */ ++#define BAM_MODE_EN BIT(0) ++ ++/* ++ * the NAND controller performs reads/writes with ECC in 516 byte chunks. ++ * the driver calls the chunks 'step' or 'codeword' interchangeably ++ */ ++#define NANDC_STEP_SIZE 512 ++ ++/* ++ * the largest page size we support is 8K, this will have 16 steps/codewords ++ * of 512 bytes each ++ */ ++#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) ++ ++/* we read at most 3 registers per codeword scan */ ++#define MAX_REG_RD (3 * MAX_NUM_STEPS) ++ ++/* ECC modes supported by the controller */ ++#define ECC_NONE BIT(0) ++#define ECC_RS_4BIT BIT(1) ++#define ECC_BCH_4BIT BIT(2) ++#define ECC_BCH_8BIT BIT(3) ++ ++/* ++ * Returns the actual register address for all NAND_DEV_ registers ++ * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) ++ */ ++#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) ++ ++/* Returns the NAND register physical address */ ++#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) ++ ++/* Returns the dma address for reg read buffer */ ++#define reg_buf_dma_addr(chip, vaddr) \ ++ ((chip)->reg_read_dma + \ ++ ((u8 *)(vaddr) - (u8 *)(chip)->reg_read_buf)) ++ ++#define QPIC_PER_CW_CMD_ELEMENTS 32 ++#define QPIC_PER_CW_CMD_SGL 32 ++#define QPIC_PER_CW_DATA_SGL 8 ++ ++#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) ++ ++/* ++ * Flags used in DMA descriptor preparation helper functions ++ * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma) ++ */ ++/* Don't set the EOT in current tx BAM sgl */ ++#define NAND_BAM_NO_EOT BIT(0) ++/* Set the NWD flag in current BAM sgl */ ++#define NAND_BAM_NWD BIT(1) ++/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ ++#define NAND_BAM_NEXT_SGL BIT(2) ++/* ++ * Erased codeword status is being used two times in single transfer so this ++ * flag will determine the current value of erased codeword status register ++ */ ++#define NAND_ERASED_CW_SET BIT(4) ++ ++#define MAX_ADDRESS_CYCLE 5 ++ ++/* ++ * This data type corresponds to the BAM transaction which will be used for all ++ * NAND transfers. ++ * @bam_ce - the array of BAM command elements ++ * @cmd_sgl - sgl for NAND BAM command pipe ++ * @data_sgl - sgl for NAND BAM consumer/producer pipe ++ * @last_data_desc - last DMA desc in data channel (tx/rx). ++ * @last_cmd_desc - last DMA desc in command channel. ++ * @txn_done - completion for NAND transfer. ++ * @bam_ce_pos - the index in bam_ce which is available for next sgl ++ * @bam_ce_start - the index in bam_ce which marks the start position ce ++ * for current sgl. It will be used for size calculation ++ * for current sgl ++ * @cmd_sgl_pos - current index in command sgl. ++ * @cmd_sgl_start - start index in command sgl. ++ * @tx_sgl_pos - current index in data sgl for tx. ++ * @tx_sgl_start - start index in data sgl for tx. ++ * @rx_sgl_pos - current index in data sgl for rx. ++ * @rx_sgl_start - start index in data sgl for rx. ++ */ ++struct bam_transaction { ++ struct bam_cmd_element *bam_ce; ++ struct scatterlist *cmd_sgl; ++ struct scatterlist *data_sgl; ++ struct dma_async_tx_descriptor *last_data_desc; ++ struct dma_async_tx_descriptor *last_cmd_desc; ++ struct completion txn_done; ++ u32 bam_ce_pos; ++ u32 bam_ce_start; ++ u32 cmd_sgl_pos; ++ u32 cmd_sgl_start; ++ u32 tx_sgl_pos; ++ u32 tx_sgl_start; ++ u32 rx_sgl_pos; ++ u32 rx_sgl_start; ++}; ++ ++/* ++ * This data type corresponds to the nand dma descriptor ++ * @dma_desc - low level DMA engine descriptor ++ * @list - list for desc_info ++ * ++ * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by ++ * ADM ++ * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM ++ * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM ++ * @dir - DMA transfer direction ++ */ ++struct desc_info { ++ struct dma_async_tx_descriptor *dma_desc; ++ struct list_head node; ++ ++ union { ++ struct scatterlist adm_sgl; ++ struct { ++ struct scatterlist *bam_sgl; ++ int sgl_cnt; ++ }; ++ }; ++ enum dma_data_direction dir; ++}; ++ ++/* ++ * holds the current register values that we want to write. acts as a contiguous ++ * chunk of memory which we use to write the controller registers through DMA. ++ */ ++struct nandc_regs { ++ __le32 cmd; ++ __le32 addr0; ++ __le32 addr1; ++ __le32 chip_sel; ++ __le32 exec; ++ ++ __le32 cfg0; ++ __le32 cfg1; ++ __le32 ecc_bch_cfg; ++ ++ __le32 clrflashstatus; ++ __le32 clrreadstatus; ++ ++ __le32 cmd1; ++ __le32 vld; ++ ++ __le32 orig_cmd1; ++ __le32 orig_vld; ++ ++ __le32 ecc_buf_cfg; ++ __le32 read_location0; ++ __le32 read_location1; ++ __le32 read_location2; ++ __le32 read_location3; ++ __le32 read_location_last0; ++ __le32 read_location_last1; ++ __le32 read_location_last2; ++ __le32 read_location_last3; ++ ++ __le32 erased_cw_detect_cfg_clr; ++ __le32 erased_cw_detect_cfg_set; ++}; ++ ++/* ++ * NAND controller data struct ++ * ++ * @dev: parent device ++ * ++ * @base: MMIO base ++ * ++ * @core_clk: controller clock ++ * @aon_clk: another controller clock ++ * ++ * @regs: a contiguous chunk of memory for DMA register ++ * writes. contains the register values to be ++ * written to controller ++ * ++ * @props: properties of current NAND controller, ++ * initialized via DT match data ++ * ++ * @controller: base controller structure ++ * @host_list: list containing all the chips attached to the ++ * controller ++ * ++ * @chan: dma channel ++ * @cmd_crci: ADM DMA CRCI for command flow control ++ * @data_crci: ADM DMA CRCI for data flow control ++ * ++ * @desc_list: DMA descriptor list (list of desc_infos) ++ * ++ * @data_buffer: our local DMA buffer for page read/writes, ++ * used when we can't use the buffer provided ++ * by upper layers directly ++ * @reg_read_buf: local buffer for reading back registers via DMA ++ * ++ * @base_phys: physical base address of controller registers ++ * @base_dma: dma base address of controller registers ++ * @reg_read_dma: contains dma address for register read buffer ++ * ++ * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf ++ * functions ++ * @max_cwperpage: maximum QPIC codewords required. calculated ++ * from all connected NAND devices pagesize ++ * ++ * @reg_read_pos: marker for data read in reg_read_buf ++ * ++ * @cmd1/vld: some fixed controller register values ++ * ++ * @exec_opwrite: flag to select correct number of code word ++ * while reading status ++ */ ++struct qcom_nand_controller { ++ struct device *dev; ++ ++ void __iomem *base; ++ ++ struct clk *core_clk; ++ struct clk *aon_clk; ++ ++ struct nandc_regs *regs; ++ struct bam_transaction *bam_txn; ++ ++ const struct qcom_nandc_props *props; ++ ++ struct nand_controller *controller; ++ struct list_head host_list; ++ ++ union { ++ /* will be used only by QPIC for BAM DMA */ ++ struct { ++ struct dma_chan *tx_chan; ++ struct dma_chan *rx_chan; ++ struct dma_chan *cmd_chan; ++ }; ++ ++ /* will be used only by EBI2 for ADM DMA */ ++ struct { ++ struct dma_chan *chan; ++ unsigned int cmd_crci; ++ unsigned int data_crci; ++ }; ++ }; ++ ++ struct list_head desc_list; ++ ++ u8 *data_buffer; ++ __le32 *reg_read_buf; ++ ++ phys_addr_t base_phys; ++ dma_addr_t base_dma; ++ dma_addr_t reg_read_dma; ++ ++ int buf_size; ++ int buf_count; ++ int buf_start; ++ unsigned int max_cwperpage; ++ ++ int reg_read_pos; ++ ++ u32 cmd1, vld; ++ bool exec_opwrite; ++}; ++ ++/* ++ * This data type corresponds to the NAND controller properties which varies ++ * among different NAND controllers. ++ * @ecc_modes - ecc mode for NAND ++ * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset ++ * @supports_bam - whether NAND controller is using BAM ++ * @nandc_part_of_qpic - whether NAND controller is part of qpic IP ++ * @qpic_version2 - flag to indicate QPIC IP version 2 ++ * @use_codeword_fixup - whether NAND has different layout for boot partitions ++ */ ++struct qcom_nandc_props { ++ u32 ecc_modes; ++ u32 dev_cmd_reg_start; ++ bool supports_bam; ++ bool nandc_part_of_qpic; ++ bool qpic_version2; ++ bool use_codeword_fixup; ++}; ++ ++void qcom_free_bam_transaction(struct qcom_nand_controller *nandc); ++struct bam_transaction *qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc); ++void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc); ++void qcom_qpic_bam_dma_done(void *data); ++void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu); ++int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc, ++ struct dma_chan *chan, unsigned long flags); ++int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, ++ int reg_off, const void *vaddr, int size, unsigned int flags); ++int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, ++ const void *vaddr, int size, unsigned int flags); ++int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, int reg_off, ++ const void *vaddr, int size, bool flow_control); ++int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first, int num_regs, ++ unsigned int flags); ++int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, int first, ++ int num_regs, unsigned int flags); ++int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off, const u8 *vaddr, ++ int size, unsigned int flags); ++int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off, const u8 *vaddr, ++ int size, unsigned int flags); ++int qcom_submit_descs(struct qcom_nand_controller *nandc); ++void qcom_clear_read_regs(struct qcom_nand_controller *nandc); ++void qcom_nandc_unalloc(struct qcom_nand_controller *nandc); ++int qcom_nandc_alloc(struct qcom_nand_controller *nandc); ++#endif ++ diff --git a/target/linux/qualcommax/patches-6.6/0405-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch b/target/linux/qualcommax/patches-6.6/0405-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch new file mode 100644 index 0000000000..5f8ff3ed56 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0405-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch @@ -0,0 +1,191 @@ +From: Md Sadre Alam +Date: Sun, 22 Sep 2024 17:03:48 +0530 +Subject: [PATCH] mtd: rawnand: qcom: use FIELD_PREP and GENMASK + +Use the bitfield macro FIELD_PREP, and GENMASK to +do the shift and mask in one go. This makes the code +more readable. + +Signed-off-by: Md Sadre Alam +--- +--- a/drivers/mtd/nand/raw/qcom_nandc.c ++++ b/drivers/mtd/nand/raw/qcom_nandc.c +@@ -281,7 +281,7 @@ static void update_rw_regs(struct qcom_n + (num_cw - 1) << CW_PER_PAGE); + + cfg1 = cpu_to_le32(host->cfg1_raw); +- ecc_bch_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE); ++ ecc_bch_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE); + } + + nandc->regs->cmd = cmd; +@@ -1494,42 +1494,41 @@ static int qcom_nand_attach_chip(struct + host->cw_size = host->cw_data + ecc->bytes; + bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; + +- host->cfg0 = (cwperpage - 1) << CW_PER_PAGE +- | host->cw_data << UD_SIZE_BYTES +- | 0 << DISABLE_STATUS_AFTER_WRITE +- | 5 << NUM_ADDR_CYCLES +- | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS +- | 0 << STATUS_BFR_READ +- | 1 << SET_RD_MODE_AFTER_STATUS +- | host->spare_bytes << SPARE_SIZE_BYTES; +- +- host->cfg1 = 7 << NAND_RECOVERY_CYCLES +- | 0 << CS_ACTIVE_BSY +- | bad_block_byte << BAD_BLOCK_BYTE_NUM +- | 0 << BAD_BLOCK_IN_SPARE_AREA +- | 2 << WR_RD_BSY_GAP +- | wide_bus << WIDE_FLASH +- | host->bch_enabled << ENABLE_BCH_ECC; +- +- host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE +- | host->cw_size << UD_SIZE_BYTES +- | 5 << NUM_ADDR_CYCLES +- | 0 << SPARE_SIZE_BYTES; +- +- host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES +- | 0 << CS_ACTIVE_BSY +- | 17 << BAD_BLOCK_BYTE_NUM +- | 1 << BAD_BLOCK_IN_SPARE_AREA +- | 2 << WR_RD_BSY_GAP +- | wide_bus << WIDE_FLASH +- | 1 << DEV0_CFG1_ECC_DISABLE; +- +- host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE +- | 0 << ECC_SW_RESET +- | host->cw_data << ECC_NUM_DATA_BYTES +- | 1 << ECC_FORCE_CLK_OPEN +- | ecc_mode << ECC_MODE +- | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH; ++ host->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, host->cw_data) | ++ FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 0) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) | ++ FIELD_PREP(ECC_PARITY_SIZE_BYTES_RS, host->ecc_bytes_hw) | ++ FIELD_PREP(STATUS_BFR_READ, 0) | ++ FIELD_PREP(SET_RD_MODE_AFTER_STATUS, 1) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, host->spare_bytes); ++ ++ host->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, bad_block_byte) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 0) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) | ++ FIELD_PREP(WIDE_FLASH, wide_bus) | ++ FIELD_PREP(ENABLE_BCH_ECC, host->bch_enabled); ++ ++ host->cfg0_raw = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, host->cw_size) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0); ++ ++ host->cfg1_raw = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) | ++ FIELD_PREP(CS_ACTIVE_BSY, 0) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) | ++ FIELD_PREP(WIDE_FLASH, wide_bus) | ++ FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1); ++ ++ host->ecc_bch_cfg = FIELD_PREP(ECC_CFG_ECC_DISABLE, !host->bch_enabled) | ++ FIELD_PREP(ECC_SW_RESET, 0) | ++ FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, host->cw_data) | ++ FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | ++ FIELD_PREP(ECC_MODE_MASK, ecc_mode) | ++ FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, host->ecc_bytes_hw); + + if (!nandc->props->qpic_version2) + host->ecc_buf_cfg = 0x203 << NUM_STEPS; +@@ -1882,21 +1881,21 @@ static int qcom_param_page_type_exec(str + nandc->regs->addr0 = 0; + nandc->regs->addr1 = 0; + +- nandc->regs->cfg0 = cpu_to_le32(0 << CW_PER_PAGE +- | 512 << UD_SIZE_BYTES +- | 5 << NUM_ADDR_CYCLES +- | 0 << SPARE_SIZE_BYTES); +- +- nandc->regs->cfg1 = cpu_to_le32(7 << NAND_RECOVERY_CYCLES +- | 0 << CS_ACTIVE_BSY +- | 17 << BAD_BLOCK_BYTE_NUM +- | 1 << BAD_BLOCK_IN_SPARE_AREA +- | 2 << WR_RD_BSY_GAP +- | 0 << WIDE_FLASH +- | 1 << DEV0_CFG1_ECC_DISABLE); ++ host->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, 0) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, 512) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0); ++ ++ host->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) | ++ FIELD_PREP(CS_ACTIVE_BSY, 0) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) | ++ FIELD_PREP(WIDE_FLASH, 0) | ++ FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1); + + if (!nandc->props->qpic_version2) +- nandc->regs->ecc_buf_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE); ++ nandc->regs->ecc_buf_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE); + + /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */ + if (!nandc->props->qpic_version2) { +--- a/include/linux/mtd/nand-qpic-common.h ++++ b/include/linux/mtd/nand-qpic-common.h +@@ -70,35 +70,42 @@ + #define BS_CORRECTABLE_ERR_MSK 0x1f + + /* NAND_DEVn_CFG0 bits */ +-#define DISABLE_STATUS_AFTER_WRITE 4 ++#define DISABLE_STATUS_AFTER_WRITE BIT(4) + #define CW_PER_PAGE 6 ++#define CW_PER_PAGE_MASK GENMASK(8, 6) + #define UD_SIZE_BYTES 9 + #define UD_SIZE_BYTES_MASK GENMASK(18, 9) +-#define ECC_PARITY_SIZE_BYTES_RS 19 ++#define ECC_PARITY_SIZE_BYTES_RS GENMASK(22, 19) + #define SPARE_SIZE_BYTES 23 + #define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) + #define NUM_ADDR_CYCLES 27 +-#define STATUS_BFR_READ 30 +-#define SET_RD_MODE_AFTER_STATUS 31 ++#define NUM_ADDR_CYCLES_MASK GENMASK(29, 27) ++#define STATUS_BFR_READ BIT(30) ++#define SET_RD_MODE_AFTER_STATUS BIT(31) + + /* NAND_DEVn_CFG0 bits */ +-#define DEV0_CFG1_ECC_DISABLE 0 +-#define WIDE_FLASH 1 ++#define DEV0_CFG1_ECC_DISABLE BIT(0) ++#define WIDE_FLASH BIT(1) + #define NAND_RECOVERY_CYCLES 2 +-#define CS_ACTIVE_BSY 5 ++#define NAND_RECOVERY_CYCLES_MASK GENMASK(4, 2) ++#define CS_ACTIVE_BSY BIT(5) + #define BAD_BLOCK_BYTE_NUM 6 +-#define BAD_BLOCK_IN_SPARE_AREA 16 ++#define BAD_BLOCK_BYTE_NUM_MASK GENMASK(15, 6) ++#define BAD_BLOCK_IN_SPARE_AREA BIT(16) + #define WR_RD_BSY_GAP 17 +-#define ENABLE_BCH_ECC 27 ++#define WR_RD_BSY_GAP_MASK GENMASK(22, 17) ++#define ENABLE_BCH_ECC BIT(27) + + /* NAND_DEV0_ECC_CFG bits */ +-#define ECC_CFG_ECC_DISABLE 0 +-#define ECC_SW_RESET 1 ++#define ECC_CFG_ECC_DISABLE BIT(0) ++#define ECC_SW_RESET BIT(1) + #define ECC_MODE 4 ++#define ECC_MODE_MASK GENMASK(5, 4) + #define ECC_PARITY_SIZE_BYTES_BCH 8 ++#define ECC_PARITY_SIZE_BYTES_BCH_MASK GENMASK(12, 8) + #define ECC_NUM_DATA_BYTES 16 + #define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) +-#define ECC_FORCE_CLK_OPEN 30 ++#define ECC_FORCE_CLK_OPEN BIT(30) + + /* NAND_DEV_CMD1 bits */ + #define READ_ADDR 0 diff --git a/target/linux/qualcommax/patches-6.6/0406-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Interface.patch b/target/linux/qualcommax/patches-6.6/0406-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Interface.patch new file mode 100644 index 0000000000..95188529c4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0406-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Interface.patch @@ -0,0 +1,1729 @@ +From: Md Sadre Alam +Date: Sun, 22 Sep 2024 17:03:49 +0530 +Subject: [PATCH] spi: spi-qpic: add driver for QCOM SPI NAND flash Interface + +This driver implements support for the SPI-NAND mode of QCOM NAND Flash +Interface as a SPI-MEM controller with pipelined ECC capability. + +Co-developed-by: Sricharan Ramabadhran +Signed-off-by: Sricharan Ramabadhran +Co-developed-by: Varadarajan Narayanan +Signed-off-by: Varadarajan Narayanan +Signed-off-by: Md Sadre Alam +--- +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -7,8 +7,11 @@ obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bm + + ifeq ($(CONFIG_MTD_NAND_QCOM),y) + obj-y += qpic_common.o ++else ++ifeq ($(CONFIG_SPI_QPIC_SNAND),y) ++obj-y += qpic_common.o ++endif + endif +- + obj-y += onenand/ + obj-y += raw/ + obj-y += spi/ +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -870,6 +870,14 @@ config SPI_QCOM_QSPI + help + QSPI(Quad SPI) driver for Qualcomm QSPI controller. + ++config SPI_QPIC_SNAND ++ bool "QPIC SNAND controller" ++ depends on ARCH_QCOM || COMPILE_TEST ++ help ++ QPIC_SNAND (QPIC SPI NAND) driver for Qualcomm QPIC controller. ++ QPIC controller supports both parallel nand and serial nand. ++ This config will enable serial nand driver for QPIC controller. ++ + config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" + depends on ARCH_QCOM || COMPILE_TEST +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -110,6 +110,7 @@ obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx- + obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o + obj-$(CONFIG_SPI_QCOM_GENI) += spi-geni-qcom.o + obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom-qspi.o ++obj-$(CONFIG_SPI_QPIC_SNAND) += spi-qpic-snand.o + obj-$(CONFIG_SPI_QUP) += spi-qup.o + obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o + obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o +--- /dev/null ++++ b/drivers/spi/spi-qpic-snand.c +@@ -0,0 +1,1634 @@ ++/* ++ * SPDX-License-Identifier: GPL-2.0 ++ * ++ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. ++ * ++ * Authors: ++ * Md Sadre Alam ++ * Sricharan R ++ * Varadarajan Narayanan ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define NAND_FLASH_SPI_CFG 0xc0 ++#define NAND_NUM_ADDR_CYCLES 0xc4 ++#define NAND_BUSY_CHECK_WAIT_CNT 0xc8 ++#define NAND_FLASH_FEATURES 0xf64 ++ ++/* QSPI NAND config reg bits */ ++#define LOAD_CLK_CNTR_INIT_EN BIT(28) ++#define CLK_CNTR_INIT_VAL_VEC 0x924 ++#define CLK_CNTR_INIT_VAL_VEC_MASK GENMASK(27, 16) ++#define FEA_STATUS_DEV_ADDR 0xc0 ++#define FEA_STATUS_DEV_ADDR_MASK GENMASK(15, 8) ++#define SPI_CFG BIT(0) ++#define SPI_NUM_ADDR 0xDA4DB ++#define SPI_WAIT_CNT 0x10 ++#define QPIC_QSPI_NUM_CS 1 ++#define SPI_TRANSFER_MODE_x1 BIT(29) ++#define SPI_TRANSFER_MODE_x4 (3 << 29) ++#define SPI_WP BIT(28) ++#define SPI_HOLD BIT(27) ++#define QPIC_SET_FEATURE BIT(31) ++ ++#define SPINAND_RESET 0xff ++#define SPINAND_READID 0x9f ++#define SPINAND_GET_FEATURE 0x0f ++#define SPINAND_SET_FEATURE 0x1f ++#define SPINAND_READ 0x13 ++#define SPINAND_ERASE 0xd8 ++#define SPINAND_WRITE_EN 0x06 ++#define SPINAND_PROGRAM_EXECUTE 0x10 ++#define SPINAND_PROGRAM_LOAD 0x84 ++ ++#define ACC_FEATURE 0xe ++#define BAD_BLOCK_MARKER_SIZE 0x2 ++#define OOB_BUF_SIZE 128 ++#define ecceng_to_qspi(eng) container_of(eng, struct qpic_spi_nand, ecc_eng) ++struct qpic_snand_op { ++ u32 cmd_reg; ++ u32 addr1_reg; ++ u32 addr2_reg; ++}; ++ ++struct snandc_read_status { ++ __le32 snandc_flash; ++ __le32 snandc_buffer; ++ __le32 snandc_erased_cw; ++}; ++ ++/* ++ * ECC state struct ++ * @corrected: ECC corrected ++ * @bitflips: Max bit flip ++ * @failed: ECC failed ++ */ ++struct qcom_ecc_stats { ++ u32 corrected; ++ u32 bitflips; ++ u32 failed; ++}; ++ ++struct qpic_ecc { ++ struct device *dev; ++ int ecc_bytes_hw; ++ int spare_bytes; ++ int bbm_size; ++ int ecc_mode; ++ int bytes; ++ int steps; ++ int step_size; ++ int strength; ++ int cw_size; ++ int cw_data; ++ u32 cfg0; ++ u32 cfg1; ++ u32 cfg0_raw; ++ u32 cfg1_raw; ++ u32 ecc_buf_cfg; ++ u32 ecc_bch_cfg; ++ u32 clrflashstatus; ++ u32 clrreadstatus; ++ bool bch_enabled; ++}; ++ ++struct qpic_spi_nand { ++ struct qcom_nand_controller *snandc; ++ struct spi_controller *ctlr; ++ struct mtd_info *mtd; ++ struct clk *iomacro_clk; ++ struct qpic_ecc *ecc; ++ struct qcom_ecc_stats ecc_stats; ++ struct nand_ecc_engine ecc_eng; ++ u8 *data_buf; ++ u8 *oob_buf; ++ u32 wlen; ++ __le32 addr1; ++ __le32 addr2; ++ __le32 cmd; ++ u32 num_cw; ++ bool oob_rw; ++ bool page_rw; ++ bool raw_rw; ++}; ++ ++static void qcom_spi_set_read_loc_first(struct qcom_nand_controller *snandc, ++ int reg, int cw_offset, int read_size, ++ int is_last_read_loc) ++{ ++ __le32 locreg_val; ++ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | ++ ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc) ++ << READ_LOCATION_LAST)); ++ ++ locreg_val = cpu_to_le32(val); ++ ++ if (reg == NAND_READ_LOCATION_0) ++ snandc->regs->read_location0 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_1) ++ snandc->regs->read_location1 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_2) ++ snandc->regs->read_location1 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_3) ++ snandc->regs->read_location3 = locreg_val; ++} ++ ++static void qcom_spi_set_read_loc_last(struct qcom_nand_controller *snandc, ++ int reg, int cw_offset, int read_size, ++ int is_last_read_loc) ++{ ++ __le32 locreg_val; ++ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | ++ ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc) ++ << READ_LOCATION_LAST)); ++ ++ locreg_val = cpu_to_le32(val); ++ ++ if (reg == NAND_READ_LOCATION_LAST_CW_0) ++ snandc->regs->read_location_last0 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_LAST_CW_1) ++ snandc->regs->read_location_last1 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_LAST_CW_2) ++ snandc->regs->read_location_last2 = locreg_val; ++ else if (reg == NAND_READ_LOCATION_LAST_CW_3) ++ snandc->regs->read_location_last3 = locreg_val; ++} ++ ++static struct qcom_nand_controller *nand_to_qcom_snand(struct nand_device *nand) ++{ ++ struct nand_ecc_engine *eng = nand->ecc.engine; ++ struct qpic_spi_nand *qspi = ecceng_to_qspi(eng); ++ ++ return qspi->snandc; ++} ++ ++static int qcom_spi_init(struct qcom_nand_controller *snandc) ++{ ++ u32 snand_cfg_val = 0x0; ++ int ret; ++ ++ snand_cfg_val = FIELD_PREP(CLK_CNTR_INIT_VAL_VEC_MASK, CLK_CNTR_INIT_VAL_VEC) | ++ FIELD_PREP(LOAD_CLK_CNTR_INIT_EN, 0) | ++ FIELD_PREP(FEA_STATUS_DEV_ADDR_MASK, FEA_STATUS_DEV_ADDR) | ++ FIELD_PREP(SPI_CFG, 0); ++ ++ snandc->regs->spi_cfg = cpu_to_le32(snand_cfg_val); ++ snandc->regs->num_addr_cycle = cpu_to_le32(SPI_NUM_ADDR); ++ snandc->regs->busy_wait_cnt = cpu_to_le32(SPI_WAIT_CNT); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->spi_cfg, NAND_FLASH_SPI_CFG, 1, 0); ++ ++ snand_cfg_val &= ~LOAD_CLK_CNTR_INIT_EN; ++ snandc->regs->spi_cfg = cpu_to_le32(snand_cfg_val); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->spi_cfg, NAND_FLASH_SPI_CFG, 1, 0); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->num_addr_cycle, NAND_NUM_ADDR_CYCLES, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->busy_wait_cnt, NAND_BUSY_CHECK_WAIT_CNT, 1, ++ NAND_BAM_NEXT_SGL); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure in submitting spi init descriptor\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int qcom_spi_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_device *nand = mtd_to_nanddev(mtd); ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct qpic_ecc *qecc = snandc->qspi->ecc; ++ ++ if (section > 1) ++ return -ERANGE; ++ ++ oobregion->length = qecc->ecc_bytes_hw + qecc->spare_bytes; ++ oobregion->offset = mtd->oobsize - oobregion->length; ++ ++ return 0; ++} ++ ++static int qcom_spi_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_device *nand = mtd_to_nanddev(mtd); ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct qpic_ecc *qecc = snandc->qspi->ecc; ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = qecc->steps * 4; ++ oobregion->offset = ((qecc->steps - 1) * qecc->bytes) + qecc->bbm_size; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops qcom_spi_ooblayout = { ++ .ecc = qcom_spi_ooblayout_ecc, ++ .free = qcom_spi_ooblayout_free, ++}; ++ ++static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) ++{ ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct nand_ecc_props *conf = &nand->ecc.ctx.conf; ++ struct mtd_info *mtd = nanddev_to_mtd(nand); ++ int cwperpage, bad_block_byte; ++ struct qpic_ecc *ecc_cfg; ++ ++ cwperpage = mtd->writesize / NANDC_STEP_SIZE; ++ snandc->qspi->num_cw = cwperpage; ++ ++ ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL); ++ if (!ecc_cfg) ++ return -ENOMEM; ++ snandc->qspi->oob_buf = kzalloc(mtd->writesize + mtd->oobsize, ++ GFP_KERNEL); ++ if (!snandc->qspi->oob_buf) ++ return -ENOMEM; ++ ++ memset(snandc->qspi->oob_buf, 0xff, mtd->writesize + mtd->oobsize); ++ ++ nand->ecc.ctx.priv = ecc_cfg; ++ snandc->qspi->mtd = mtd; ++ ++ ecc_cfg->ecc_bytes_hw = 7; ++ ecc_cfg->spare_bytes = 4; ++ ecc_cfg->bbm_size = 1; ++ ecc_cfg->bch_enabled = true; ++ ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; ++ ++ ecc_cfg->steps = 4; ++ ecc_cfg->strength = 4; ++ ecc_cfg->step_size = 512; ++ ecc_cfg->cw_data = 516; ++ ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes; ++ bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1; ++ ++ mtd_set_ooblayout(mtd, &qcom_spi_ooblayout); ++ ++ ecc_cfg->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_data) | ++ FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 1) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 3) | ++ FIELD_PREP(ECC_PARITY_SIZE_BYTES_RS, ecc_cfg->ecc_bytes_hw) | ++ FIELD_PREP(STATUS_BFR_READ, 0) | ++ FIELD_PREP(SET_RD_MODE_AFTER_STATUS, 1) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, ecc_cfg->spare_bytes); ++ ++ ecc_cfg->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 0) | ++ FIELD_PREP(CS_ACTIVE_BSY, 0) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, bad_block_byte) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 0) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 20) | ++ FIELD_PREP(WIDE_FLASH, 0) | ++ FIELD_PREP(ENABLE_BCH_ECC, ecc_cfg->bch_enabled); ++ ++ ecc_cfg->cfg0_raw = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | ++ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 3) | ++ FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_size) | ++ FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0); ++ ++ ecc_cfg->cfg1_raw = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 0) | ++ FIELD_PREP(CS_ACTIVE_BSY, 0) | ++ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) | ++ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) | ++ FIELD_PREP(WR_RD_BSY_GAP_MASK, 20) | ++ FIELD_PREP(WIDE_FLASH, 0) | ++ FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1); ++ ++ ecc_cfg->ecc_bch_cfg = FIELD_PREP(ECC_CFG_ECC_DISABLE, !ecc_cfg->bch_enabled) | ++ FIELD_PREP(ECC_SW_RESET, 0) | ++ FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | ++ FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | ++ FIELD_PREP(ECC_MODE_MASK, 0) | ++ FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); ++ ++ ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS; ++ ecc_cfg->clrflashstatus = FS_READY_BSY_N; ++ ecc_cfg->clrreadstatus = 0xc0; ++ ++ conf->step_size = ecc_cfg->step_size; ++ conf->strength = ecc_cfg->strength; ++ ++ snandc->regs->erased_cw_detect_cfg_clr = cpu_to_le32(CLR_ERASED_PAGE_DET); ++ snandc->regs->erased_cw_detect_cfg_set = cpu_to_le32(SET_ERASED_PAGE_DET); ++ ++ dev_dbg(snandc->dev, "ECC strength: %u bits per %u bytes\n", ++ ecc_cfg->strength, ecc_cfg->step_size); ++ ++ return 0; ++} ++ ++static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand) ++{ ++ struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand); ++ ++ kfree(ecc_cfg); ++} ++ ++static int qcom_spi_ecc_prepare_io_req_pipelined(struct nand_device *nand, ++ struct nand_page_io_req *req) ++{ ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand); ++ ++ snandc->qspi->ecc = ecc_cfg; ++ snandc->qspi->raw_rw = false; ++ snandc->qspi->oob_rw = false; ++ snandc->qspi->page_rw = false; ++ ++ if (req->datalen) ++ snandc->qspi->page_rw = true; ++ ++ if (req->ooblen) ++ snandc->qspi->oob_rw = true; ++ ++ if (req->mode == MTD_OPS_RAW) ++ snandc->qspi->raw_rw = true; ++ ++ return 0; ++} ++ ++static int qcom_spi_ecc_finish_io_req_pipelined(struct nand_device *nand, ++ struct nand_page_io_req *req) ++{ ++ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); ++ struct mtd_info *mtd = nanddev_to_mtd(nand); ++ ++ if (req->mode == MTD_OPS_RAW || req->type != NAND_PAGE_READ) ++ return 0; ++ ++ if (snandc->qspi->ecc_stats.failed) ++ mtd->ecc_stats.failed += snandc->qspi->ecc_stats.failed; ++ else ++ mtd->ecc_stats.corrected += snandc->qspi->ecc_stats.corrected; ++ ++ if (snandc->qspi->ecc_stats.failed) ++ return -EBADMSG; ++ else ++ return snandc->qspi->ecc_stats.bitflips; ++} ++ ++static struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = { ++ .init_ctx = qcom_spi_ecc_init_ctx_pipelined, ++ .cleanup_ctx = qcom_spi_ecc_cleanup_ctx_pipelined, ++ .prepare_io_req = qcom_spi_ecc_prepare_io_req_pipelined, ++ .finish_io_req = qcom_spi_ecc_finish_io_req_pipelined, ++}; ++ ++/* helper to configure location register values */ ++static void qcom_spi_set_read_loc(struct qcom_nand_controller *snandc, int cw, int reg, ++ int cw_offset, int read_size, int is_last_read_loc) ++{ ++ int reg_base = NAND_READ_LOCATION_0; ++ int num_cw = snandc->qspi->num_cw; ++ ++ if (cw == (num_cw - 1)) ++ reg_base = NAND_READ_LOCATION_LAST_CW_0; ++ ++ reg_base += reg * 4; ++ ++ if (cw == (num_cw - 1)) ++ return qcom_spi_set_read_loc_last(snandc, reg_base, cw_offset, ++ read_size, is_last_read_loc); ++ else ++ return qcom_spi_set_read_loc_first(snandc, reg_base, cw_offset, ++ read_size, is_last_read_loc); ++} ++ ++static void ++qcom_spi_config_cw_read(struct qcom_nand_controller *snandc, bool use_ecc, int cw) ++{ ++ __le32 *reg = &snandc->regs->read_location0; ++ int num_cw = snandc->qspi->num_cw; ++ ++ qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); ++ if (cw == (num_cw - 1)) { ++ reg = &snandc->regs->read_location_last0; ++ qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_LAST_CW_0, 4, ++ NAND_BAM_NEXT_SGL); ++ } ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 2, 0); ++ qcom_read_reg_dma(snandc, NAND_ERASED_CW_DETECT_STATUS, 1, ++ NAND_BAM_NEXT_SGL); ++} ++ ++static int qcom_spi_block_erase(struct qcom_nand_controller *snandc) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ int ret; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cfg0 = cpu_to_le32(ecc_cfg->cfg0_raw & ~(7 << CW_PER_PAGE)); ++ snandc->regs->cfg1 = cpu_to_le32(ecc_cfg->cfg1_raw); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to erase block\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void qcom_spi_config_single_cw_page_read(struct qcom_nand_controller *snandc, ++ bool use_ecc, int cw) ++{ ++ __le32 *reg = &snandc->regs->read_location0; ++ int num_cw = snandc->qspi->num_cw; ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, ++ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ ++ if (cw == (num_cw - 1)) { ++ reg = &snandc->regs->read_location_last0; ++ qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_LAST_CW_0, 4, NAND_BAM_NEXT_SGL); ++ } ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1, 0); ++} ++ ++static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ struct mtd_info *mtd = snandc->qspi->mtd; ++ int size, ret = 0; ++ int col, bbpos; ++ u32 cfg0, cfg1, ecc_bch_cfg; ++ u32 num_cw = snandc->qspi->num_cw; ++ ++ qcom_clear_bam_transaction(snandc); ++ qcom_clear_read_regs(snandc); ++ ++ size = ecc_cfg->cw_size; ++ col = ecc_cfg->cw_size * (num_cw - 1); ++ ++ memset(snandc->data_buffer, 0xff, size); ++ snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ ++ cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ 0 << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1_raw; ++ ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; ++ ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_set_read_loc(snandc, num_cw - 1, 0, 0, ecc_cfg->cw_size, 1); ++ ++ qcom_spi_config_single_cw_page_read(snandc, false, num_cw - 1); ++ ++ qcom_read_data_dma(snandc, FLASH_BUF_ACC, snandc->data_buffer, size, 0); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failed to read last cw\n"); ++ return ret; ++ } ++ ++ qcom_nandc_dev_to_mem(snandc, true); ++ u32 flash = le32_to_cpu(snandc->reg_read_buf[0]); ++ ++ if (flash & (FS_OP_ERR | FS_MPU_ERR)) ++ return -EIO; ++ ++ bbpos = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); ++ ++ if (snandc->data_buffer[bbpos] == 0xff) ++ snandc->data_buffer[bbpos + 1] = 0xff; ++ if (snandc->data_buffer[bbpos] != 0xff) ++ snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos]; ++ ++ memcpy(op->data.buf.in, snandc->data_buffer + bbpos, op->data.nbytes); ++ ++ return ret; ++} ++ ++static int qcom_spi_check_error(struct qcom_nand_controller *snandc, u8 *data_buf, u8 *oob_buf) ++{ ++ struct snandc_read_status *buf; ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ int i, num_cw = snandc->qspi->num_cw; ++ bool flash_op_err = false, erased; ++ unsigned int max_bitflips = 0; ++ unsigned int uncorrectable_cws = 0; ++ ++ snandc->qspi->ecc_stats.failed = 0; ++ snandc->qspi->ecc_stats.corrected = 0; ++ ++ qcom_nandc_dev_to_mem(snandc, true); ++ buf = (struct snandc_read_status *)snandc->reg_read_buf; ++ ++ for (i = 0; i < num_cw; i++, buf++) { ++ u32 flash, buffer, erased_cw; ++ int data_len, oob_len; ++ ++ if (i == (num_cw - 1)) { ++ data_len = NANDC_STEP_SIZE - ((num_cw - 1) << 2); ++ oob_len = num_cw << 2; ++ } else { ++ data_len = ecc_cfg->cw_data; ++ oob_len = 0; ++ } ++ ++ flash = le32_to_cpu(buf->snandc_flash); ++ buffer = le32_to_cpu(buf->snandc_buffer); ++ erased_cw = le32_to_cpu(buf->snandc_erased_cw); ++ ++ if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) { ++ if (ecc_cfg->bch_enabled) ++ erased = (erased_cw & ERASED_CW) == ERASED_CW; ++ else ++ erased = false; ++ ++ if (!erased) ++ uncorrectable_cws |= BIT(i); ++ ++ } else if (flash & (FS_OP_ERR | FS_MPU_ERR)) { ++ flash_op_err = true; ++ } else { ++ unsigned int stat; ++ ++ stat = buffer & BS_CORRECTABLE_ERR_MSK; ++ snandc->qspi->ecc_stats.corrected += stat; ++ max_bitflips = max(max_bitflips, stat); ++ } ++ ++ if (data_buf) ++ data_buf += data_len; ++ if (oob_buf) ++ oob_buf += oob_len + ecc_cfg->bytes; ++ } ++ ++ if (flash_op_err) ++ return -EIO; ++ ++ if (!uncorrectable_cws) ++ snandc->qspi->ecc_stats.bitflips = max_bitflips; ++ else ++ snandc->qspi->ecc_stats.failed++; ++ ++ return 0; ++} ++ ++static int qcom_spi_check_raw_flash_errors(struct qcom_nand_controller *snandc, int cw_cnt) ++{ ++ int i; ++ ++ qcom_nandc_dev_to_mem(snandc, true); ++ ++ for (i = 0; i < cw_cnt; i++) { ++ u32 flash = le32_to_cpu(snandc->reg_read_buf[i]); ++ ++ if (flash & (FS_OP_ERR | FS_MPU_ERR)) ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_buf, ++ u8 *oob_buf, int cw) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ struct mtd_info *mtd = snandc->qspi->mtd; ++ int data_size1, data_size2, oob_size1, oob_size2; ++ int ret, reg_off = FLASH_BUF_ACC, read_loc = 0; ++ int raw_cw = cw; ++ u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; ++ int col; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ raw_cw = num_cw - 1; ++ ++ cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ 0 << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1_raw; ++ ecc_bch_cfg = ECC_CFG_ECC_DISABLE; ++ ++ col = ecc_cfg->cw_size * cw; ++ ++ snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_set_read_loc(snandc, raw_cw, 0, 0, ecc_cfg->cw_size, 1); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, ++ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ ++ data_size1 = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); ++ oob_size1 = ecc_cfg->bbm_size; ++ ++ if (cw == (num_cw - 1)) { ++ data_size2 = NANDC_STEP_SIZE - data_size1 - ++ ((num_cw - 1) * 4); ++ oob_size2 = (num_cw * 4) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size2 = ecc_cfg->cw_data - data_size1; ++ oob_size2 = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; ++ } ++ ++ qcom_spi_set_read_loc(snandc, cw, 0, read_loc, data_size1, 0); ++ read_loc += data_size1; ++ ++ qcom_spi_set_read_loc(snandc, cw, 1, read_loc, oob_size1, 0); ++ read_loc += oob_size1; ++ ++ qcom_spi_set_read_loc(snandc, cw, 2, read_loc, data_size2, 0); ++ read_loc += data_size2; ++ ++ qcom_spi_set_read_loc(snandc, cw, 3, read_loc, oob_size2, 1); ++ ++ qcom_spi_config_cw_read(snandc, false, raw_cw); ++ ++ qcom_read_data_dma(snandc, reg_off, data_buf, data_size1, 0); ++ reg_off += data_size1; ++ ++ qcom_read_data_dma(snandc, reg_off, oob_buf, oob_size1, 0); ++ reg_off += oob_size1; ++ ++ qcom_read_data_dma(snandc, reg_off, data_buf + data_size1, data_size2, 0); ++ reg_off += data_size2; ++ ++ qcom_read_data_dma(snandc, reg_off, oob_buf + oob_size1, oob_size2, 0); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to read raw cw %d\n", cw); ++ return ret; ++ } ++ ++ return qcom_spi_check_raw_flash_errors(snandc, 1); ++} ++ ++static int qcom_spi_read_page_raw(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *data_buf = NULL, *oob_buf = NULL; ++ int ret, cw; ++ u32 num_cw = snandc->qspi->num_cw; ++ ++ if (snandc->qspi->page_rw) ++ data_buf = op->data.buf.in; ++ ++ oob_buf = snandc->qspi->oob_buf; ++ memset(oob_buf, 0xff, OOB_BUF_SIZE); ++ ++ for (cw = 0; cw < num_cw; cw++) { ++ ret = qcom_spi_read_cw_raw(snandc, data_buf, oob_buf, cw); ++ if (ret) ++ return ret; ++ ++ if (data_buf) ++ data_buf += ecc_cfg->cw_data; ++ if (oob_buf) ++ oob_buf += ecc_cfg->bytes; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start; ++ int ret, i; ++ u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; ++ ++ data_buf = op->data.buf.in; ++ data_buf_start = data_buf; ++ ++ oob_buf = snandc->qspi->oob_buf; ++ oob_buf_start = oob_buf; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ ++ cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1; ++ ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ++ ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1); ++ ++ qcom_clear_bam_transaction(snandc); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, ++ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ ++ for (i = 0; i < num_cw; i++) { ++ int data_size, oob_size; ++ ++ if (i == (num_cw - 1)) { ++ data_size = 512 - ((num_cw - 1) << 2); ++ oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size = ecc_cfg->cw_data; ++ oob_size = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; ++ } ++ ++ if (data_buf && oob_buf) { ++ qcom_spi_set_read_loc(snandc, i, 0, 0, data_size, 0); ++ qcom_spi_set_read_loc(snandc, i, 1, data_size, oob_size, 1); ++ } else if (data_buf) { ++ qcom_spi_set_read_loc(snandc, i, 0, 0, data_size, 1); ++ } else { ++ qcom_spi_set_read_loc(snandc, i, 0, data_size, oob_size, 1); ++ } ++ ++ qcom_spi_config_cw_read(snandc, true, i); ++ ++ if (data_buf) ++ qcom_read_data_dma(snandc, FLASH_BUF_ACC, data_buf, ++ data_size, 0); ++ if (oob_buf) { ++ int j; ++ ++ for (j = 0; j < ecc_cfg->bbm_size; j++) ++ *oob_buf++ = 0xff; ++ ++ qcom_read_data_dma(snandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); ++ } ++ ++ if (data_buf) ++ data_buf += data_size; ++ if (oob_buf) ++ oob_buf += oob_size; ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to read page\n"); ++ return ret; ++ } ++ ++ return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start); ++} ++ ++static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start; ++ int ret, i; ++ u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; ++ ++ oob_buf = op->data.buf.in; ++ oob_buf_start = oob_buf; ++ ++ data_buf_start = data_buf; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1; ++ ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ++ ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, ++ NAND_ERASED_CW_DETECT_CFG, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, ++ NAND_ERASED_CW_DETECT_CFG, 1, ++ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); ++ ++ for (i = 0; i < num_cw; i++) { ++ int data_size, oob_size; ++ ++ if (i == (num_cw - 1)) { ++ data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); ++ oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size = ecc_cfg->cw_data; ++ oob_size = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; ++ } ++ ++ qcom_spi_set_read_loc(snandc, i, 0, data_size, oob_size, 1); ++ ++ qcom_spi_config_cw_read(snandc, true, i); ++ ++ if (oob_buf) { ++ int j; ++ ++ for (j = 0; j < ecc_cfg->bbm_size; j++) ++ *oob_buf++ = 0xff; ++ ++ qcom_read_data_dma(snandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); ++ } ++ ++ if (oob_buf) ++ oob_buf += oob_size; ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to read oob\n"); ++ return ret; ++ } ++ ++ return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start); ++} ++ ++static int qcom_spi_read_page(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ if (snandc->qspi->page_rw && snandc->qspi->raw_rw) ++ return qcom_spi_read_page_raw(snandc, op); ++ ++ if (snandc->qspi->page_rw) ++ return qcom_spi_read_page_ecc(snandc, op); ++ ++ if (snandc->qspi->oob_rw && snandc->qspi->raw_rw) ++ return qcom_spi_read_last_cw(snandc, op); ++ ++ if (snandc->qspi->oob_rw) ++ return qcom_spi_read_page_oob(snandc, op); ++ ++ return 0; ++} ++ ++static void qcom_spi_config_page_write(struct qcom_nand_controller *snandc) ++{ ++ qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, ++ 1, NAND_BAM_NEXT_SGL); ++} ++ ++static void qcom_spi_config_cw_write(struct qcom_nand_controller *snandc) ++{ ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); ++ qcom_write_reg_dma(snandc, &snandc->regs->clrreadstatus, NAND_READ_STATUS, 1, ++ NAND_BAM_NEXT_SGL); ++} ++ ++static int qcom_spi_program_raw(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ struct mtd_info *mtd = snandc->qspi->mtd; ++ u8 *data_buf = NULL, *oob_buf = NULL; ++ int i, ret; ++ int num_cw = snandc->qspi->num_cw; ++ u32 cfg0, cfg1, ecc_bch_cfg; ++ ++ cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1_raw; ++ ecc_bch_cfg = ECC_CFG_ECC_DISABLE; ++ ++ data_buf = snandc->qspi->data_buf; ++ ++ oob_buf = snandc->qspi->oob_buf; ++ memset(oob_buf, 0xff, OOB_BUF_SIZE); ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); ++ snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_config_page_write(snandc); ++ ++ for (i = 0; i < num_cw; i++) { ++ int data_size1, data_size2, oob_size1, oob_size2; ++ int reg_off = FLASH_BUF_ACC; ++ ++ data_size1 = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); ++ oob_size1 = ecc_cfg->bbm_size; ++ ++ if ((i == (num_cw - 1))) { ++ data_size2 = NANDC_STEP_SIZE - data_size1 - ++ ((num_cw - 1) << 2); ++ oob_size2 = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size2 = ecc_cfg->cw_data - data_size1; ++ oob_size2 = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; ++ } ++ ++ qcom_write_data_dma(snandc, reg_off, data_buf, data_size1, ++ NAND_BAM_NO_EOT); ++ reg_off += data_size1; ++ data_buf += data_size1; ++ ++ qcom_write_data_dma(snandc, reg_off, oob_buf, oob_size1, ++ NAND_BAM_NO_EOT); ++ oob_buf += oob_size1; ++ reg_off += oob_size1; ++ ++ qcom_write_data_dma(snandc, reg_off, data_buf, data_size2, ++ NAND_BAM_NO_EOT); ++ reg_off += data_size2; ++ data_buf += data_size2; ++ ++ qcom_write_data_dma(snandc, reg_off, oob_buf, oob_size2, 0); ++ oob_buf += oob_size2; ++ ++ qcom_spi_config_cw_write(snandc); ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to write raw page\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_program_ecc(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *data_buf = NULL, *oob_buf = NULL; ++ int i, ret; ++ int num_cw = snandc->qspi->num_cw; ++ u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; ++ ++ cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1; ++ ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ++ ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; ++ ++ if (snandc->qspi->data_buf) ++ data_buf = snandc->qspi->data_buf; ++ ++ oob_buf = snandc->qspi->oob_buf; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ snandc->regs->addr0 = snandc->qspi->addr1; ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ qcom_spi_config_page_write(snandc); ++ ++ for (i = 0; i < num_cw; i++) { ++ int data_size, oob_size; ++ ++ if (i == (num_cw - 1)) { ++ data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); ++ oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ++ ecc_cfg->spare_bytes; ++ } else { ++ data_size = ecc_cfg->cw_data; ++ oob_size = ecc_cfg->bytes; ++ } ++ ++ if (data_buf) ++ qcom_write_data_dma(snandc, FLASH_BUF_ACC, data_buf, data_size, ++ i == (num_cw - 1) ? NAND_BAM_NO_EOT : 0); ++ ++ if (i == (num_cw - 1)) { ++ if (oob_buf) { ++ oob_buf += ecc_cfg->bbm_size; ++ qcom_write_data_dma(snandc, FLASH_BUF_ACC + data_size, ++ oob_buf, oob_size, 0); ++ } ++ } ++ ++ qcom_spi_config_cw_write(snandc); ++ ++ if (data_buf) ++ data_buf += data_size; ++ if (oob_buf) ++ oob_buf += oob_size; ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to write page\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_program_oob(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ++ u8 *oob_buf = NULL; ++ int ret, col, data_size, oob_size; ++ int num_cw = snandc->qspi->num_cw; ++ u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; ++ ++ cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | ++ (num_cw - 1) << CW_PER_PAGE; ++ cfg1 = ecc_cfg->cfg1; ++ ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ++ ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; ++ ++ col = ecc_cfg->cw_size * (num_cw - 1); ++ ++ oob_buf = snandc->qspi->data_buf; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); ++ snandc->regs->addr1 = snandc->qspi->addr2; ++ snandc->regs->cmd = snandc->qspi->cmd; ++ snandc->regs->cfg0 = cpu_to_le32(cfg0); ++ snandc->regs->cfg1 = cpu_to_le32(cfg1); ++ snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); ++ snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg); ++ snandc->regs->exec = cpu_to_le32(1); ++ ++ /* calculate the data and oob size for the last codeword/step */ ++ data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); ++ oob_size = snandc->qspi->mtd->oobavail; ++ ++ memset(snandc->data_buffer, 0xff, ecc_cfg->cw_data); ++ /* override new oob content to last codeword */ ++ mtd_ooblayout_get_databytes(snandc->qspi->mtd, snandc->data_buffer + data_size, ++ oob_buf, 0, snandc->qspi->mtd->oobavail); ++ qcom_spi_config_page_write(snandc); ++ qcom_write_data_dma(snandc, FLASH_BUF_ACC, snandc->data_buffer, data_size + oob_size, 0); ++ qcom_spi_config_cw_write(snandc); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) { ++ dev_err(snandc->dev, "failure to write oob\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qcom_spi_program_execute(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ if (snandc->qspi->page_rw && snandc->qspi->raw_rw) ++ return qcom_spi_program_raw(snandc, op); ++ ++ if (snandc->qspi->page_rw) ++ return qcom_spi_program_ecc(snandc, op); ++ ++ if (snandc->qspi->oob_rw) ++ return qcom_spi_program_oob(snandc, op); ++ ++ return 0; ++} ++ ++static u32 qcom_spi_cmd_mapping(struct qcom_nand_controller *snandc, u32 opcode) ++{ ++ u32 cmd = 0x0; ++ ++ switch (opcode) { ++ case SPINAND_RESET: ++ cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_RESET_DEVICE); ++ break; ++ case SPINAND_READID: ++ cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_FETCH_ID); ++ break; ++ case SPINAND_GET_FEATURE: ++ cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE); ++ break; ++ case SPINAND_SET_FEATURE: ++ cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE | ++ QPIC_SET_FEATURE); ++ break; ++ case SPINAND_READ: ++ if (snandc->qspi->raw_rw) { ++ cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 | ++ SPI_WP | SPI_HOLD | OP_PAGE_READ); ++ } else { ++ cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 | ++ SPI_WP | SPI_HOLD | OP_PAGE_READ_WITH_ECC); ++ } ++ ++ break; ++ case SPINAND_ERASE: ++ cmd = OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE | SPI_WP | ++ SPI_HOLD | SPI_TRANSFER_MODE_x1; ++ break; ++ case SPINAND_WRITE_EN: ++ cmd = SPINAND_WRITE_EN; ++ break; ++ case SPINAND_PROGRAM_EXECUTE: ++ cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 | ++ SPI_WP | SPI_HOLD | OP_PROGRAM_PAGE); ++ break; ++ case SPINAND_PROGRAM_LOAD: ++ cmd = SPINAND_PROGRAM_LOAD; ++ break; ++ default: ++ dev_err(snandc->dev, "Opcode not supported: %u\n", opcode); ++ return -EOPNOTSUPP; ++ } ++ ++ return cmd; ++} ++ ++static int qcom_spi_write_page(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_snand_op s_op = {}; ++ u32 cmd; ++ ++ cmd = qcom_spi_cmd_mapping(snandc, op->cmd.opcode); ++ if (cmd < 0) ++ return cmd; ++ ++ s_op.cmd_reg = cmd; ++ ++ if (op->cmd.opcode == SPINAND_PROGRAM_LOAD) ++ snandc->qspi->data_buf = (u8 *)op->data.buf.out; ++ ++ return 0; ++} ++ ++static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, ++ const struct spi_mem_op *op) ++{ ++ struct qpic_snand_op s_op = {}; ++ u32 cmd; ++ int ret, opcode; ++ ++ cmd = qcom_spi_cmd_mapping(snandc, op->cmd.opcode); ++ if (cmd < 0) ++ return cmd; ++ ++ s_op.cmd_reg = cmd; ++ s_op.addr1_reg = op->addr.val; ++ s_op.addr2_reg = 0; ++ ++ opcode = op->cmd.opcode; ++ ++ switch (opcode) { ++ case SPINAND_WRITE_EN: ++ return 0; ++ case SPINAND_PROGRAM_EXECUTE: ++ s_op.addr1_reg = op->addr.val << 16; ++ s_op.addr2_reg = op->addr.val >> 16 & 0xff; ++ snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); ++ snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); ++ snandc->qspi->cmd = cpu_to_le32(cmd); ++ return qcom_spi_program_execute(snandc, op); ++ case SPINAND_READ: ++ s_op.addr1_reg = (op->addr.val << 16); ++ s_op.addr2_reg = op->addr.val >> 16 & 0xff; ++ snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); ++ snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); ++ snandc->qspi->cmd = cpu_to_le32(cmd); ++ return 0; ++ case SPINAND_ERASE: ++ s_op.addr2_reg = (op->addr.val >> 16) & 0xffff; ++ s_op.addr1_reg = op->addr.val; ++ snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg << 16); ++ snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); ++ snandc->qspi->cmd = cpu_to_le32(cmd); ++ qcom_spi_block_erase(snandc); ++ return 0; ++ default: ++ break; ++ } ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ ++ snandc->regs->cmd = cpu_to_le32(s_op.cmd_reg); ++ snandc->regs->exec = cpu_to_le32(1); ++ snandc->regs->addr0 = cpu_to_le32(s_op.addr1_reg); ++ snandc->regs->addr1 = cpu_to_le32(s_op.addr2_reg); ++ ++ qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); ++ qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) ++ dev_err(snandc->dev, "failure in submitting cmd descriptor\n"); ++ ++ return ret; ++} ++ ++static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_mem_op *op) ++{ ++ int ret, val, opcode; ++ bool copy = false, copy_ftr = false; ++ ++ ret = qcom_spi_send_cmdaddr(snandc, op); ++ if (ret) ++ return ret; ++ ++ snandc->buf_count = 0; ++ snandc->buf_start = 0; ++ qcom_clear_read_regs(snandc); ++ qcom_clear_bam_transaction(snandc); ++ opcode = op->cmd.opcode; ++ ++ switch (opcode) { ++ case SPINAND_READID: ++ snandc->buf_count = 4; ++ qcom_read_reg_dma(snandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); ++ copy = true; ++ break; ++ case SPINAND_GET_FEATURE: ++ snandc->buf_count = 4; ++ qcom_read_reg_dma(snandc, NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); ++ copy_ftr = true; ++ break; ++ case SPINAND_SET_FEATURE: ++ snandc->regs->flash_feature = cpu_to_le32(*(u32 *)op->data.buf.out); ++ qcom_write_reg_dma(snandc, &snandc->regs->flash_feature, ++ NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); ++ break; ++ case SPINAND_PROGRAM_EXECUTE: ++ case SPINAND_WRITE_EN: ++ case SPINAND_RESET: ++ case SPINAND_ERASE: ++ case SPINAND_READ: ++ return 0; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ ret = qcom_submit_descs(snandc); ++ if (ret) ++ dev_err(snandc->dev, "failure in submitting descriptor for:%d\n", opcode); ++ ++ if (copy) { ++ qcom_nandc_dev_to_mem(snandc, true); ++ memcpy(op->data.buf.in, snandc->reg_read_buf, snandc->buf_count); ++ } ++ ++ if (copy_ftr) { ++ qcom_nandc_dev_to_mem(snandc, true); ++ val = le32_to_cpu(*(__le32 *)snandc->reg_read_buf); ++ val >>= 8; ++ memcpy(op->data.buf.in, &val, snandc->buf_count); ++ } ++ ++ return ret; ++} ++ ++static bool qcom_spi_is_page_op(const struct spi_mem_op *op) ++{ ++ if (op->addr.buswidth != 1 && op->addr.buswidth != 2 && op->addr.buswidth != 4) ++ return false; ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ if (op->addr.buswidth == 4 && op->data.buswidth == 4) ++ return true; ++ ++ if (op->addr.nbytes == 2 && op->addr.buswidth == 1) ++ return true; ++ ++ } else if (op->data.dir == SPI_MEM_DATA_OUT) { ++ if (op->data.buswidth == 4) ++ return true; ++ if (op->addr.nbytes == 2 && op->addr.buswidth == 1) ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool qcom_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) ++{ ++ if (!spi_mem_default_supports_op(mem, op)) ++ return false; ++ ++ if (op->cmd.nbytes != 1 || op->cmd.buswidth != 1) ++ return false; ++ ++ if (qcom_spi_is_page_op(op)) ++ return true; ++ ++ return ((!op->addr.nbytes || op->addr.buswidth == 1) && ++ (!op->dummy.nbytes || op->dummy.buswidth == 1) && ++ (!op->data.nbytes || op->data.buswidth == 1)); ++} ++ ++static int qcom_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) ++{ ++ struct qcom_nand_controller *snandc = spi_controller_get_devdata(mem->spi->controller); ++ ++ dev_dbg(snandc->dev, "OP %02x ADDR %08llX@%d:%u DATA %d:%u", op->cmd.opcode, ++ op->addr.val, op->addr.buswidth, op->addr.nbytes, ++ op->data.buswidth, op->data.nbytes); ++ ++ if (qcom_spi_is_page_op(op)) { ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ return qcom_spi_read_page(snandc, op); ++ if (op->data.dir == SPI_MEM_DATA_OUT) ++ return qcom_spi_write_page(snandc, op); ++ } else { ++ return qcom_spi_io_op(snandc, op); ++ } ++ ++ return 0; ++} ++ ++static const struct spi_controller_mem_ops qcom_spi_mem_ops = { ++ .supports_op = qcom_spi_supports_op, ++ .exec_op = qcom_spi_exec_op, ++}; ++ ++static const struct spi_controller_mem_caps qcom_spi_mem_caps = { ++ .ecc = true, ++}; ++ ++static int qcom_spi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct spi_controller *ctlr; ++ struct qcom_nand_controller *snandc; ++ struct qpic_spi_nand *qspi; ++ struct qpic_ecc *ecc; ++ struct resource *res; ++ const void *dev_data; ++ int ret; ++ ++ ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); ++ if (!ecc) ++ return -ENOMEM; ++ ++ qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL); ++ if (!qspi) ++ return -ENOMEM; ++ ++ ctlr = __devm_spi_alloc_controller(dev, sizeof(*snandc), false); ++ if (!ctlr) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, ctlr); ++ ++ snandc = spi_controller_get_devdata(ctlr); ++ qspi->snandc = snandc; ++ ++ snandc->dev = dev; ++ snandc->qspi = qspi; ++ snandc->qspi->ctlr = ctlr; ++ snandc->qspi->ecc = ecc; ++ ++ dev_data = of_device_get_match_data(dev); ++ if (!dev_data) { ++ dev_err(&pdev->dev, "failed to get device data\n"); ++ return -ENODEV; ++ } ++ ++ snandc->props = dev_data; ++ snandc->dev = &pdev->dev; ++ ++ snandc->core_clk = devm_clk_get(dev, "core"); ++ if (IS_ERR(snandc->core_clk)) ++ return PTR_ERR(snandc->core_clk); ++ ++ snandc->aon_clk = devm_clk_get(dev, "aon"); ++ if (IS_ERR(snandc->aon_clk)) ++ return PTR_ERR(snandc->aon_clk); ++ ++ snandc->qspi->iomacro_clk = devm_clk_get(dev, "iom"); ++ if (IS_ERR(snandc->qspi->iomacro_clk)) ++ return PTR_ERR(snandc->qspi->iomacro_clk); ++ ++ snandc->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(snandc->base)) ++ return PTR_ERR(snandc->base); ++ ++ snandc->base_phys = res->start; ++ snandc->base_dma = dma_map_resource(dev, res->start, resource_size(res), ++ DMA_BIDIRECTIONAL, 0); ++ if (dma_mapping_error(dev, snandc->base_dma)) ++ return -ENXIO; ++ ++ ret = clk_prepare_enable(snandc->core_clk); ++ if (ret) ++ goto err_dis_core_clk; ++ ++ ret = clk_prepare_enable(snandc->aon_clk); ++ if (ret) ++ goto err_dis_aon_clk; ++ ++ ret = clk_prepare_enable(snandc->qspi->iomacro_clk); ++ if (ret) ++ goto err_dis_iom_clk; ++ ++ ret = qcom_nandc_alloc(snandc); ++ if (ret) ++ goto err_snand_alloc; ++ ++ ret = qcom_spi_init(snandc); ++ if (ret) ++ goto err_spi_init; ++ ++ /* setup ECC engine */ ++ snandc->qspi->ecc_eng.dev = &pdev->dev; ++ snandc->qspi->ecc_eng.integration = NAND_ECC_ENGINE_INTEGRATION_PIPELINED; ++ snandc->qspi->ecc_eng.ops = &qcom_spi_ecc_engine_ops_pipelined; ++ snandc->qspi->ecc_eng.priv = snandc; ++ ++ ret = nand_ecc_register_on_host_hw_engine(&snandc->qspi->ecc_eng); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to register ecc engine:%d\n", ret); ++ goto err_spi_init; ++ } ++ ++ ctlr->num_chipselect = QPIC_QSPI_NUM_CS; ++ ctlr->mem_ops = &qcom_spi_mem_ops; ++ ctlr->mem_caps = &qcom_spi_mem_caps; ++ ctlr->dev.of_node = pdev->dev.of_node; ++ ctlr->mode_bits = SPI_TX_DUAL | SPI_RX_DUAL | ++ SPI_TX_QUAD | SPI_RX_QUAD; ++ ++ ret = spi_register_controller(ctlr); ++ if (ret) { ++ dev_err(&pdev->dev, "spi_register_controller failed.\n"); ++ goto err_spi_init; ++ } ++ ++ return 0; ++ ++err_spi_init: ++ qcom_nandc_unalloc(snandc); ++err_snand_alloc: ++ clk_disable_unprepare(snandc->qspi->iomacro_clk); ++err_dis_iom_clk: ++ clk_disable_unprepare(snandc->aon_clk); ++err_dis_aon_clk: ++ clk_disable_unprepare(snandc->core_clk); ++err_dis_core_clk: ++ dma_unmap_resource(dev, res->start, resource_size(res), ++ DMA_BIDIRECTIONAL, 0); ++ return ret; ++} ++ ++static void qcom_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_controller *ctlr = platform_get_drvdata(pdev); ++ struct qcom_nand_controller *snandc = spi_controller_get_devdata(ctlr); ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ spi_unregister_controller(ctlr); ++ ++ qcom_nandc_unalloc(snandc); ++ ++ clk_disable_unprepare(snandc->aon_clk); ++ clk_disable_unprepare(snandc->core_clk); ++ clk_disable_unprepare(snandc->qspi->iomacro_clk); ++ ++ dma_unmap_resource(&pdev->dev, snandc->base_dma, resource_size(res), ++ DMA_BIDIRECTIONAL, 0); ++} ++ ++static const struct qcom_nandc_props ipq9574_snandc_props = { ++ .dev_cmd_reg_start = 0x7000, ++ .supports_bam = true, ++}; ++ ++static const struct of_device_id qcom_snandc_of_match[] = { ++ { ++ .compatible = "qcom,spi-qpic-snand", ++ .data = &ipq9574_snandc_props, ++ }, ++ {} ++} ++MODULE_DEVICE_TABLE(of, qcom_snandc_of_match); ++ ++static struct platform_driver qcom_spi_driver = { ++ .driver = { ++ .name = "qcom_snand", ++ .of_match_table = qcom_snandc_of_match, ++ }, ++ .probe = qcom_spi_probe, ++ .remove = qcom_spi_remove, ++}; ++module_platform_driver(qcom_spi_driver); ++ ++MODULE_DESCRIPTION("SPI driver for QPIC QSPI cores"); ++MODULE_AUTHOR("Md Sadre Alam "); ++MODULE_LICENSE("GPL"); ++ +--- a/include/linux/mtd/nand-qpic-common.h ++++ b/include/linux/mtd/nand-qpic-common.h +@@ -322,6 +322,10 @@ struct nandc_regs { + __le32 read_location_last1; + __le32 read_location_last2; + __le32 read_location_last3; ++ __le32 spi_cfg; ++ __le32 num_addr_cycle; ++ __le32 busy_wait_cnt; ++ __le32 flash_feature; + + __le32 erased_cw_detect_cfg_clr; + __le32 erased_cw_detect_cfg_set; +@@ -336,6 +340,7 @@ struct nandc_regs { + * + * @core_clk: controller clock + * @aon_clk: another controller clock ++ * @iomacro_clk: io macro clock + * + * @regs: a contiguous chunk of memory for DMA register + * writes. contains the register values to be +@@ -345,6 +350,7 @@ struct nandc_regs { + * initialized via DT match data + * + * @controller: base controller structure ++ * @qspi: qpic spi structure + * @host_list: list containing all the chips attached to the + * controller + * +@@ -389,6 +395,7 @@ struct qcom_nand_controller { + const struct qcom_nandc_props *props; + + struct nand_controller *controller; ++ struct qpic_spi_nand *qspi; + struct list_head host_list; + + union { diff --git a/target/linux/qualcommax/patches-6.6/0408-spi-spi-qpic-fix-compilation-issues.patch b/target/linux/qualcommax/patches-6.6/0408-spi-spi-qpic-fix-compilation-issues.patch new file mode 100644 index 0000000000..03b18e0829 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0408-spi-spi-qpic-fix-compilation-issues.patch @@ -0,0 +1,45 @@ +From: George Moussalem +Subject: [PATCH] spi: spi-qpic: fix compilation issues +Date: Sun, 06 Oct 2024 16:34:11 +0400 + +The compiler will throw a warning when freeing a variable, setting values +of u32 to zero using memset, when the number of bytes is greater than the +size of the variable passed, so let's set each of the 8 variables +contiguously set in memory as part of the structure to zero. + +The output type of the remove function is void while it should return an +integer indicating success (0) or a negative number as an error. So let's +switch to use the new .remove_new function which expects nothing to be +returned + +Signed-off-by: George Moussalem +--- +--- a/drivers/mtd/nand/qpic_common.c ++++ b/drivers/mtd/nand/qpic_common.c +@@ -82,7 +82,14 @@ void qcom_clear_bam_transaction(struct q + if (!nandc->props->supports_bam) + return; + +- memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8); ++ bam_txn->bam_ce_pos = 0; ++ bam_txn->bam_ce_start = 0; ++ bam_txn->cmd_sgl_pos = 0; ++ bam_txn->cmd_sgl_start = 0; ++ bam_txn->tx_sgl_pos = 0; ++ bam_txn->tx_sgl_start = 0; ++ bam_txn->rx_sgl_pos = 0; ++ bam_txn->rx_sgl_start = 0; + bam_txn->last_data_desc = NULL; + + sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -1624,7 +1624,7 @@ static struct platform_driver qcom_spi_d + .of_match_table = qcom_snandc_of_match, + }, + .probe = qcom_spi_probe, +- .remove = qcom_spi_remove, ++ .remove_new = qcom_spi_remove, + }; + module_platform_driver(qcom_spi_driver); + diff --git a/target/linux/qualcommax/patches-6.6/0411-spi-spi-qpic-snand-support-BCH8.patch b/target/linux/qualcommax/patches-6.6/0411-spi-spi-qpic-snand-support-BCH8.patch new file mode 100644 index 0000000000..6442361b0a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0411-spi-spi-qpic-snand-support-BCH8.patch @@ -0,0 +1,53 @@ +From 396886e8644d5b601126b97e0b36c40c5fb5cecf Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:11 +0800 +Subject: [PATCH 1/2] spi: spi-qpic-snand: support BCH8 + +Add BCH8 error-correcting code support for QPIC SPI Nand controller. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + drivers/spi/spi-qpic-snand.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +--- a/drivers/spi/spi-qpic-snand.c ++++ b/drivers/spi/spi-qpic-snand.c +@@ -252,6 +252,7 @@ static int qcom_spi_ecc_init_ctx_pipelin + struct nand_ecc_props *conf = &nand->ecc.ctx.conf; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int cwperpage, bad_block_byte; ++ int ecc_mode; + struct qpic_ecc *ecc_cfg; + + cwperpage = mtd->writesize / NANDC_STEP_SIZE; +@@ -270,14 +271,17 @@ static int qcom_spi_ecc_init_ctx_pipelin + nand->ecc.ctx.priv = ecc_cfg; + snandc->qspi->mtd = mtd; + +- ecc_cfg->ecc_bytes_hw = 7; +- ecc_cfg->spare_bytes = 4; ++ /* BCH8 or BCH4 */ ++ ecc_mode = mtd->oobsize > 64 ? 1 : 0; ++ ++ ecc_cfg->ecc_bytes_hw = ecc_mode ? 13 : 7; ++ ecc_cfg->spare_bytes = ecc_mode ? 2 : 4; + ecc_cfg->bbm_size = 1; + ecc_cfg->bch_enabled = true; + ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; + + ecc_cfg->steps = 4; +- ecc_cfg->strength = 4; ++ ecc_cfg->strength = ecc_mode ? 8 : 4; + ecc_cfg->step_size = 512; + ecc_cfg->cw_data = 516; + ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes; +@@ -319,7 +323,7 @@ static int qcom_spi_ecc_init_ctx_pipelin + FIELD_PREP(ECC_SW_RESET, 0) | + FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | + FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | +- FIELD_PREP(ECC_MODE_MASK, 0) | ++ FIELD_PREP(ECC_MODE_MASK, ecc_mode) | + FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); + + ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS; diff --git a/target/linux/qualcommax/patches-6.6/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch b/target/linux/qualcommax/patches-6.6/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch new file mode 100644 index 0000000000..3f50be2574 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch @@ -0,0 +1,25 @@ +From 3d550dc3eb4eaa2fe1d0668ed67e835c91487d61 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:11 +0800 +Subject: [PATCH 2/2] mtd: spinand: qpic only support max 4 bytes ID + +The QPIC SPI NAND controller supports a max of 4 bytes of device ID. +As such, set a maximum of 4 bytes. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + drivers/mtd/nand/spi/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -1087,7 +1087,7 @@ int spinand_match_and_init(struct spinan + if (rdid_method != info->devid.method) + continue; + +- if (memcmp(id + 1, info->devid.id, info->devid.len)) ++ if (memcmp(id + 1, info->devid.id, min(3, info->devid.len))) + continue; + + nand->memorg = table[i].memorg; diff --git a/target/linux/qualcommax/patches-6.6/0421-arm64-dts-qcom-ipq5018-Add-SPI-nand-node.patch b/target/linux/qualcommax/patches-6.6/0421-arm64-dts-qcom-ipq5018-Add-SPI-nand-node.patch new file mode 100644 index 0000000000..2de155637f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0421-arm64-dts-qcom-ipq5018-Add-SPI-nand-node.patch @@ -0,0 +1,52 @@ +From c2019f64539dd24e6e0da3cea2219d6f9e6b03e4 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:11 +0800 +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add SPI nand node + +Add SPI NAND support for IPQ5018 SoC. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 40 +++++++++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -461,6 +461,36 @@ + status = "disabled"; + }; + ++ qpic_bam: dma@7984000 { ++ compatible = "qcom,bam-v1.7.0"; ++ reg = <0x07984000 0x1c000>; ++ interrupts = ; ++ clocks = <&gcc GCC_QPIC_AHB_CLK>; ++ clock-names = "bam_clk"; ++ #dma-cells = <1>; ++ qcom,ee = <0>; ++ status = "disabled"; ++ }; ++ ++ qpic_nand: qpic-nand@79b0000 { ++ compatible = "qcom,spi-qpic-snand"; ++ reg = <0x079b0000 0x10000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ clocks = <&gcc GCC_QPIC_CLK>, ++ <&gcc GCC_QPIC_AHB_CLK>, ++ <&gcc GCC_QPIC_IO_MACRO_CLK>; ++ clock-names = "core", "aon", "iom"; ++ ++ dmas = <&qpic_bam 0>, ++ <&qpic_bam 1>, ++ <&qpic_bam 2>, ++ <&qpic_bam 3>; ++ dma-names = "tx", "rx", "cmd", "status"; ++ ++ status = "disabled"; ++ }; ++ + usb: usb@8af8800 { + compatible = "qcom,ipq5018-dwc3", "qcom,dwc3"; + reg = <0x08af8800 0x400>; diff --git a/target/linux/qualcommax/patches-6.6/0431-arm64-dts-qcom-ipq5018-Add-more-nand-compatible-for-.patch b/target/linux/qualcommax/patches-6.6/0431-arm64-dts-qcom-ipq5018-Add-more-nand-compatible-for-.patch new file mode 100644 index 0000000000..fdbea3cf62 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0431-arm64-dts-qcom-ipq5018-Add-more-nand-compatible-for-.patch @@ -0,0 +1,23 @@ +From b76a7649402d3eb1245ab463832133fc7efda194 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:11 +0800 +Subject: [PATCH] arm64: dts: qcom: ipq5018: Add more nand compatible for + uboot to fix partitions + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -473,7 +473,7 @@ + }; + + qpic_nand: qpic-nand@79b0000 { +- compatible = "qcom,spi-qpic-snand"; ++ compatible = "qcom,spi-qpic-snand", "qcom,ebi2-nandc-bam-v2.1.1"; + reg = <0x079b0000 0x10000>; + #address-cells = <1>; + #size-cells = <0>; diff --git a/target/linux/qualcommax/patches-6.6/0701-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller-.patch b/target/linux/qualcommax/patches-6.6/0701-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller-.patch new file mode 100644 index 0000000000..ad7e08c895 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0701-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller-.patch @@ -0,0 +1,113 @@ +From 7b89dbf5c7dcd8a9c131721e93c1292e5993968b Mon Sep 17 00:00:00 2001 +From: Luo Jie +Date: Tue, 20 Aug 2024 22:02:42 +0800 +Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL clock controller + for IPQ SoC + +The CMN PLL controller provides clocks to networking hardware blocks +on Qualcomm IPQ9574 SoC. It receives input clock from the on-chip Wi-Fi, +and produces output clocks at fixed rates. These output rates are +predetermined, and are unrelated to the input clock rate. The output +clocks are supplied to the Ethernet hardware such as PPE (packet +process engine) and the externally connected switch or PHY device. + +Signed-off-by: Luo Jie +Reviewed-by: Krzysztof Kozlowski +--- + .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 70 +++++++++++++++++++ + include/dt-bindings/clock/qcom,ipq-cmn-pll.h | 15 ++++ + 2 files changed, 85 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml + create mode 100644 include/dt-bindings/clock/qcom,ipq-cmn-pll.h + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml +@@ -0,0 +1,70 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/clock/qcom,ipq9574-cmn-pll.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Qualcomm CMN PLL Clock Controller on IPQ SoC ++ ++maintainers: ++ - Bjorn Andersson ++ - Luo Jie ++ ++description: ++ The CMN PLL clock controller expects a reference input clock. ++ This reference clock is from the on-board Wi-Fi. The CMN PLL ++ supplies a number of fixed rate output clocks to the Ethernet ++ devices including PPE (packet process engine) and the connected ++ switch or PHY device. ++ ++properties: ++ compatible: ++ enum: ++ - qcom,ipq9574-cmn-pll ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: The reference clock. The supported clock rates include ++ 25000000, 31250000, 40000000, 48000000, 50000000 and 96000000 HZ. ++ - description: The AHB clock ++ - description: The SYS clock ++ description: ++ The reference clock is the source clock of CMN PLL, which is from the ++ Wi-Fi. The AHB and SYS clocks must be enabled to access CMN PLL ++ clock registers. ++ ++ clock-names: ++ items: ++ - const: ref ++ - const: ahb ++ - const: sys ++ ++ "#clock-cells": ++ const: 1 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ - "#clock-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ ++ clock-controller@9b000 { ++ compatible = "qcom,ipq9574-cmn-pll"; ++ reg = <0x0009b000 0x800>; ++ clocks = <&cmn_pll_ref_clk>, ++ <&gcc GCC_CMN_12GPLL_AHB_CLK>, ++ <&gcc GCC_CMN_12GPLL_SYS_CLK>; ++ clock-names = "ref", "ahb", "sys"; ++ #clock-cells = <1>; ++ }; ++... +--- /dev/null ++++ b/include/dt-bindings/clock/qcom,ipq-cmn-pll.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ ++/* ++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. ++ */ ++ ++#ifndef _DT_BINDINGS_CLK_QCOM_IPQ_CMN_PLL_H ++#define _DT_BINDINGS_CLK_QCOM_IPQ_CMN_PLL_H ++ ++/* The output clocks from CMN PLL of IPQ9574. */ ++#define PPE_353MHZ_CLK 0 ++#define ETH0_50MHZ_CLK 1 ++#define ETH1_50MHZ_CLK 2 ++#define ETH2_50MHZ_CLK 3 ++#define ETH_25MHZ_CLK 4 ++#endif diff --git a/target/linux/qualcommax/patches-6.6/0702-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ.patch b/target/linux/qualcommax/patches-6.6/0702-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ.patch new file mode 100644 index 0000000000..afccffa67e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0702-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ.patch @@ -0,0 +1,288 @@ +From a7e8397e2db6133e3435054a3f312dbd9cab05ed Mon Sep 17 00:00:00 2001 +From: Luo Jie +Date: Tue, 20 Aug 2024 22:02:43 +0800 +Subject: [PATCH] clk: qcom: Add CMN PLL clock controller driver for IPQ + SoC + +The CMN PLL clock controller supplies clocks to the hardware +blocks that together make up the Ethernet function on Qualcomm +IPQ SoCs. The driver is initially supported for IPQ9574 SoC. + +The CMN PLL clock controller expects a reference input clock +from the on-board Wi-Fi block acting as clock source. The input +reference clock needs to be configured to one of the supported +clock rates. + +The controller supplies a number of fixed-rate output clocks. +For the IPQ9574, there is one output clock of 353 MHZ to PPE +(Packet Process Engine) hardware block, three 50 MHZ output +clocks and an additional 25 MHZ output clock supplied to the +connected Ethernet devices. + +Signed-off-by: Luo Jie +--- + drivers/clk/qcom/Kconfig | 10 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-ipq-cmn-pll.c | 227 +++++++++++++++++++++++++++++ + 3 files changed, 238 insertions(+) + create mode 100644 drivers/clk/qcom/clk-ipq-cmn-pll.c + +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -139,6 +139,16 @@ config IPQ_APSS_6018 + Say Y if you want to support CPU frequency scaling on + ipq based devices. + ++config IPQ_CMN_PLL ++ tristate "IPQ CMN PLL Clock Controller" ++ depends on IPQ_GCC_9574 ++ help ++ Support for CMN PLL clock controller on IPQ platform. The ++ CMN PLL feeds the reference clocks to the Ethernet devices ++ based on IPQ SoC. ++ Say Y or M if you want to support CMN PLL clock on the IPQ ++ based devices. ++ + config IPQ_GCC_4019 + tristate "IPQ4019 Global Clock Controller" + help +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -23,6 +23,7 @@ obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8 + obj-$(CONFIG_CLK_GFM_LPASS_SM8250) += lpass-gfm-sm8250.o + obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o + obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o ++obj-$(CONFIG_IPQ_CMN_PLL) += clk-ipq-cmn-pll.o + obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o + obj-$(CONFIG_IPQ_GCC_5018) += gcc-ipq5018.o + obj-$(CONFIG_IPQ_GCC_5332) += gcc-ipq5332.o +--- /dev/null ++++ b/drivers/clk/qcom/clk-ipq-cmn-pll.c +@@ -0,0 +1,227 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. ++ */ ++ ++/* ++ * CMN PLL block expects the reference clock from on-board Wi-Fi block, and ++ * supplies fixed rate clocks as output to the Ethernet hardware blocks. ++ * The Ethernet related blocks include PPE (packet process engine) and the ++ * external connected PHY (or switch) chip receiving clocks from the CMN PLL. ++ * ++ * On the IPQ9574 SoC, There are three clocks with 50 MHZ, one clock with ++ * 25 MHZ which are output from the CMN PLL to Ethernet PHY (or switch), ++ * and one clock with 353 MHZ to PPE. ++ * ++ * +---------+ ++ * | GCC | ++ * +--+---+--+ ++ * AHB CLK| |SYS CLK ++ * V V ++ * +-------+---+------+ ++ * | +-------------> eth0-50mhz ++ * REF CLK | IPQ9574 | ++ * -------->+ +-------------> eth1-50mhz ++ * | CMN PLL block | ++ * | +-------------> eth2-50mhz ++ * | | ++ * +---------+--------+-------------> eth-25mhz ++ * | ++ * V ++ * ppe-353mhz ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CMN_PLL_REFCLK_SRC_SELECTION 0x28 ++#define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8) ++ ++#define CMN_PLL_REFCLK_CONFIG 0x784 ++#define CMN_PLL_REFCLK_EXTERNAL BIT(9) ++#define CMN_PLL_REFCLK_DIV GENMASK(8, 4) ++#define CMN_PLL_REFCLK_INDEX GENMASK(3, 0) ++ ++#define CMN_PLL_POWER_ON_AND_RESET 0x780 ++#define CMN_ANA_EN_SW_RSTN BIT(6) ++ ++/** ++ * struct cmn_pll_fixed_output_clk - CMN PLL output clocks information ++ * @id: Clock specifier to be supplied ++ * @name: Clock name to be registered ++ * @rate: Clock rate ++ */ ++struct cmn_pll_fixed_output_clk { ++ unsigned int id; ++ const char *name; ++ const unsigned long rate; ++}; ++ ++#define CLK_PLL_OUTPUT(_id, _name, _rate) { \ ++ .id = _id, \ ++ .name = _name, \ ++ .rate = _rate, \ ++} ++ ++static const struct cmn_pll_fixed_output_clk ipq9574_output_clks[] = { ++ CLK_PLL_OUTPUT(PPE_353MHZ_CLK, "ppe-353mhz", 353000000UL), ++ CLK_PLL_OUTPUT(ETH0_50MHZ_CLK, "eth0-50mhz", 50000000UL), ++ CLK_PLL_OUTPUT(ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL), ++ CLK_PLL_OUTPUT(ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL), ++ CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL), ++}; ++ ++static int ipq_cmn_pll_config(struct device *dev, unsigned long parent_rate) ++{ ++ void __iomem *base; ++ u32 val; ++ ++ base = devm_of_iomap(dev, dev->of_node, 0, NULL); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ val = readl(base + CMN_PLL_REFCLK_CONFIG); ++ val &= ~(CMN_PLL_REFCLK_EXTERNAL | CMN_PLL_REFCLK_INDEX); ++ ++ /* ++ * Configure the reference input clock selection as per the given rate. ++ * The output clock rates are always of fixed value. ++ */ ++ switch (parent_rate) { ++ case 25000000: ++ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 3); ++ break; ++ case 31250000: ++ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 4); ++ break; ++ case 40000000: ++ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 6); ++ break; ++ case 48000000: ++ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7); ++ break; ++ case 50000000: ++ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 8); ++ break; ++ case 96000000: ++ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7); ++ val &= ~CMN_PLL_REFCLK_DIV; ++ val |= FIELD_PREP(CMN_PLL_REFCLK_DIV, 2); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ writel(val, base + CMN_PLL_REFCLK_CONFIG); ++ ++ /* Update the source clock rate selection. Only 96 MHZ uses 0. */ ++ val = readl(base + CMN_PLL_REFCLK_SRC_SELECTION); ++ val &= ~CMN_PLL_REFCLK_SRC_DIV; ++ if (parent_rate != 96000000) ++ val |= FIELD_PREP(CMN_PLL_REFCLK_SRC_DIV, 1); ++ ++ writel(val, base + CMN_PLL_REFCLK_SRC_SELECTION); ++ ++ /* ++ * Reset the CMN PLL block by asserting/de-asserting for 100 ms ++ * each, to ensure the updated configurations take effect. ++ */ ++ val = readl(base + CMN_PLL_POWER_ON_AND_RESET); ++ val &= ~CMN_ANA_EN_SW_RSTN; ++ writel(val, base); ++ msleep(100); ++ ++ val |= CMN_ANA_EN_SW_RSTN; ++ writel(val, base + CMN_PLL_POWER_ON_AND_RESET); ++ msleep(100); ++ ++ return 0; ++} ++ ++static int ipq_cmn_pll_clk_register(struct device *dev, const char *parent) ++{ ++ const struct cmn_pll_fixed_output_clk *fixed_clk; ++ struct clk_hw_onecell_data *data; ++ unsigned int num_clks; ++ struct clk_hw *hw; ++ int i; ++ ++ num_clks = ARRAY_SIZE(ipq9574_output_clks); ++ fixed_clk = ipq9574_output_clks; ++ ++ data = devm_kzalloc(dev, struct_size(data, hws, num_clks), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ for (i = 0; i < num_clks; i++) { ++ hw = devm_clk_hw_register_fixed_rate(dev, fixed_clk[i].name, ++ parent, 0, ++ fixed_clk[i].rate); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ ++ data->hws[fixed_clk[i].id] = hw; ++ } ++ data->num = num_clks; ++ ++ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); ++} ++ ++static int ipq_cmn_pll_clk_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct clk *clk; ++ int ret; ++ ++ /* ++ * To access the CMN PLL registers, the GCC AHB & SYSY clocks ++ * for CMN PLL block need to be enabled. ++ */ ++ clk = devm_clk_get_enabled(dev, "ahb"); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Enable AHB clock failed\n"); ++ ++ clk = devm_clk_get_enabled(dev, "sys"); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Enable SYS clock failed\n"); ++ ++ clk = devm_clk_get(dev, "ref"); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Get reference clock failed\n"); ++ ++ /* Configure CMN PLL to apply the reference clock. */ ++ ret = ipq_cmn_pll_config(dev, clk_get_rate(clk)); ++ if (ret) ++ return dev_err_probe(dev, ret, "Configure CMN PLL failed\n"); ++ ++ return ipq_cmn_pll_clk_register(dev, __clk_get_name(clk)); ++} ++ ++static const struct of_device_id ipq_cmn_pll_clk_ids[] = { ++ { .compatible = "qcom,ipq9574-cmn-pll", }, ++ { } ++}; ++ ++static struct platform_driver ipq_cmn_pll_clk_driver = { ++ .probe = ipq_cmn_pll_clk_probe, ++ .driver = { ++ .name = "ipq_cmn_pll", ++ .of_match_table = ipq_cmn_pll_clk_ids, ++ }, ++}; ++ ++module_platform_driver(ipq_cmn_pll_clk_driver); ++ ++MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPQ CMN PLL Driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/qualcommax/patches-6.6/0703-clk-qcom-cmn-pll-add-IPQ5018-support.patch b/target/linux/qualcommax/patches-6.6/0703-clk-qcom-cmn-pll-add-IPQ5018-support.patch new file mode 100644 index 0000000000..9d0d52b8eb --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0703-clk-qcom-cmn-pll-add-IPQ5018-support.patch @@ -0,0 +1,78 @@ +From a28797563b8c97c9abced82e0cf89302fcd2bf37 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:11 +0800 +Subject: [PATCH 1/2] clk: qcom: cmn-pll: add IPQ5018 support + +Add support for IPQ5018 (and removing dependency on the IPQ9574 platform). +The common network block in IPQ5018 must be enabled first through a +specific register at a fixed offset in the TCSR area, set in the DTS. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + drivers/clk/qcom/Kconfig | 1 - + drivers/clk/qcom/clk-ipq-cmn-pll.c | 29 +++++++++++++++++++++++++++++ + 2 files changed, 29 insertions(+), 1 deletion(-) + +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -141,7 +141,6 @@ config IPQ_APSS_6018 + + config IPQ_CMN_PLL + tristate "IPQ CMN PLL Clock Controller" +- depends on IPQ_GCC_9574 + help + Support for CMN PLL clock controller on IPQ platform. The + CMN PLL feeds the reference clocks to the Ethernet devices +--- a/drivers/clk/qcom/clk-ipq-cmn-pll.c ++++ b/drivers/clk/qcom/clk-ipq-cmn-pll.c +@@ -42,6 +42,9 @@ + #include + #include + ++#define TCSR_ETH_CMN 0x0 ++#define TCSR_ETH_CMN_ENABLE BIT(0) ++ + #define CMN_PLL_REFCLK_SRC_SELECTION 0x28 + #define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8) + +@@ -79,6 +82,28 @@ static const struct cmn_pll_fixed_output + CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL), + }; + ++static int ipq_cmn_pll_tcsr_enable(struct platform_device *pdev) ++{ ++ struct resource *res; ++ void __iomem *tcsr_base; ++ u32 val; ++ ++ /* For IPQ50xx, tcsr is necessary to enable cmn block */ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcsr"); ++ if (!res) ++ return 0; ++ ++ tcsr_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR_OR_NULL(tcsr_base)) ++ return PTR_ERR(tcsr_base); ++ ++ val = readl(tcsr_base + TCSR_ETH_CMN); ++ val |= TCSR_ETH_CMN_ENABLE; ++ writel(val, (tcsr_base + TCSR_ETH_CMN)); ++ ++ return 0; ++} ++ + static int ipq_cmn_pll_config(struct device *dev, unsigned long parent_rate) + { + void __iomem *base; +@@ -181,6 +206,10 @@ static int ipq_cmn_pll_clk_probe(struct + struct clk *clk; + int ret; + ++ ret = ipq_cmn_pll_tcsr_enable(pdev); ++ if (ret) ++ return dev_err_probe(dev, ret, "Enable CMN PLL failed\n"); ++ + /* + * To access the CMN PLL registers, the GCC AHB & SYSY clocks + * for CMN PLL block need to be enabled. diff --git a/target/linux/qualcommax/patches-6.6/0704-arm64-dts-qcom-ipq5018-Add-ethernet-cmn-node.patch b/target/linux/qualcommax/patches-6.6/0704-arm64-dts-qcom-ipq5018-Add-ethernet-cmn-node.patch new file mode 100644 index 0000000000..8127656ddd --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0704-arm64-dts-qcom-ipq5018-Add-ethernet-cmn-node.patch @@ -0,0 +1,45 @@ +From 1b625a37b96b0448aac126d7720eec38de8e5956 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:11 +0800 +Subject: [PATCH 2/2] arm64: dts: qcom: ipq5018: Add ethernet cmn node + +Signed-off-by: Ziyang Huang +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -16,6 +16,12 @@ + #size-cells = <2>; + + clocks { ++ cmn_pll_ref_clk: cmn-pll-ref-clk { ++ compatible = "fixed-clock"; ++ clock-frequency = <96000000>; ++ #clock-cells = <0>; ++ }; ++ + sleep_clk: sleep-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; +@@ -186,6 +192,19 @@ + status = "disabled"; + }; + ++ cmn_pll: clock-controller@9b000 { ++ compatible = "qcom,ipq9574-cmn-pll"; ++ reg = <0x0009b000 0x800>, ++ <0x19475c4 0x4>; ++ reg-names = "cmn", ++ "tcsr"; ++ clocks = <&cmn_pll_ref_clk>, ++ <&gcc GCC_CMN_BLK_AHB_CLK>, ++ <&gcc GCC_CMN_BLK_SYS_CLK>; ++ clock-names = "ref", "ahb", "sys"; ++ #clock-cells = <1>; ++ }; ++ + qfprom: qfprom@a0000 { + compatible = "qcom,ipq5018-qfprom", "qcom,qfprom"; + reg = <0xa0000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.6/0711-net-phy-qcom-Introduce-IPQ5018-internal-PHY-driver.patch b/target/linux/qualcommax/patches-6.6/0711-net-phy-qcom-Introduce-IPQ5018-internal-PHY-driver.patch new file mode 100644 index 0000000000..7764df4219 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0711-net-phy-qcom-Introduce-IPQ5018-internal-PHY-driver.patch @@ -0,0 +1,184 @@ +From 77ad12b3a5e21cae859247c0b82cf9a5b661e531 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:11 +0800 +Subject: [PATCH 1/3] net: phy: qcom: Introduce IPQ5018 internal PHY driver + +Introduce the internal GE PHY driver, part of the Qualcomm IPQ50xx SoC. +The driver registers two clock providers needed and referenced by the GCC +using DT properties and phandles. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + drivers/net/phy/qcom/Kconfig | 6 ++ + drivers/net/phy/qcom/Makefile | 1 + + drivers/net/phy/qcom/ipq5018.c | 138 +++++++++++++++++++++++++++++++++ + 3 files changed, 145 insertions(+) + create mode 100644 drivers/net/phy/qcom/ipq5018.c + +--- a/drivers/net/phy/qcom/Kconfig ++++ b/drivers/net/phy/qcom/Kconfig +@@ -9,6 +9,12 @@ config AT803X_PHY + help + Currently supports the AR8030, AR8031, AR8033, AR8035 model + ++config IPQ5018_PHY ++ tristate "Qualcomm IPQ5018 internal PHYs" ++ select QCOM_NET_PHYLIB ++ help ++ Currently supports the Qualcomm IPQ5018 internal PHY ++ + config QCA83XX_PHY + tristate "Qualcomm Atheros QCA833x PHYs" + select QCOM_NET_PHYLIB +--- a/drivers/net/phy/qcom/Makefile ++++ b/drivers/net/phy/qcom/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o + obj-$(CONFIG_AT803X_PHY) += at803x.o ++obj-$(CONFIG_IPQ5018_PHY) += ipq5018.o + obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o + obj-$(CONFIG_QCA808X_PHY) += qca808x.o + obj-$(CONFIG_QCA807X_PHY) += qca807x.o +--- /dev/null ++++ b/drivers/net/phy/qcom/ipq5018.c +@@ -0,0 +1,138 @@ ++#include ++#include ++#include ++#include ++#include ++ ++#include "qcom.h" ++ ++#define IPQ5018_PHY_ID 0x004dd0c0 ++ ++#define TX_RX_CLK_RATE 125000000 /* 125M */ ++ ++#define IPQ5018_PHY_FIFO_CONTROL 0x19 ++#define IPQ5018_PHY_FIFO_RESET GENMASK(1, 0) ++ ++struct ipq5018_phy { ++ int num_clks; ++ struct clk_bulk_data *clks; ++ struct reset_control *rst; ++ ++ struct clk_hw *clk_rx, *clk_tx; ++ struct clk_hw_onecell_data *clk_data; ++}; ++ ++static int ipq5018_probe(struct phy_device *phydev) ++{ ++ struct ipq5018_phy *priv; ++ struct device *dev = &phydev->mdio.dev; ++ char name[64]; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return dev_err_probe(dev, -ENOMEM, ++ "failed to allocate priv\n"); ++ ++ priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks); ++ if (priv->num_clks < 0) ++ return dev_err_probe(dev, priv->num_clks, ++ "failed to acquire clocks\n"); ++ ++ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "failed to enable clocks\n"); ++ ++ priv->rst = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR_OR_NULL(priv->rst)) ++ return dev_err_probe(dev, PTR_ERR(priv->rst), ++ "failed to acquire reset\n"); ++ ++ ret = reset_control_reset(priv->rst); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "failed to reset\n"); ++ ++ snprintf(name, sizeof(name), "%s#rx", dev_name(dev)); ++ priv->clk_rx = clk_hw_register_fixed_rate(dev, name, NULL, 0, ++ TX_RX_CLK_RATE); ++ if (IS_ERR_OR_NULL(priv->clk_rx)) ++ return dev_err_probe(dev, PTR_ERR(priv->clk_rx), ++ "failed to register rx clock\n"); ++ ++ snprintf(name, sizeof(name), "%s#tx", dev_name(dev)); ++ priv->clk_tx = clk_hw_register_fixed_rate(dev, name, NULL, 0, ++ TX_RX_CLK_RATE); ++ if (IS_ERR_OR_NULL(priv->clk_tx)) ++ return dev_err_probe(dev, PTR_ERR(priv->clk_tx), ++ "failed to register tx clock\n"); ++ ++ priv->clk_data = devm_kzalloc(dev, ++ struct_size(priv->clk_data, hws, 2), ++ GFP_KERNEL); ++ if (!priv->clk_data) ++ return dev_err_probe(dev, -ENOMEM, ++ "failed to allocate clk_data\n"); ++ ++ priv->clk_data->num = 2; ++ priv->clk_data->hws[0] = priv->clk_rx; ++ priv->clk_data->hws[1] = priv->clk_tx; ++ ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, ++ priv->clk_data); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "fail to register clock provider\n"); ++ ++ return 0; ++} ++ ++static int ipq5018_soft_reset(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = phy_modify(phydev, IPQ5018_PHY_FIFO_CONTROL, ++ IPQ5018_PHY_FIFO_RESET, 0); ++ if (ret < 0) ++ return ret; ++ ++ msleep(50); ++ ++ ret = phy_modify(phydev, IPQ5018_PHY_FIFO_CONTROL, ++ IPQ5018_PHY_FIFO_RESET, IPQ5018_PHY_FIFO_RESET); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int ipq5018_cable_test_start(struct phy_device *phydev) ++{ ++ /* we do all the (time consuming) work later */ ++ return 0; ++} ++ ++static struct phy_driver ipq5018_internal_phy_driver[] = { ++ { ++ PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID), ++ .name = "Qualcomm IPQ5018 internal PHY", ++ .flags = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST, ++ .probe = ipq5018_probe, ++ .soft_reset = ipq5018_soft_reset, ++ .read_status = at803x_read_status, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .cable_test_start = ipq5018_cable_test_start, ++ .cable_test_get_status = qca808x_cable_test_get_status, ++ }, ++}; ++module_phy_driver(ipq5018_internal_phy_driver); ++ ++static struct mdio_device_id __maybe_unused ipq5018_internal_phy_ids[] = { ++ { PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, ipq5018_internal_phy_ids); ++ ++MODULE_DESCRIPTION("Qualcomm IPQ5018 internal PHY driver"); ++MODULE_AUTHOR("Ziyang Huang "); diff --git a/target/linux/qualcommax/patches-6.6/0712-arm64-dts-qcom-ipq5018-add-mdio-node.patch b/target/linux/qualcommax/patches-6.6/0712-arm64-dts-qcom-ipq5018-add-mdio-node.patch new file mode 100644 index 0000000000..ce5367a085 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0712-arm64-dts-qcom-ipq5018-add-mdio-node.patch @@ -0,0 +1,43 @@ +From d2cdc83fb2c7360856e598810b88211d815fc851 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH 2/3] arm64: dts: qcom: ipq5018: add mdio node + +The IPQ5018 SoC contains two MDIO controllers. MDIO0 is used to control +its internal GE Phy, while MDIO1 is wired to external PHYs/switch. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -192,6 +192,26 @@ + status = "disabled"; + }; + ++ mdio0: mdio@88000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "qcom,ipq5018-mdio", "qcom,qca-mdio"; ++ reg = <0x88000 0x64>; ++ clocks = <&gcc GCC_MDIO0_AHB_CLK>; ++ clock-names = "gcc_mdio_ahb_clk"; ++ status = "disabled"; ++ }; ++ ++ mdio1: mdio@90000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "qcom,ipq5018-mdio"; ++ reg = <0x90000 0x64>; ++ clocks = <&gcc GCC_MDIO1_AHB_CLK>; ++ clock-names = "gcc_mdio_ahb_clk"; ++ status = "disabled"; ++ }; ++ + cmn_pll: clock-controller@9b000 { + compatible = "qcom,ipq9574-cmn-pll"; + reg = <0x0009b000 0x800>, diff --git a/target/linux/qualcommax/patches-6.6/0713-arm64-dts-qcom-ipq5018-add-ge_phy-node.patch b/target/linux/qualcommax/patches-6.6/0713-arm64-dts-qcom-ipq5018-add-ge_phy-node.patch new file mode 100644 index 0000000000..9ac109858d --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0713-arm64-dts-qcom-ipq5018-add-ge_phy-node.patch @@ -0,0 +1,45 @@ +From 28490d95fe9e059c5ce74b2289d66e0d7ede2d50 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH 3/3] arm64: dts: qcom: ipq5018: add ge_phy node + +Add the GE PHY node and register the output clocks in the GCC node. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -200,6 +200,18 @@ + clocks = <&gcc GCC_MDIO0_AHB_CLK>; + clock-names = "gcc_mdio_ahb_clk"; + status = "disabled"; ++ ++ ge_phy: ethernet-phy@7 { ++ compatible = "ethernet-phy-id004d.d0c0"; ++ reg = <7>; ++ resets = <&gcc GCC_GEPHY_BCR>, ++ <&gcc GCC_GEPHY_MDC_SW_ARES>, ++ <&gcc GCC_GEPHY_DSP_HW_ARES>, ++ <&gcc GCC_GEPHY_RX_ARES>, ++ <&gcc GCC_GEPHY_TX_ARES>; ++ clocks = <&gcc GCC_GEPHY_RX_CLK>, ++ <&gcc GCC_GEPHY_TX_CLK>; ++ }; + }; + + mdio1: mdio@90000 { +@@ -394,8 +406,8 @@ + <&pcie0_phy>, + <&pcie1_phy>, + <0>, +- <0>, +- <0>, ++ <&ge_phy 0>, ++ <&ge_phy 1>, + <0>, + <0>; + #clock-cells = <1>; diff --git a/target/linux/qualcommax/patches-6.6/0714-net-phy-qcom-IPQ5018-enable-configuration-of-DAC-settings.patch b/target/linux/qualcommax/patches-6.6/0714-net-phy-qcom-IPQ5018-enable-configuration-of-DAC-settings.patch new file mode 100644 index 0000000000..0c773caf38 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0714-net-phy-qcom-IPQ5018-enable-configuration-of-DAC-settings.patch @@ -0,0 +1,111 @@ +From: George Moussalem +Date: Sun, 19 Jan 2025 11:25:27 +0400 +Subject: [PATCH] net: phy: qcom: ipq5018 enable configuration of DAC settings + +Allow setting amplitude and bias current as needed on the IPQ5018 Internal +GE PHY. When the "qcom,dac" property is set in the DTS, the driver expects +a pair of u32 values: + +(from QCA8337 datasheet) +11: follow DSP setting +10: bypass half amplitude and follow DSP half bias current +01: half amplitude follow DSP and bypass half bias current +00: full amplitude and full bias current + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/phy/qcom/ipq5018.c ++++ b/drivers/net/phy/qcom/ipq5018.c +@@ -13,6 +13,10 @@ + #define IPQ5018_PHY_FIFO_CONTROL 0x19 + #define IPQ5018_PHY_FIFO_RESET GENMASK(1, 0) + ++#define IPQ5018_PHY_DEBUG_EDAC 0x4380 ++#define IPQ5018_PHY_MMD1_MDAC 0x8100 ++#define IPQ5018_PHY_DAC_MASK GENMASK(15,8) ++ + struct ipq5018_phy { + int num_clks; + struct clk_bulk_data *clks; +@@ -20,20 +24,35 @@ struct ipq5018_phy { + + struct clk_hw *clk_rx, *clk_tx; + struct clk_hw_onecell_data *clk_data; ++ ++ u32 mdac; ++ u32 edac; + }; + + static int ipq5018_probe(struct phy_device *phydev) + { +- struct ipq5018_phy *priv; + struct device *dev = &phydev->mdio.dev; ++ struct ipq5018_phy *priv; ++ u32 mdac, edac = 0; + char name[64]; +- int ret; ++ int ret, cnt; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return dev_err_probe(dev, -ENOMEM, + "failed to allocate priv\n"); + ++ cnt = of_property_count_u32_elems(dev->of_node, "qcom,dac"); ++ if (cnt == 2) { ++ ret = of_property_read_u32_index(dev->of_node, "qcom,dac", 0, &mdac); ++ if (!ret) ++ priv->mdac = mdac; ++ ++ ret = of_property_read_u32_index(dev->of_node, "qcom,dac", 1, &edac); ++ if (!ret) ++ priv->edac = edac; ++ } ++ + priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks); + if (priv->num_clks < 0) + return dev_err_probe(dev, priv->num_clks, +@@ -84,6 +103,8 @@ static int ipq5018_probe(struct phy_devi + return dev_err_probe(dev, ret, + "fail to register clock provider\n"); + ++ phydev->priv = priv; ++ + return 0; + } + +@@ -112,12 +133,34 @@ static int ipq5018_cable_test_start(stru + return 0; + } + ++static int ipq5018_config_init(struct phy_device *phydev) ++{ ++ struct ipq5018_phy *priv = phydev->priv; ++ int ret; ++ ++ /* setting mdac in MMD1 */ ++ if (priv->mdac) { ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC, ++ IPQ5018_PHY_DAC_MASK, priv->mdac); ++ if (ret) ++ return ret; ++ } ++ ++ /* setting edac in debug register */ ++ if (priv->edac) ++ return at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC, ++ IPQ5018_PHY_DAC_MASK, priv->edac); ++ ++ return 0; ++} ++ + static struct phy_driver ipq5018_internal_phy_driver[] = { + { + PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID), + .name = "Qualcomm IPQ5018 internal PHY", + .flags = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST, + .probe = ipq5018_probe, ++ .config_init = ipq5018_config_init, + .soft_reset = ipq5018_soft_reset, + .read_status = at803x_read_status, + .config_intr = at803x_config_intr, diff --git a/target/linux/qualcommax/patches-6.6/0715-net-phy-qcom-add-IPQ5018-initvals-and-CDT-feature.patch b/target/linux/qualcommax/patches-6.6/0715-net-phy-qcom-add-IPQ5018-initvals-and-CDT-feature.patch new file mode 100644 index 0000000000..ae4214b0cb --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0715-net-phy-qcom-add-IPQ5018-initvals-and-CDT-feature.patch @@ -0,0 +1,108 @@ +From: George Moussalem +Date: Fri, 24 Jan 2025 17:18:12 +0400 +Subject: [PATCH] net: phy: qcom: add IPQ5018 initvals and CDT feature + +The Cable Diagnostics Test for IPQ5018 follows the same logic as qca808x. +However, the IPQ5018 GE PHY has its own threshold values. So let's set the +CDT thresholds for the IPQ5018 internal GE PHY. While add it, add and set +thesholds for MSE for signal quality measurement and 8023az for EEE. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/phy/qcom/ipq5018.c ++++ b/drivers/net/phy/qcom/ipq5018.c +@@ -17,6 +17,38 @@ + #define IPQ5018_PHY_MMD1_MDAC 0x8100 + #define IPQ5018_PHY_DAC_MASK GENMASK(15,8) + ++#define IPQ5018_PHY_MMD1_MSE_THRESH1 0x1000 ++#define IPQ5018_PHY_MMD1_MSE_THRESH2 0x1001 ++#define IPQ5018_PHY_MMD3_AZ_CTRL1 0x8008 ++#define IPQ5018_PHY_MMD3_AZ_CTRL2 0x8009 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3 0x8074 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4 0x8075 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5 0x8076 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6 0x8077 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7 0x8078 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9 0x807a ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13 0x807e ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL14 0x807f ++ ++#define IPQ5018_PHY_MMD1_MSE_THRESH1_VAL 0xf1 ++#define IPQ5018_PHY_MMD1_MSE_THRESH2_VAL 0x1f6 ++#define IPQ5018_PHY_MMD3_AZ_CTRL1_VAL 0x7880 ++#define IPQ5018_PHY_MMD3_AZ_CTRL2_VAL 0xc8 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3_VAL 0xc040 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4_VAL 0xa060 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5_VAL 0xc040 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6_VAL 0xa060 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7_VAL 0xc24c ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9_VAL 0xc060 ++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13_VAL 0xb060 ++#define IPQ5018_PHY_MMD3_NEAR_ECHO_THRESH_VAL 0x90b0 ++ ++#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE 0x1 ++#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK GENMASK(7,4) ++#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT 0x50 ++ ++#define IPQ5018_PHY_DEBUG_ANA_DAC_FILTER 0xa080 ++ + struct ipq5018_phy { + int num_clks; + struct clk_bulk_data *clks; +@@ -129,6 +161,24 @@ static int ipq5018_soft_reset(struct phy + + static int ipq5018_cable_test_start(struct phy_device *phydev) + { ++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3, ++ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3_VAL); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4, ++ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4_VAL); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5, ++ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5_VAL); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6, ++ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6_VAL); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7, ++ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7_VAL); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9, ++ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9_VAL); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, ++ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13, ++ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13_VAL); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3, ++ IPQ5018_PHY_MMD3_NEAR_ECHO_THRESH_VAL); ++ + /* we do all the (time consuming) work later */ + return 0; + } +@@ -136,8 +186,30 @@ static int ipq5018_cable_test_start(stru + static int ipq5018_config_init(struct phy_device *phydev) + { + struct ipq5018_phy *priv = phydev->priv; ++ u16 val = 0; + int ret; + ++ /* set LDO efuse: first temporarily store ANA_DAC_FILTER value from ++ debug register as it will be reset once the ANA_LDO_EFUSE register ++ is written to */ ++ val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER); ++ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE, ++ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK, ++ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT); ++ at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val); ++ ++ /* set 8023AZ CTRL values */ ++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_AZ_CTRL1, ++ IPQ5018_PHY_MMD3_AZ_CTRL1_VAL); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_AZ_CTRL2, ++ IPQ5018_PHY_MMD3_AZ_CTRL2_VAL); ++ ++ /* set MSE threshold values */ ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1, ++ IPQ5018_PHY_MMD1_MSE_THRESH1_VAL); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2, ++ IPQ5018_PHY_MMD1_MSE_THRESH2_VAL); ++ + /* setting mdac in MMD1 */ + if (priv->mdac) { + ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC, diff --git a/target/linux/qualcommax/patches-6.6/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch b/target/linux/qualcommax/patches-6.6/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch new file mode 100644 index 0000000000..0a22289d39 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch @@ -0,0 +1,35 @@ +From f71366e0530db2c5cecbbbb6edfbf7344bd6f83b Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH 1/2] clk: gcc-ipq5018: remove the unsupported clk + combination for gmac + +Comment out the unsupported clock combination in the frequency table +for GMAC1. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + drivers/clk/qcom/gcc-ipq5018.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq5018.c ++++ b/drivers/clk/qcom/gcc-ipq5018.c +@@ -677,7 +677,7 @@ static const struct freq_tbl ftbl_gmac1_ + F(2500000, P_UNIPHY_RX, 12.5, 0, 0), + F(24000000, P_XO, 1, 0, 0), + F(25000000, P_UNIPHY_RX, 2.5, 0, 0), +- F(125000000, P_UNIPHY_RX, 2.5, 0, 0), ++ /* F(125000000, P_UNIPHY_RX, 2.5, 0, 0), */ + F(125000000, P_UNIPHY_RX, 1, 0, 0), + F(312500000, P_UNIPHY_RX, 1, 0, 0), + { } +@@ -717,7 +717,7 @@ static const struct freq_tbl ftbl_gmac1_ + F(2500000, P_UNIPHY_TX, 12.5, 0, 0), + F(24000000, P_XO, 1, 0, 0), + F(25000000, P_UNIPHY_TX, 2.5, 0, 0), +- F(125000000, P_UNIPHY_TX, 2.5, 0, 0), ++ /* F(125000000, P_UNIPHY_TX, 2.5, 0, 0), */ + F(125000000, P_UNIPHY_TX, 1, 0, 0), + F(312500000, P_UNIPHY_TX, 1, 0, 0), + { } diff --git a/target/linux/qualcommax/patches-6.6/0722-clk-gcc-ipq5018-refer-to-ge-phy-rx-and-tx-clk-providers-by-name.patch b/target/linux/qualcommax/patches-6.6/0722-clk-gcc-ipq5018-refer-to-ge-phy-rx-and-tx-clk-providers-by-name.patch new file mode 100644 index 0000000000..6cd1a2a16b --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0722-clk-gcc-ipq5018-refer-to-ge-phy-rx-and-tx-clk-providers-by-name.patch @@ -0,0 +1,62 @@ +From ce9e56a436e486690097cfbdda2d0c11b60db4c2 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH] clk: gcc-ipq5018: refer to GE PHY rx and tx clk providers by name + +QCA-SSDK does not register the output clocks of the onboard GE Phy and +uniphy so the GCC and DTS can't reference them by their index. +The SSDK references them by name, so let's change the GCC driver +accordingly. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + drivers/clk/qcom/gcc-ipq5018.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq5018.c ++++ b/drivers/clk/qcom/gcc-ipq5018.c +@@ -335,8 +335,8 @@ static const struct parent_map gcc_xo_gp + + static const struct clk_parent_data gcc_xo_gephy_gcc_rx_gephy_gcc_tx_ubi32_pll_gpll0[] = { + { .index = DT_XO }, +- { .index = DT_GEPHY_RX_CLK }, +- { .index = DT_GEPHY_TX_CLK }, ++ { .name = "gephy_gcc_rx", .index = -1 }, ++ { .name = "gephy_gcc_tx", .index = -1 }, + { .hw = &ubi32_pll.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + }; +@@ -351,8 +351,8 @@ static const struct parent_map gcc_xo_ge + + static const struct clk_parent_data gcc_xo_gephy_gcc_tx_gephy_gcc_rx_ubi32_pll_gpll0[] = { + { .index = DT_XO }, +- { .index = DT_GEPHY_TX_CLK }, +- { .index = DT_GEPHY_RX_CLK }, ++ { .name = "gephy_gcc_tx", .index = -1 }, ++ { .name = "gephy_gcc_rx", .index = -1 }, + { .hw = &ubi32_pll.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + }; +@@ -367,8 +367,8 @@ static const struct parent_map gcc_xo_ge + + static const struct clk_parent_data gcc_xo_uniphy_gcc_rx_uniphy_gcc_tx_ubi32_pll_gpll0[] = { + { .index = DT_XO }, +- { .index = DT_UNIPHY_RX_CLK }, +- { .index = DT_UNIPHY_TX_CLK }, ++ { .name = "uniphy_gcc_rx", .index = -1 }, ++ { .name = "uniphy_gcc_tx", .index = -1 }, + { .hw = &ubi32_pll.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + }; +@@ -383,8 +383,8 @@ static const struct parent_map gcc_xo_un + + static const struct clk_parent_data gcc_xo_uniphy_gcc_tx_uniphy_gcc_rx_ubi32_pll_gpll0[] = { + { .index = DT_XO }, +- { .index = DT_UNIPHY_TX_CLK }, +- { .index = DT_UNIPHY_RX_CLK }, ++ { .name = "uniphy_gcc_tx", .index = -1 }, ++ { .name = "uniphy_gcc_rx", .index = -1 }, + { .hw = &ubi32_pll.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + }; diff --git a/target/linux/qualcommax/patches-6.6/0751-net-dsa-qca8k-always-enable-SGMII-auto-negotiation.patch b/target/linux/qualcommax/patches-6.6/0751-net-dsa-qca8k-always-enable-SGMII-auto-negotiation.patch new file mode 100644 index 0000000000..bccac8a4eb --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0751-net-dsa-qca8k-always-enable-SGMII-auto-negotiation.patch @@ -0,0 +1,30 @@ +From d7a41a3ab6b8e3a3158997cda13f1fe28a37268c Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH] net: dsa: qca8k: always enable SGMII auto-negotiation + +fixed-link can't work well without this + +Signed-off-by: Ziyang Huang +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1545,11 +1545,10 @@ static int qca8k_pcs_config(struct phyli + return -EINVAL; + } + +- /* Enable/disable SerDes auto-negotiation as necessary */ +- val = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ? +- 0 : QCA8K_PWS_SERDES_AEN_DIS; +- +- ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8K_PWS_SERDES_AEN_DIS, val); ++ /* Enable SerDes auto-negotiation always. ++ * So fixed-link can work. ++ */ ++ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8K_PWS_SERDES_AEN_DIS, 0); + if (ret) + return ret; + diff --git a/target/linux/qualcommax/patches-6.6/0752-net-dsa-qca8k-support-PHY-to-PHY-CPU-link.patch b/target/linux/qualcommax/patches-6.6/0752-net-dsa-qca8k-support-PHY-to-PHY-CPU-link.patch new file mode 100644 index 0000000000..e664a717a3 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0752-net-dsa-qca8k-support-PHY-to-PHY-CPU-link.patch @@ -0,0 +1,49 @@ +From 8a56ac86c2eed13024413aa23a6cda85613d60f9 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sat, 18 Jan 2025 16:18:40 +0800 +Subject: [PATCH 1/2] net: dsa: qca8k: support PHY-to-PHY CPU link + +PHY-to-PHY CPU link is a common/demo design in IPQ50xx platform, since it only has a SGMII/SGMII+ link and a MDI link. + +For DSA, CPU tag is the only requirement. Fortunately, qca8337 can enable it on any port. So it's ok to trust a PHY-to-PHY link as a CPU link. + +Signed-off-by: Ziyang Huang +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1013,7 +1013,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + return err; + } + +- if (!dsa_is_user_port(priv->ds, reg)) ++ if (reg == 0 || reg == 6) + continue; + + of_get_phy_mode(port, &mode); +@@ -1088,17 +1088,19 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri + + static int qca8k_find_cpu_port(struct dsa_switch *ds) + { +- struct qca8k_priv *priv = ds->priv; ++ int i; + +- /* Find the connected cpu port. Valid port are 0 or 6 */ + if (dsa_is_cpu_port(ds, 0)) + return 0; + +- dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6"); +- + if (dsa_is_cpu_port(ds, 6)) + return 6; + ++ /* PHY-to-PHY link */ ++ for (i = 1; i <= 5; i++) ++ if (dsa_is_cpu_port(ds, i)) ++ return i; ++ + return -EINVAL; + } + diff --git a/target/linux/qualcommax/patches-6.6/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch b/target/linux/qualcommax/patches-6.6/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch new file mode 100644 index 0000000000..81248e517a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch @@ -0,0 +1,202 @@ +From: Manikanta Mylavarapu +Date: Fri, 10 Nov 2023 14:49:29 +0530 +Subject: [PATCH] dt-bindings: remoteproc: qcom: Add support for multipd model + +Add new binding document for multipd model remoteproc. +IPQ5332, IPQ9574 follows multipd model. + +Signed-off-by: Manikanta Mylavarapu +Reviewed-by: Krzysztof Kozlowski +--- +--- /dev/null ++++ b/Documentation/devicetree/bindings/remoteproc/qcom,multipd-pil.yaml +@@ -0,0 +1,189 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/remoteproc/qcom,multipd-pil.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Qualcomm Multipd Secure Peripheral Image Loader ++ ++maintainers: ++ - Manikanta Mylavarapu ++ ++description: ++ Multipd Peripheral Image Loader loads firmware and boots Q6 protection domain, ++ WCSS protection domain remoteproc's on the Qualcomm IPQ9574, IPQ5332 SoC. ++ Protection domain is similar to process in Linux. Here QDSP6 processor runs ++ each wifi radio functionality on a separate process. One process can't access ++ other process resources, so this is termed as PD i.e protection domain. ++ ++properties: ++ compatible: ++ enum: ++ - qcom,ipq5332-q6-mpd ++ - qcom,ipq9574-q6-mpd ++ ++ reg: ++ maxItems: 1 ++ ++ firmware-name: ++ maxItems: 2 ++ ++ interrupts: ++ items: ++ - description: Watchdog interrupt ++ - description: Fatal interrupt ++ - description: Ready interrupt ++ - description: Handover interrupt ++ - description: Stop acknowledge interrupt ++ ++ interrupt-names: ++ items: ++ - const: wdog ++ - const: fatal ++ - const: ready ++ - const: handover ++ - const: stop-ack ++ ++ qcom,smem-states: ++ $ref: /schemas/types.yaml#/definitions/phandle-array ++ description: States used by the AP to signal the remote processor ++ items: ++ - description: Shutdown Q6 ++ - description: Stop Q6 ++ ++ qcom,smem-state-names: ++ description: ++ Names of the states used by the AP to signal the remote processor ++ items: ++ - const: shutdown ++ - const: stop ++ ++ memory-region: ++ items: ++ - description: Q6 reserved region ++ ++ glink-edge: ++ $ref: /schemas/remoteproc/qcom,glink-edge.yaml# ++ description: ++ Qualcomm G-Link subnode which represents communication edge, channels ++ and devices related to the Modem. ++ unevaluatedProperties: false ++ ++patternProperties: ++ "^pd-1|pd-2|pd-3": ++ type: object ++ description: ++ WCSS means 'wireless connectivity sub system', in simple words it's a ++ wifi radio block. In multipd model, Q6 root protection domain will ++ provide services to WCSS user protection domain. In other words WCSS ++ protection domains depends on Q6 protection domain i.e Q6 should be up ++ before WCSS, similarly Q6 should shut down after all WCSS domains are ++ down. It can be achieved by building parent-child topology between Q6 ++ and WCSS domain's i.e keeping wcss node as sub node of Q6 device node. ++ ++ properties: ++ firmware-name: ++ maxItems: 1 ++ ++ interrupts: ++ items: ++ - description: Fatal interrupt ++ - description: Ready interrupt ++ - description: Spawn acknowledge interrupt ++ - description: Stop acknowledge interrupt ++ ++ interrupt-names: ++ items: ++ - const: fatal ++ - const: ready ++ - const: spawn-ack ++ - const: stop-ack ++ ++ qcom,smem-states: ++ $ref: /schemas/types.yaml#/definitions/phandle-array ++ description: States used by the AP to signal the remote processor ++ items: ++ - description: Shutdown WCSS pd ++ - description: Stop WCSS pd ++ - description: Spawn WCSS pd ++ ++ qcom,smem-state-names: ++ description: ++ Names of the states used by the AP to signal the remote processor ++ items: ++ - const: shutdown ++ - const: stop ++ - const: spawn ++ ++ required: ++ - firmware-name ++ - interrupts ++ - interrupt-names ++ - qcom,smem-states ++ - qcom,smem-state-names ++ ++ additionalProperties: false ++ ++required: ++ - compatible ++ - firmware-name ++ - reg ++ - interrupts ++ - interrupt-names ++ - qcom,smem-states ++ - qcom,smem-state-names ++ - memory-region ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ q6v5_wcss: remoteproc@d100000 { ++ compatible = "qcom,ipq5332-q6-mpd"; ++ reg = <0xd100000 0x4040>; ++ firmware-name = "ath11k/IPQ5332/hw1.0/q6_fw0.mdt", ++ "ath11k/IPQ5332/hw1.0/iu_fw.mdt"; ++ interrupts-extended = <&intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING>, ++ <&wcss_smp2p_in 0 IRQ_TYPE_NONE>, ++ <&wcss_smp2p_in 1 IRQ_TYPE_NONE>, ++ <&wcss_smp2p_in 2 IRQ_TYPE_NONE>, ++ <&wcss_smp2p_in 3 IRQ_TYPE_NONE>; ++ interrupt-names = "wdog", ++ "fatal", ++ "ready", ++ "handover", ++ "stop-ack"; ++ ++ qcom,smem-states = <&wcss_smp2p_out 0>, ++ <&wcss_smp2p_out 1>; ++ qcom,smem-state-names = "shutdown", ++ "stop"; ++ ++ memory-region = <&q6_region>; ++ ++ glink-edge { ++ interrupts = ; ++ label = "rtr"; ++ qcom,remote-pid = <1>; ++ mboxes = <&apcs_glb 8>; ++ }; ++ ++ pd-1 { ++ firmware-name = "ath11k/IPQ5332/hw1.0/q6_fw1.mdt"; ++ interrupts-extended = <&wcss_smp2p_in 8 IRQ_TYPE_NONE>, ++ <&wcss_smp2p_in 9 IRQ_TYPE_NONE>, ++ <&wcss_smp2p_in 12 IRQ_TYPE_NONE>, ++ <&wcss_smp2p_in 11 IRQ_TYPE_NONE>; ++ interrupt-names = "fatal", ++ "ready", ++ "spawn-ack", ++ "stop-ack"; ++ qcom,smem-states = <&wcss_smp2p_out 8>, ++ <&wcss_smp2p_out 9>, ++ <&wcss_smp2p_out 10>; ++ qcom,smem-state-names = "shutdown", ++ "stop", ++ "spawn"; ++ }; ++ }; diff --git a/target/linux/qualcommax/patches-6.6/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch b/target/linux/qualcommax/patches-6.6/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch new file mode 100644 index 0000000000..677c61f7ac --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch @@ -0,0 +1,43 @@ +From 50799703c6c8ec0860e19b102dd7cca3d29028e1 Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +Date: Fri, 10 Nov 2023 14:49:34 +0530 +Subject: [PATCH] firmware: qcom_scm: ipq5332: add support to pass + metadata size + +IPQ5332 security software running under trustzone +requires metadata size. With V2 cmd, pass metadata +size as well. + +Signed-off-by: Manikanta Mylavarapu +--- + drivers/firmware/qcom_scm.c | 8 ++++++++ + drivers/firmware/qcom_scm.h | 1 + + 2 files changed, 9 insertions(+) + +--- a/drivers/firmware/qcom_scm.c ++++ b/drivers/firmware/qcom_scm.c +@@ -592,6 +592,14 @@ int qcom_scm_pas_mem_setup(u32 periphera + if (ret) + goto disable_clk; + ++ if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, ++ QCOM_SCM_PAS_INIT_IMAGE_V2)) { ++ desc.cmd = QCOM_SCM_PAS_INIT_IMAGE_V2; ++ desc.arginfo = ++ QCOM_SCM_ARGS(3, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL); ++ desc.args[2] = size; ++ } ++ + ret = qcom_scm_call(__scm->dev, &desc, &res); + qcom_scm_bw_disable(); + +--- a/drivers/firmware/qcom_scm.h ++++ b/drivers/firmware/qcom_scm.h +@@ -92,6 +92,7 @@ extern int scm_legacy_call(struct device + + #define QCOM_SCM_SVC_PIL 0x02 + #define QCOM_SCM_PIL_PAS_INIT_IMAGE 0x01 ++#define QCOM_SCM_PAS_INIT_IMAGE_V2 0x1a + #define QCOM_SCM_PIL_PAS_MEM_SETUP 0x02 + #define QCOM_SCM_PIL_PAS_AUTH_AND_RESET 0x05 + #define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06 diff --git a/target/linux/qualcommax/patches-6.6/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch b/target/linux/qualcommax/patches-6.6/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch new file mode 100644 index 0000000000..06c6b04756 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch @@ -0,0 +1,126 @@ +From 217fbbc122663c5a3dac752cebef44fb3e0cc179 Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +Date: Fri, 10 Nov 2023 14:49:35 +0530 +Subject: [PATCH] firmware: qcom_scm: ipq5332: add msa lock/unlock + support + +IPQ5332 user pd remoteproc firmwares need to be locked +with MSA(modem secure access) features. This patch add +support to lock/unlock MSA features. + +Signed-off-by: Manikanta Mylavarapu +--- + drivers/firmware/qcom_scm.c | 78 ++++++++++++++++++++++++++ + drivers/firmware/qcom_scm.h | 2 + + include/linux/firmware/qcom/qcom_scm.h | 2 + + 3 files changed, 82 insertions(+) + +--- a/drivers/firmware/qcom_scm.c ++++ b/drivers/firmware/qcom_scm.c +@@ -712,6 +712,84 @@ bool qcom_scm_pas_supported(u32 peripher + } + EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); + ++/** ++ * qcom_scm_msa_lock() - Lock given peripheral firmware region as MSA ++ * ++ * @peripheral: peripheral id ++ * ++ * Return 0 on success. ++ */ ++int qcom_scm_msa_lock(u32 peripheral) ++{ ++ int ret; ++ struct qcom_scm_desc desc = { ++ .svc = QCOM_SCM_SVC_PIL, ++ .cmd = QCOM_SCM_MSA_LOCK, ++ .arginfo = QCOM_SCM_ARGS(1), ++ .args[0] = peripheral, ++ .owner = ARM_SMCCC_OWNER_SIP, ++ }; ++ struct qcom_scm_res res; ++ ++ if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, ++ QCOM_SCM_MSA_LOCK)) ++ return 0; ++ ++ ret = qcom_scm_clk_enable(); ++ if (ret) ++ return ret; ++ ++ ret = qcom_scm_bw_enable(); ++ if (ret) ++ return ret; ++ ++ ret = qcom_scm_call(__scm->dev, &desc, &res); ++ qcom_scm_bw_disable(); ++ qcom_scm_clk_disable(); ++ ++ return ret ? : res.result[0]; ++} ++EXPORT_SYMBOL_GPL(qcom_scm_msa_lock); ++ ++/** ++ * qcom_scm_msa_unlock() - Unlock given peripheral MSA firmware region ++ * ++ * @peripheral: peripheral id ++ * ++ * Return 0 on success. ++ */ ++int qcom_scm_msa_unlock(u32 peripheral) ++{ ++ int ret; ++ struct qcom_scm_desc desc = { ++ .svc = QCOM_SCM_SVC_PIL, ++ .cmd = QCOM_SCM_MSA_UNLOCK, ++ .arginfo = QCOM_SCM_ARGS(1), ++ .args[0] = peripheral, ++ .owner = ARM_SMCCC_OWNER_SIP, ++ }; ++ struct qcom_scm_res res; ++ ++ if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, ++ QCOM_SCM_MSA_UNLOCK)) ++ return 0; ++ ++ ret = qcom_scm_clk_enable(); ++ if (ret) ++ return ret; ++ ++ ret = qcom_scm_bw_enable(); ++ if (ret) ++ return ret; ++ ++ ret = qcom_scm_call(__scm->dev, &desc, &res); ++ qcom_scm_bw_disable(); ++ qcom_scm_clk_disable(); ++ ++ return ret ? : res.result[0]; ++} ++EXPORT_SYMBOL_GPL(qcom_scm_msa_unlock); ++ + static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) + { + struct qcom_scm_desc desc = { +--- a/drivers/firmware/qcom_scm.h ++++ b/drivers/firmware/qcom_scm.h +@@ -98,6 +98,8 @@ extern int scm_legacy_call(struct device + #define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06 + #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 + #define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a ++#define QCOM_SCM_MSA_LOCK 0x24 ++#define QCOM_SCM_MSA_UNLOCK 0x25 + + #define QCOM_SCM_SVC_IO 0x05 + #define QCOM_SCM_IO_READ 0x01 +--- a/include/linux/firmware/qcom/qcom_scm.h ++++ b/include/linux/firmware/qcom/qcom_scm.h +@@ -81,6 +81,8 @@ extern int qcom_scm_pas_mem_setup(u32 pe + extern int qcom_scm_pas_auth_and_reset(u32 peripheral); + extern int qcom_scm_pas_shutdown(u32 peripheral); + extern bool qcom_scm_pas_supported(u32 peripheral); ++extern int qcom_scm_msa_lock(u32 peripheral); ++extern int qcom_scm_msa_unlock(u32 peripheral); + + extern int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); + extern int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); diff --git a/target/linux/qualcommax/patches-6.6/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch b/target/linux/qualcommax/patches-6.6/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch new file mode 100644 index 0000000000..4e955435b2 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch @@ -0,0 +1,148 @@ +From cae691d32306966065df869fa7424728d1b16b14 Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +Date: Fri, 10 Nov 2023 14:49:36 +0530 +Subject: [PATCH] remoteproc: qcom: q6v5: Add multipd interrupts support + +In multipd model, root & user pd remoteproc's interrupts are +different. User pd needs additional interrupts like spawn. +Instead of going with qcom_q6v5_init(), we defined a new +function to register userpd rproc interrupts in mpd driver. +Since userpd rproc uses some of common interrupts like fatal, +ready, static is removed from ISR handler and used in userpd +interrupt registration. + +Signed-off-by: Manikanta Mylavarapu +--- + drivers/remoteproc/qcom_q6v5.c | 41 +++++++++++++++++++++++++++++++--- + drivers/remoteproc/qcom_q6v5.h | 11 +++++++++ + 2 files changed, 49 insertions(+), 3 deletions(-) + +--- a/drivers/remoteproc/qcom_q6v5.c ++++ b/drivers/remoteproc/qcom_q6v5.c +@@ -112,7 +112,7 @@ static irqreturn_t q6v5_wdog_interrupt(i + return IRQ_HANDLED; + } + +-static irqreturn_t q6v5_fatal_interrupt(int irq, void *data) ++irqreturn_t q6v5_fatal_interrupt(int irq, void *data) + { + struct qcom_q6v5 *q6v5 = data; + size_t len; +@@ -132,8 +132,9 @@ static irqreturn_t q6v5_fatal_interrupt( + + return IRQ_HANDLED; + } ++EXPORT_SYMBOL_GPL(q6v5_fatal_interrupt); + +-static irqreturn_t q6v5_ready_interrupt(int irq, void *data) ++irqreturn_t q6v5_ready_interrupt(int irq, void *data) + { + struct qcom_q6v5 *q6v5 = data; + +@@ -141,6 +142,7 @@ static irqreturn_t q6v5_ready_interrupt( + + return IRQ_HANDLED; + } ++EXPORT_SYMBOL_GPL(q6v5_ready_interrupt); + + /** + * qcom_q6v5_wait_for_start() - wait for remote processor start signal +@@ -177,7 +179,17 @@ static irqreturn_t q6v5_handover_interru + return IRQ_HANDLED; + } + +-static irqreturn_t q6v5_stop_interrupt(int irq, void *data) ++irqreturn_t q6v5_spawn_interrupt(int irq, void *data) ++{ ++ struct qcom_q6v5 *q6v5 = data; ++ ++ complete(&q6v5->spawn_done); ++ ++ return IRQ_HANDLED; ++} ++EXPORT_SYMBOL_GPL(q6v5_spawn_interrupt); ++ ++irqreturn_t q6v5_stop_interrupt(int irq, void *data) + { + struct qcom_q6v5 *q6v5 = data; + +@@ -185,6 +197,7 @@ static irqreturn_t q6v5_stop_interrupt(i + + return IRQ_HANDLED; + } ++EXPORT_SYMBOL_GPL(q6v5_stop_interrupt); + + /** + * qcom_q6v5_request_stop() - request the remote processor to stop +@@ -215,6 +228,28 @@ int qcom_q6v5_request_stop(struct qcom_q + EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop); + + /** ++ * qcom_q6v5_request_spawn() - request the remote processor to spawn ++ * @q6v5: reference to qcom_q6v5 context ++ * ++ * Return: 0 on success, negative errno on failure ++ */ ++int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5) ++{ ++ int ret; ++ ++ ret = qcom_smem_state_update_bits(q6v5->spawn_state, ++ BIT(q6v5->spawn_bit), BIT(q6v5->spawn_bit)); ++ ++ ret = wait_for_completion_timeout(&q6v5->spawn_done, 5 * HZ); ++ ++ qcom_smem_state_update_bits(q6v5->spawn_state, ++ BIT(q6v5->spawn_bit), 0); ++ ++ return ret == 0 ? -ETIMEDOUT : 0; ++} ++EXPORT_SYMBOL_GPL(qcom_q6v5_request_spawn); ++ ++/** + * qcom_q6v5_panic() - panic handler to invoke a stop on the remote + * @q6v5: reference to qcom_q6v5 context + * +--- a/drivers/remoteproc/qcom_q6v5.h ++++ b/drivers/remoteproc/qcom_q6v5.h +@@ -18,21 +18,27 @@ struct qcom_q6v5 { + + struct qcom_smem_state *state; + struct qmp *qmp; ++ struct qcom_smem_state *shutdown_state; ++ struct qcom_smem_state *spawn_state; + + struct icc_path *path; + + unsigned stop_bit; ++ unsigned shutdown_bit; ++ unsigned spawn_bit; + + int wdog_irq; + int fatal_irq; + int ready_irq; + int handover_irq; + int stop_irq; ++ int spawn_irq; + + bool handover_issued; + + struct completion start_done; + struct completion stop_done; ++ struct completion spawn_done; + + int crash_reason; + +@@ -50,7 +56,12 @@ void qcom_q6v5_deinit(struct qcom_q6v5 * + int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5); + int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5); + int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon); ++int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5); + int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout); + unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5); ++irqreturn_t q6v5_fatal_interrupt(int irq, void *data); ++irqreturn_t q6v5_ready_interrupt(int irq, void *data); ++irqreturn_t q6v5_spawn_interrupt(int irq, void *data); ++irqreturn_t q6v5_stop_interrupt(int irq, void *data); + + #endif diff --git a/target/linux/qualcommax/patches-6.6/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch b/target/linux/qualcommax/patches-6.6/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch new file mode 100644 index 0000000000..14371cc0fd --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch @@ -0,0 +1,901 @@ +From 3f61ff1fb5c90f8b6c28a3a2b4a29203000ee585 Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +Date: Fri, 10 Nov 2023 14:49:37 +0530 +Subject: [PATCH] remoteproc: qcom: Add Hexagon based multipd rproc + driver + +It adds support to bring up remoteproc's on multipd model. +Pd means protection domain. It's similar to process in Linux. +Here QDSP6 processor runs each wifi radio functionality on a +separate process. One process can't access other process +resources, so this is termed as PD i.e protection domain. + +Here we have two pd's called root and user pd. We can correlate +Root pd as root and user pd as user in linux. Root pd has more +privileges than user pd. Root will provide services to user pd. + +>From remoteproc driver perspective, root pd corresponds to QDSP6 +processor bring up and user pd corresponds to Wifi radio (WCSS) +bring up. + +Here WCSS(user) PD is dependent on Q6(root) PD, so first +q6 pd should be up before wcss pd. After wcss pd goes down, +q6 pd should be turned off. + + APPS QDSP6 +------- ------------- +| | Crash notification | | ------ +| |<---------------------|----------|-------|User| +| | | | |->|PD1 | +| | | ------- | | ------ +| | | | | | | +|Root | Start/stop Q6 | | R | | | +|PD |<---------------------|->| | | | +|rproc| Crash notification | | O | | | +| | | | | | | +|User |Start/stop UserPD1 | | O | | | +|PD1 |----------------------|->| |-|----| +|rproc| | | T | | | +| | | | | | | +|User |Start/stop UserPD2 | | P | | | +|PD2 |----------------------|->| |-|----| +|rproc| | | D | | | +| | | ------- | | ------ +| | Crash notification | | |->|User| +| |<---------------------|----------|-------|PD2 | +------- | | ------ + ------------ + +IPQ5332, IPQ9574 supports multipd remoteproc driver. + +Signed-off-by: Manikanta Mylavarapu +--- + drivers/remoteproc/Kconfig | 19 + + drivers/remoteproc/Makefile | 1 + + drivers/remoteproc/qcom_q6v5_mpd.c | 802 +++++++++++++++++++++++++++++ + 3 files changed, 822 insertions(+) + create mode 100644 drivers/remoteproc/qcom_q6v5_mpd.c + +--- a/drivers/remoteproc/Kconfig ++++ b/drivers/remoteproc/Kconfig +@@ -234,6 +234,25 @@ config QCOM_Q6V5_PAS + CDSP (Compute DSP), MPSS (Modem Peripheral SubSystem), and + SLPI (Sensor Low Power Island). + ++config QCOM_Q6V5_MPD ++ tristate "Qualcomm Hexagon based MPD model Peripheral Image Loader" ++ depends on OF && ARCH_QCOM ++ depends on QCOM_SMEM ++ depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n ++ depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n ++ depends on QCOM_SYSMON || QCOM_SYSMON=n ++ depends on RPMSG_QCOM_GLINK || RPMSG_QCOM_GLINK=n ++ depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n ++ select QCOM_MDT_LOADER ++ select QCOM_PIL_INFO ++ select QCOM_Q6V5_COMMON ++ select QCOM_RPROC_COMMON ++ select QCOM_SCM ++ help ++ Say y here to support the Qualcomm Secure Peripheral Image Loader ++ for the Hexagon based MultiPD model remote processors on e.g. IPQ5018. ++ This is trustZone wireless subsystem. ++ + config QCOM_Q6V5_WCSS + tristate "Qualcomm Hexagon based WCSS Peripheral Image Loader" + depends on OF && ARCH_QCOM +--- a/drivers/remoteproc/Makefile ++++ b/drivers/remoteproc/Makefile +@@ -25,6 +25,7 @@ obj-$(CONFIG_QCOM_PIL_INFO) += qcom_pil + obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o + obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o + obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6v5_adsp.o ++obj-$(CONFIG_QCOM_Q6V5_MPD) += qcom_q6v5_mpd.o + obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o + obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o + obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o +--- /dev/null ++++ b/drivers/remoteproc/qcom_q6v5_mpd.c +@@ -0,0 +1,802 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2016-2018 Linaro Ltd. ++ * Copyright (C) 2014 Sony Mobile Communications AB ++ * Copyright (c) 2012-2018, 2021 The Linux Foundation. All rights reserved. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "qcom_common.h" ++#include "qcom_q6v5.h" ++ ++#include "remoteproc_internal.h" ++ ++#define WCSS_CRASH_REASON 421 ++#define WCSS_SMEM_HOST 1 ++ ++#define WCNSS_PAS_ID 6 ++#define MPD_WCNSS_PAS_ID 0xD ++ ++#define BUF_SIZE 35 ++ ++#define MAX_FIRMWARE 3 ++ ++#define RPD_SWID MPD_WCNSS_PAS_ID ++#define UPD_SWID 0x12 ++#define REMOTE_PID 1 ++#define UPD_BOOT_INFO_SMEM_SIZE 4096 ++#define UPD_BOOT_INFO_HEADER_TYPE 0x2 ++#define UPD_BOOT_INFO_SMEM_ID 507 ++#define VERSION2 2 ++ ++static LIST_HEAD(upd_rproc_list); ++enum { ++ Q6_IPQ, ++ WCSS_IPQ, ++}; ++ ++/** ++ * struct userpd_boot_info_header - header of user pd bootinfo ++ * @type: type of bootinfo passing over smem ++ * @length: length of header in bytes ++ */ ++struct userpd_boot_info_header { ++ u8 type; ++ u8 length; ++}; ++ ++/** ++ * struct userpd_boot_info - holds info required to boot user pd ++ * @header: pointer to header ++ * @pid: unique id represents each user pd process ++ * @bootaddr: load address of user pd firmware ++ * @data_size: user pd firmware memory size ++ */ ++struct userpd_boot_info { ++ struct userpd_boot_info_header header; ++ u8 pid; ++ u32 bootaddr; ++ u32 data_size; ++} __packed; ++ ++struct q6_wcss { ++ struct device *dev; ++ struct qcom_rproc_glink glink_subdev; ++ struct qcom_rproc_ssr ssr_subdev; ++ struct qcom_q6v5 q6; ++ phys_addr_t mem_phys; ++ phys_addr_t mem_reloc; ++ void *mem_region; ++ size_t mem_size; ++ u8 pd_asid; ++ const struct wcss_data *desc; ++ const char **firmware; ++ u32 version; ++}; ++ ++struct wcss_data { ++ u32 pasid; ++ bool share_upd_info_to_q6; ++}; ++ ++/** ++ * qcom_get_pd_asid() - get the pd asid number from PD spawn bit ++ * @rproc: rproc handle ++ * ++ * Returns asid on success ++ */ ++static u8 qcom_get_pd_asid(struct rproc *rproc) ++{ ++ struct q6_wcss *wcss = rproc->priv; ++ u8 bit = wcss->q6.spawn_bit; ++ ++ return bit / 8; ++} ++ ++static int q6_wcss_start(struct rproc *rproc) ++{ ++ struct q6_wcss *wcss = rproc->priv; ++ int ret; ++ const struct wcss_data *desc = wcss->desc; ++ ++ qcom_q6v5_prepare(&wcss->q6); ++ ++ ret = qcom_scm_pas_auth_and_reset(desc->pasid); ++ if (ret) { ++ dev_err(wcss->dev, "wcss_reset failed\n"); ++ return ret; ++ } ++ ++ ret = qcom_q6v5_wait_for_start(&wcss->q6, 5 * HZ); ++ if (ret == -ETIMEDOUT) ++ dev_err(wcss->dev, "start timed out\n"); ++ ++ return ret; ++} ++ ++static int q6_wcss_spawn_pd(struct rproc *rproc) ++{ ++ int ret; ++ struct q6_wcss *wcss = rproc->priv; ++ ++ ret = qcom_q6v5_request_spawn(&wcss->q6); ++ if (ret == -ETIMEDOUT) { ++ dev_err(wcss->dev, "%s spawn timedout\n", rproc->name); ++ return ret; ++ } ++ ++ ret = qcom_q6v5_wait_for_start(&wcss->q6, msecs_to_jiffies(10000)); ++ if (ret == -ETIMEDOUT) { ++ dev_err(wcss->dev, "%s start timedout\n", rproc->name); ++ wcss->q6.running = false; ++ return ret; ++ } ++ wcss->q6.running = true; ++ return ret; ++} ++ ++static int wcss_pd_start(struct rproc *rproc) ++{ ++ struct q6_wcss *wcss = rproc->priv; ++ u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; ++ int ret; ++ ++ ret = qcom_scm_msa_lock(pasid); ++ if (ret) { ++ dev_err(wcss->dev, "failed to power up pd\n"); ++ return ret; ++ } ++ ++ if (wcss->q6.spawn_bit) { ++ ret = q6_wcss_spawn_pd(rproc); ++ if (ret) ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int q6_wcss_stop(struct rproc *rproc) ++{ ++ struct q6_wcss *wcss = rproc->priv; ++ const struct wcss_data *desc = wcss->desc; ++ int ret; ++ ++ ret = qcom_scm_pas_shutdown(desc->pasid); ++ if (ret) { ++ dev_err(wcss->dev, "not able to shutdown\n"); ++ return ret; ++ } ++ qcom_q6v5_unprepare(&wcss->q6); ++ ++ return 0; ++} ++ ++/** ++ * wcss_pd_stop() - Stop WCSS user pd ++ * @rproc: rproc handle ++ * ++ * Stop root pd after user pd down. Root pd ++ * is used to provide services to user pd, so ++ * keeping root pd alive when user pd is down ++ * is invalid. ++ * --------------------------------------------- ++ * ++ * ----------- ++ * |-------->| User PD1 | ++ * | ----------- ++ * | ++ * | ++ * ----- | ----------- ++ * | Q6 |---------------->| User Pd2 | ++ * ----- | ----------- ++ * | ++ * | ++ * | ----------- ++ * |--------->| User Pd3 | ++ * ----------- ++ * ---------------------------------------------- ++ */ ++static int wcss_pd_stop(struct rproc *rproc) ++{ ++ struct q6_wcss *wcss = rproc->priv; ++ struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent); ++ u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; ++ int ret; ++ ++ if (rproc->state != RPROC_CRASHED && wcss->q6.stop_bit) { ++ ret = qcom_q6v5_request_stop(&wcss->q6, NULL); ++ if (ret) { ++ dev_err(&rproc->dev, "pd not stopped\n"); ++ return ret; ++ } ++ } ++ ++ ret = qcom_scm_msa_unlock(pasid); ++ if (ret) { ++ dev_err(wcss->dev, "failed to power down pd\n"); ++ return ret; ++ } ++ ++ rproc_shutdown(rpd_rproc); ++ ++ return 0; ++} ++ ++static void *q6_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len, ++ bool *is_iomem) ++{ ++ struct q6_wcss *wcss = rproc->priv; ++ int offset; ++ ++ offset = da - wcss->mem_reloc; ++ if (offset < 0 || offset + len > wcss->mem_size) ++ return NULL; ++ ++ return wcss->mem_region + offset; ++} ++ ++/** ++ * share_upd_bootinfo_to_q6() - Share userpd boot info to Q6 root pd ++ * @rproc: rproc handle ++ * ++ * Q6 needs user pd parameters like loadaddress and ++ * PIL size to authenticate user pd with underlying ++ * security software. If authenticatoin success then ++ * only Q6 spawns user pd and sends spawn ack to rproc ++ * driver. This API is passing userpd boot info to Q6 ++ * over SMEM. ++ * ++ * User pd boot-info format mentioned below ++ *
++ * ++ * ++ * Returns 0 on success else negative value on failure. ++ */ ++static int share_upd_bootinfo_to_q6(struct rproc *rproc) ++{ ++ int ret; ++ size_t size; ++ u16 cnt = 0, version; ++ void *ptr; ++ struct q6_wcss *wcss = rproc->priv, *upd_wcss; ++ struct rproc *upd_rproc; ++ struct userpd_boot_info upd_bootinfo = {0}; ++ const struct firmware *fw; ++ ++ ret = qcom_smem_alloc(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID, ++ UPD_BOOT_INFO_SMEM_SIZE); ++ if (ret && ret != -EEXIST) { ++ dev_err(wcss->dev, ++ "failed to allocate q6 bootinfo smem segment\n"); ++ return ret; ++ } ++ ++ ptr = qcom_smem_get(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID, &size); ++ if (IS_ERR(ptr) || size != UPD_BOOT_INFO_SMEM_SIZE) { ++ dev_err(wcss->dev, ++ "Unable to acquire smp2p item(%d) ret:%ld\n", ++ UPD_BOOT_INFO_SMEM_ID, PTR_ERR(ptr)); ++ return PTR_ERR(ptr); ++ } ++ ++ /*Version*/ ++ version = VERSION2; ++ memcpy_toio(ptr, &version, sizeof(version)); ++ ptr += sizeof(version); ++ ++ list_for_each_entry(upd_rproc, &upd_rproc_list, node) ++ cnt++; ++ ++ /* No of elements */ ++ cnt = (sizeof(upd_bootinfo) * cnt); ++ memcpy_toio(ptr, &cnt, sizeof(u16)); ++ ptr += sizeof(u16); ++ ++ list_for_each_entry(upd_rproc, &upd_rproc_list, node) { ++ upd_wcss = upd_rproc->priv; ++ ++ /* TYPE */ ++ upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE; ++ ++ /* LENGTH */ ++ upd_bootinfo.header.length = ++ sizeof(upd_bootinfo) - sizeof(upd_bootinfo.header); ++ ++ /* Process ID */ ++ upd_bootinfo.pid = upd_wcss->pd_asid + 1; ++ ++ ret = request_firmware(&fw, upd_rproc->firmware, upd_wcss->dev); ++ if (ret < 0) { ++ dev_err(upd_wcss->dev, "request_firmware failed: %d\n", ret); ++ return ret; ++ } ++ ++ /* Load address */ ++ upd_bootinfo.bootaddr = rproc_get_boot_addr(upd_rproc, fw); ++ ++ /* Firmware mem size */ ++ upd_bootinfo.data_size = qcom_mdt_get_size(fw); ++ ++ release_firmware(fw); ++ ++ /* copy into smem */ ++ memcpy_toio(ptr, &upd_bootinfo, sizeof(upd_bootinfo)); ++ ptr += sizeof(upd_bootinfo); ++ } ++ return 0; ++} ++ ++static int q6_wcss_load(struct rproc *rproc, const struct firmware *fw) ++{ ++ struct q6_wcss *wcss = rproc->priv; ++ const struct firmware *fw_hdl; ++ int ret; ++ const struct wcss_data *desc = wcss->desc; ++ int loop; ++ ++ /* Share user pd boot info to Q6 remote processor */ ++ if (desc->share_upd_info_to_q6) { ++ ret = share_upd_bootinfo_to_q6(rproc); ++ if (ret) { ++ dev_err(wcss->dev, ++ "user pd boot info sharing with q6 failed %d\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware, ++ desc->pasid, wcss->mem_region, ++ wcss->mem_phys, wcss->mem_size, ++ &wcss->mem_reloc); ++ if (ret) ++ return ret; ++ ++ for (loop = 1; loop < MAX_FIRMWARE; loop++) { ++ if (!wcss->firmware[loop]) ++ continue; ++ ++ ret = request_firmware(&fw_hdl, wcss->firmware[loop], ++ wcss->dev); ++ if (ret) ++ continue; ++ ++ ret = qcom_mdt_load_no_init(wcss->dev, fw_hdl, ++ wcss->firmware[loop], 0, ++ wcss->mem_region, ++ wcss->mem_phys, ++ wcss->mem_size, ++ &wcss->mem_reloc); ++ ++ release_firmware(fw_hdl); ++ ++ if (ret) { ++ dev_err(wcss->dev, ++ "can't load %s ret:%d\n", wcss->firmware[loop], ret); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * wcss_pd_load() - Load WCSS user pd firmware ++ * @rproc: rproc handle ++ * @fw: firmware handle ++ * ++ * User pd get services from root pd. So first ++ * bring up root pd and then load userpd firmware. ++ * --------------------------------------------- ++ * ++ * ----------- ++ * |-------->| User PD1 | ++ * | ----------- ++ * | ++ * | ++ * ----- | ----------- ++ * | Q6 |---------------->| User Pd2 | ++ * ----- | ----------- ++ * | ++ * | ++ * | ----------- ++ * |--------->| User Pd3 | ++ * ----------- ++ * ---------------------------------------------- ++ * ++ */ ++static int wcss_pd_load(struct rproc *rproc, const struct firmware *fw) ++{ ++ struct q6_wcss *wcss = rproc->priv; ++ struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent); ++ u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; ++ int ret; ++ ++ ret = rproc_boot(rpd_rproc); ++ if (ret) ++ return ret; ++ ++ return qcom_mdt_load(wcss->dev, fw, rproc->firmware, ++ pasid, wcss->mem_region, ++ wcss->mem_phys, wcss->mem_size, ++ &wcss->mem_reloc); ++} ++ ++static unsigned long q6_wcss_panic(struct rproc *rproc) ++{ ++ struct q6_wcss *wcss = rproc->priv; ++ ++ return qcom_q6v5_panic(&wcss->q6); ++} ++ ++static const struct rproc_ops wcss_ops = { ++ .start = wcss_pd_start, ++ .stop = wcss_pd_stop, ++ .load = wcss_pd_load, ++ .get_boot_addr = rproc_elf_get_boot_addr, ++}; ++ ++static const struct rproc_ops q6_wcss_ops = { ++ .start = q6_wcss_start, ++ .stop = q6_wcss_stop, ++ .da_to_va = q6_wcss_da_to_va, ++ .load = q6_wcss_load, ++ .get_boot_addr = rproc_elf_get_boot_addr, ++ .panic = q6_wcss_panic, ++}; ++ ++static int q6_alloc_memory_region(struct q6_wcss *wcss) ++{ ++ struct reserved_mem *rmem = NULL; ++ struct device_node *node; ++ struct device *dev = wcss->dev; ++ ++ if (wcss->version == Q6_IPQ) { ++ node = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (node) ++ rmem = of_reserved_mem_lookup(node); ++ ++ of_node_put(node); ++ ++ if (!rmem) { ++ dev_err(dev, "unable to acquire memory-region\n"); ++ return -EINVAL; ++ } ++ } else { ++ struct rproc *rpd_rproc = dev_get_drvdata(dev->parent); ++ struct q6_wcss *rpd_wcss = rpd_rproc->priv; ++ ++ wcss->mem_phys = rpd_wcss->mem_phys; ++ wcss->mem_reloc = rpd_wcss->mem_reloc; ++ wcss->mem_size = rpd_wcss->mem_size; ++ wcss->mem_region = rpd_wcss->mem_region; ++ return 0; ++ } ++ ++ wcss->mem_phys = rmem->base; ++ wcss->mem_reloc = rmem->base; ++ wcss->mem_size = rmem->size; ++ wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size); ++ if (!wcss->mem_region) { ++ dev_err(dev, "unable to map memory region: %pa+%pa\n", ++ &rmem->base, &rmem->size); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++static int q6_get_inbound_irq(struct qcom_q6v5 *q6, ++ struct platform_device *pdev, ++ const char *int_name, ++ int index, int *pirq, ++ irqreturn_t (*handler)(int irq, void *data)) ++{ ++ int ret, irq; ++ char *interrupt, *tmp = (char *)int_name; ++ struct q6_wcss *wcss = q6->rproc->priv; ++ ++ irq = platform_get_irq(pdev, index); ++ if (irq < 0) ++ return irq; ++ ++ *pirq = irq; ++ ++ interrupt = devm_kzalloc(&pdev->dev, BUF_SIZE, GFP_KERNEL); ++ if (!interrupt) ++ return -ENOMEM; ++ ++ snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", wcss->pd_asid, tmp); ++ ++ ret = devm_request_threaded_irq(&pdev->dev, *pirq, ++ NULL, handler, ++ IRQF_TRIGGER_RISING | IRQF_ONESHOT, ++ interrupt, q6); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to acquire %s irq\n", interrupt); ++ return 0; ++} ++ ++static int q6_get_outbound_irq(struct qcom_q6v5 *q6, ++ struct platform_device *pdev, ++ const char *int_name) ++{ ++ struct qcom_smem_state *tmp_state; ++ unsigned bit; ++ ++ tmp_state = qcom_smem_state_get(&pdev->dev, int_name, &bit); ++ if (IS_ERR(tmp_state)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(tmp_state), ++ "failed to acquire %s state\n", int_name); ++ ++ if (!strcmp(int_name, "stop")) { ++ q6->state = tmp_state; ++ q6->stop_bit = bit; ++ } else if (!strcmp(int_name, "spawn")) { ++ q6->spawn_state = tmp_state; ++ q6->spawn_bit = bit; ++ } ++ ++ return 0; ++} ++ ++static int init_irq(struct qcom_q6v5 *q6, ++ struct platform_device *pdev, struct rproc *rproc, ++ int crash_reason, const char *load_state, ++ void (*handover)(struct qcom_q6v5 *q6)) ++{ ++ int ret; ++ struct q6_wcss *wcss = rproc->priv; ++ ++ q6->rproc = rproc; ++ q6->dev = &pdev->dev; ++ q6->crash_reason = crash_reason; ++ q6->handover = handover; ++ ++ init_completion(&q6->start_done); ++ init_completion(&q6->stop_done); ++ init_completion(&q6->spawn_done); ++ ++ ret = q6_get_outbound_irq(q6, pdev, "stop"); ++ if (ret) ++ return ret; ++ ++ ret = q6_get_outbound_irq(q6, pdev, "spawn"); ++ if (ret) ++ return ret; ++ ++ /* Get pd_asid to prepare interrupt names */ ++ wcss->pd_asid = qcom_get_pd_asid(rproc); ++ ++ ret = q6_get_inbound_irq(q6, pdev, "fatal", 0, &q6->fatal_irq, ++ q6v5_fatal_interrupt); ++ if (ret) ++ return ret; ++ ++ ret = q6_get_inbound_irq(q6, pdev, "ready", 1, &q6->ready_irq, ++ q6v5_ready_interrupt); ++ if (ret) ++ return ret; ++ ++ ret = q6_get_inbound_irq(q6, pdev, "stop-ack", 3, &q6->stop_irq, ++ q6v5_stop_interrupt); ++ if (ret) ++ return ret; ++ ++ ret = q6_get_inbound_irq(q6, pdev, "spawn-ack", 2, &q6->spawn_irq, ++ q6v5_spawn_interrupt); ++ if (ret) ++ return ret; ++ return 0; ++} ++ ++static void q6_release_resources(void) ++{ ++ struct rproc *upd_rproc; ++ ++ /* Release userpd resources */ ++ list_for_each_entry(upd_rproc, &upd_rproc_list, node) { ++ rproc_del(upd_rproc); ++ rproc_free(upd_rproc); ++ } ++} ++ ++static int q6_register_userpd(struct platform_device *pdev, ++ struct device_node *userpd_np) ++{ ++ struct q6_wcss *wcss; ++ struct rproc *rproc = NULL; ++ int ret; ++ struct platform_device *userpd_pdev; ++ const char *firmware_name = NULL; ++ const char *label = NULL; ++ ++ ret = of_property_read_string(userpd_np, "firmware-name", ++ &firmware_name); ++ if (ret < 0) { ++ /* All userpd's who want to register as rproc must have firmware. ++ * Other than userpd like glink they don't need any firmware. ++ * So for glink child simply return success. ++ */ ++ if (ret == -EINVAL) { ++ /* Confirming userpd_np is glink node or not */ ++ if (!of_property_read_string(userpd_np, "label", &label)) ++ return 0; ++ } ++ return ret; ++ } ++ ++ dev_info(&pdev->dev, "%s node found\n", userpd_np->name); ++ ++ userpd_pdev = of_platform_device_create(userpd_np, userpd_np->name, ++ &pdev->dev); ++ if (!userpd_pdev) ++ return dev_err_probe(&pdev->dev, -ENODEV, ++ "failed to create %s platform device\n", ++ userpd_np->name); ++ ++ userpd_pdev->dev.driver = pdev->dev.driver; ++ rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops, ++ firmware_name, sizeof(*wcss)); ++ if (!rproc) { ++ ret = -ENOMEM; ++ goto free_rproc; ++ } ++ ++ wcss = rproc->priv; ++ wcss->dev = &userpd_pdev->dev; ++ wcss->version = WCSS_IPQ; ++ ++ ret = q6_alloc_memory_region(wcss); ++ if (ret) ++ goto free_rproc; ++ ++ ret = init_irq(&wcss->q6, userpd_pdev, rproc, ++ WCSS_CRASH_REASON, NULL, NULL); ++ if (ret) ++ goto free_rproc; ++ ++ rproc->auto_boot = false; ++ ret = rproc_add(rproc); ++ if (ret) ++ goto free_rproc; ++ ++ list_add(&rproc->node, &upd_rproc_list); ++ platform_set_drvdata(userpd_pdev, rproc); ++ qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, userpd_pdev->name); ++ return 0; ++ ++free_rproc: ++ kfree(rproc); ++ return ret; ++} ++ ++static int q6_wcss_probe(struct platform_device *pdev) ++{ ++ const struct wcss_data *desc; ++ struct q6_wcss *wcss; ++ struct rproc *rproc; ++ int ret; ++ const char **firmware; ++ struct device_node *userpd_np; ++ const struct rproc_ops *ops = &q6_wcss_ops; ++ ++ desc = of_device_get_match_data(&pdev->dev); ++ if (!desc) ++ return -EINVAL; ++ ++ firmware = devm_kcalloc(&pdev->dev, MAX_FIRMWARE, ++ sizeof(*firmware), GFP_KERNEL); ++ if (!firmware) ++ return -ENOMEM; ++ ++ ret = of_property_read_string_array(pdev->dev.of_node, "firmware-name", ++ firmware, MAX_FIRMWARE); ++ if (ret < 0) ++ return ret; ++ ++ rproc = rproc_alloc(&pdev->dev, pdev->name, ops, ++ firmware[0], sizeof(*wcss)); ++ if (!rproc) ++ return -ENOMEM; ++ ++ wcss = rproc->priv; ++ wcss->dev = &pdev->dev; ++ wcss->desc = desc; ++ wcss->firmware = firmware; ++ wcss->version = Q6_IPQ; ++ ++ ret = q6_alloc_memory_region(wcss); ++ if (ret) ++ goto free_rproc; ++ ++ ret = qcom_q6v5_init(&wcss->q6, pdev, rproc, ++ WCSS_CRASH_REASON, NULL, NULL); ++ if (ret) ++ goto free_rproc; ++ ++ qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss"); ++ qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss"); ++ ++ rproc->auto_boot = false; ++ ret = rproc_add(rproc); ++ if (ret) ++ goto free_rproc; ++ ++ platform_set_drvdata(pdev, rproc); ++ ++ /* Iterate over userpd child's and register with rproc */ ++ for_each_available_child_of_node(pdev->dev.of_node, userpd_np) { ++ ret = q6_register_userpd(pdev, userpd_np); ++ if (ret) { ++ /* release resources of successfully allocated userpd rproc's */ ++ q6_release_resources(); ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to register userpd(%s)\n", ++ userpd_np->name); ++ } ++ } ++ return 0; ++ ++free_rproc: ++ rproc_free(rproc); ++ ++ return ret; ++} ++ ++static int q6_wcss_remove(struct platform_device *pdev) ++{ ++ struct rproc *rproc = platform_get_drvdata(pdev); ++ struct q6_wcss *wcss = rproc->priv; ++ ++ qcom_q6v5_deinit(&wcss->q6); ++ ++ rproc_del(rproc); ++ rproc_free(rproc); ++ ++ return 0; ++} ++ ++static const struct wcss_data q6_ipq5332_res_init = { ++ .pasid = MPD_WCNSS_PAS_ID, ++ .share_upd_info_to_q6 = true, ++}; ++ ++static const struct wcss_data q6_ipq9574_res_init = { ++ .pasid = WCNSS_PAS_ID, ++}; ++ ++static const struct of_device_id q6_wcss_of_match[] = { ++ { .compatible = "qcom,ipq5332-q6-mpd", .data = &q6_ipq5332_res_init }, ++ { .compatible = "qcom,ipq9574-q6-mpd", .data = &q6_ipq9574_res_init }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, q6_wcss_of_match); ++ ++static struct platform_driver q6_wcss_driver = { ++ .probe = q6_wcss_probe, ++ .remove = q6_wcss_remove, ++ .driver = { ++ .name = "qcom-q6-mpd", ++ .of_match_table = q6_wcss_of_match, ++ }, ++}; ++module_platform_driver(q6_wcss_driver); ++ ++MODULE_DESCRIPTION("Hexagon WCSS Multipd Peripheral Image Loader"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/qualcommax/patches-6.6/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch b/target/linux/qualcommax/patches-6.6/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch new file mode 100644 index 0000000000..b3cd492d05 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch @@ -0,0 +1,321 @@ +From 6c66dff196cbba8515380110dd3599cde31dd896 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH 1/2] rproc: qcom_q6v5_mpd: split q6_wcss to rootpd and + userpd + +Split the q6_wcss structure and create a separate userpd struct to clearly +differentiate between the process to bring up the QDSP6 processor vs +process(es) to bring up the Wifi radio(s) (WCSS) for better readability. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + drivers/remoteproc/qcom_q6v5_mpd.c | 126 +++++++++++++---------------- + 1 file changed, 56 insertions(+), 70 deletions(-) + +--- a/drivers/remoteproc/qcom_q6v5_mpd.c ++++ b/drivers/remoteproc/qcom_q6v5_mpd.c +@@ -44,10 +44,6 @@ + #define VERSION2 2 + + static LIST_HEAD(upd_rproc_list); +-enum { +- Q6_IPQ, +- WCSS_IPQ, +-}; + + /** + * struct userpd_boot_info_header - header of user pd bootinfo +@@ -82,10 +78,15 @@ struct q6_wcss { + phys_addr_t mem_reloc; + void *mem_region; + size_t mem_size; +- u8 pd_asid; + const struct wcss_data *desc; + const char **firmware; +- u32 version; ++}; ++ ++struct userpd { ++ u8 pd_asid; ++ struct device *dev; ++ struct qcom_rproc_ssr ssr_subdev; ++ struct qcom_q6v5 q6; + }; + + struct wcss_data { +@@ -101,8 +102,8 @@ struct wcss_data { + */ + static u8 qcom_get_pd_asid(struct rproc *rproc) + { +- struct q6_wcss *wcss = rproc->priv; +- u8 bit = wcss->q6.spawn_bit; ++ struct userpd *upd = rproc->priv; ++ u8 bit = upd->q6.spawn_bit; + + return bit / 8; + } +@@ -131,37 +132,37 @@ static int q6_wcss_start(struct rproc *r + static int q6_wcss_spawn_pd(struct rproc *rproc) + { + int ret; +- struct q6_wcss *wcss = rproc->priv; ++ struct userpd *upd = rproc->priv; + +- ret = qcom_q6v5_request_spawn(&wcss->q6); ++ ret = qcom_q6v5_request_spawn(&upd->q6); + if (ret == -ETIMEDOUT) { +- dev_err(wcss->dev, "%s spawn timedout\n", rproc->name); ++ dev_err(upd->dev, "%s spawn timedout\n", rproc->name); + return ret; + } + +- ret = qcom_q6v5_wait_for_start(&wcss->q6, msecs_to_jiffies(10000)); ++ ret = qcom_q6v5_wait_for_start(&upd->q6, msecs_to_jiffies(10000)); + if (ret == -ETIMEDOUT) { +- dev_err(wcss->dev, "%s start timedout\n", rproc->name); +- wcss->q6.running = false; ++ dev_err(upd->dev, "%s start timedout\n", rproc->name); ++ upd->q6.running = false; + return ret; + } +- wcss->q6.running = true; ++ upd->q6.running = true; + return ret; + } + + static int wcss_pd_start(struct rproc *rproc) + { +- struct q6_wcss *wcss = rproc->priv; +- u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; ++ struct userpd *upd = rproc->priv; ++ u32 pasid = (upd->pd_asid << 8) | UPD_SWID; + int ret; + + ret = qcom_scm_msa_lock(pasid); + if (ret) { +- dev_err(wcss->dev, "failed to power up pd\n"); ++ dev_err(upd->dev, "failed to power up pd\n"); + return ret; + } + +- if (wcss->q6.spawn_bit) { ++ if (upd->q6.spawn_bit) { + ret = q6_wcss_spawn_pd(rproc); + if (ret) + return ret; +@@ -213,22 +214,22 @@ static int q6_wcss_stop(struct rproc *rp + */ + static int wcss_pd_stop(struct rproc *rproc) + { +- struct q6_wcss *wcss = rproc->priv; +- struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent); +- u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; ++ struct userpd *upd = rproc->priv; ++ struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); ++ u32 pasid = (upd->pd_asid << 8) | UPD_SWID; + int ret; + +- if (rproc->state != RPROC_CRASHED && wcss->q6.stop_bit) { +- ret = qcom_q6v5_request_stop(&wcss->q6, NULL); ++ if (rproc->state != RPROC_CRASHED && upd->q6.stop_bit) { ++ ret = qcom_q6v5_request_stop(&upd->q6, NULL); + if (ret) { +- dev_err(&rproc->dev, "pd not stopped\n"); ++ dev_err(upd->dev, "pd not stopped\n"); + return ret; + } + } + + ret = qcom_scm_msa_unlock(pasid); + if (ret) { +- dev_err(wcss->dev, "failed to power down pd\n"); ++ dev_err(upd->dev, "failed to power down pd\n"); + return ret; + } + +@@ -273,7 +274,8 @@ static int share_upd_bootinfo_to_q6(stru + size_t size; + u16 cnt = 0, version; + void *ptr; +- struct q6_wcss *wcss = rproc->priv, *upd_wcss; ++ struct q6_wcss *wcss = rproc->priv; ++ struct userpd *upd; + struct rproc *upd_rproc; + struct userpd_boot_info upd_bootinfo = {0}; + const struct firmware *fw; +@@ -308,7 +310,7 @@ static int share_upd_bootinfo_to_q6(stru + ptr += sizeof(u16); + + list_for_each_entry(upd_rproc, &upd_rproc_list, node) { +- upd_wcss = upd_rproc->priv; ++ upd = upd_rproc->priv; + + /* TYPE */ + upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE; +@@ -318,11 +320,11 @@ static int share_upd_bootinfo_to_q6(stru + sizeof(upd_bootinfo) - sizeof(upd_bootinfo.header); + + /* Process ID */ +- upd_bootinfo.pid = upd_wcss->pd_asid + 1; ++ upd_bootinfo.pid = upd->pd_asid + 1; + +- ret = request_firmware(&fw, upd_rproc->firmware, upd_wcss->dev); ++ ret = request_firmware(&fw, upd_rproc->firmware, upd->dev); + if (ret < 0) { +- dev_err(upd_wcss->dev, "request_firmware failed: %d\n", ret); ++ dev_err(upd->dev, "request_firmware failed: %d\n", ret); + return ret; + } + +@@ -421,19 +423,20 @@ static int q6_wcss_load(struct rproc *rp + */ + static int wcss_pd_load(struct rproc *rproc, const struct firmware *fw) + { +- struct q6_wcss *wcss = rproc->priv; +- struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent); +- u32 pasid = (wcss->pd_asid << 8) | UPD_SWID; ++ struct userpd *upd = rproc->priv; ++ struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); ++ struct q6_wcss *wcss = rpd_rproc->priv; ++ u32 pasid = (upd->pd_asid << 8) | UPD_SWID; + int ret; + + ret = rproc_boot(rpd_rproc); + if (ret) + return ret; + +- return qcom_mdt_load(wcss->dev, fw, rproc->firmware, ++ return qcom_mdt_load(upd->dev, fw, rproc->firmware, + pasid, wcss->mem_region, + wcss->mem_phys, wcss->mem_size, +- &wcss->mem_reloc); ++ NULL); + } + + static unsigned long q6_wcss_panic(struct rproc *rproc) +@@ -465,26 +468,15 @@ static int q6_alloc_memory_region(struct + struct device_node *node; + struct device *dev = wcss->dev; + +- if (wcss->version == Q6_IPQ) { +- node = of_parse_phandle(dev->of_node, "memory-region", 0); +- if (node) +- rmem = of_reserved_mem_lookup(node); +- +- of_node_put(node); +- +- if (!rmem) { +- dev_err(dev, "unable to acquire memory-region\n"); +- return -EINVAL; +- } +- } else { +- struct rproc *rpd_rproc = dev_get_drvdata(dev->parent); +- struct q6_wcss *rpd_wcss = rpd_rproc->priv; +- +- wcss->mem_phys = rpd_wcss->mem_phys; +- wcss->mem_reloc = rpd_wcss->mem_reloc; +- wcss->mem_size = rpd_wcss->mem_size; +- wcss->mem_region = rpd_wcss->mem_region; +- return 0; ++ node = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (node) ++ rmem = of_reserved_mem_lookup(node); ++ ++ of_node_put(node); ++ ++ if (!rmem) { ++ dev_err(dev, "unable to acquire memory-region\n"); ++ return -EINVAL; + } + + wcss->mem_phys = rmem->base; +@@ -508,7 +500,7 @@ static int q6_get_inbound_irq(struct qco + { + int ret, irq; + char *interrupt, *tmp = (char *)int_name; +- struct q6_wcss *wcss = q6->rproc->priv; ++ struct userpd *upd = q6->rproc->priv; + + irq = platform_get_irq(pdev, index); + if (irq < 0) +@@ -520,7 +512,7 @@ static int q6_get_inbound_irq(struct qco + if (!interrupt) + return -ENOMEM; + +- snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", wcss->pd_asid, tmp); ++ snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", upd->pd_asid, tmp); + + ret = devm_request_threaded_irq(&pdev->dev, *pirq, + NULL, handler, +@@ -561,7 +553,7 @@ static int init_irq(struct qcom_q6v5 *q6 + void (*handover)(struct qcom_q6v5 *q6)) + { + int ret; +- struct q6_wcss *wcss = rproc->priv; ++ struct userpd *upd = rproc->priv; + + q6->rproc = rproc; + q6->dev = &pdev->dev; +@@ -581,7 +573,7 @@ static int init_irq(struct qcom_q6v5 *q6 + return ret; + + /* Get pd_asid to prepare interrupt names */ +- wcss->pd_asid = qcom_get_pd_asid(rproc); ++ upd->pd_asid = qcom_get_pd_asid(rproc); + + ret = q6_get_inbound_irq(q6, pdev, "fatal", 0, &q6->fatal_irq, + q6v5_fatal_interrupt); +@@ -619,7 +611,7 @@ static void q6_release_resources(void) + static int q6_register_userpd(struct platform_device *pdev, + struct device_node *userpd_np) + { +- struct q6_wcss *wcss; ++ struct userpd *upd; + struct rproc *rproc = NULL; + int ret; + struct platform_device *userpd_pdev; +@@ -652,21 +644,16 @@ static int q6_register_userpd(struct pla + + userpd_pdev->dev.driver = pdev->dev.driver; + rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops, +- firmware_name, sizeof(*wcss)); ++ firmware_name, sizeof(*upd)); + if (!rproc) { + ret = -ENOMEM; + goto free_rproc; + } + +- wcss = rproc->priv; +- wcss->dev = &userpd_pdev->dev; +- wcss->version = WCSS_IPQ; +- +- ret = q6_alloc_memory_region(wcss); +- if (ret) +- goto free_rproc; ++ upd = rproc->priv; ++ upd->dev = &userpd_pdev->dev; + +- ret = init_irq(&wcss->q6, userpd_pdev, rproc, ++ ret = init_irq(&upd->q6, userpd_pdev, rproc, + WCSS_CRASH_REASON, NULL, NULL); + if (ret) + goto free_rproc; +@@ -678,7 +665,7 @@ static int q6_register_userpd(struct pla + + list_add(&rproc->node, &upd_rproc_list); + platform_set_drvdata(userpd_pdev, rproc); +- qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, userpd_pdev->name); ++ qcom_add_ssr_subdev(rproc, &upd->ssr_subdev, userpd_pdev->name); + return 0; + + free_rproc: +@@ -719,7 +706,6 @@ static int q6_wcss_probe(struct platform + wcss->dev = &pdev->dev; + wcss->desc = desc; + wcss->firmware = firmware; +- wcss->version = Q6_IPQ; + + ret = q6_alloc_memory_region(wcss); + if (ret) diff --git a/target/linux/qualcommax/patches-6.6/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch b/target/linux/qualcommax/patches-6.6/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch new file mode 100644 index 0000000000..59f49ad66e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch @@ -0,0 +1,206 @@ +From 0fa7bdb855247b738d1d227d6f4b3417ebdf21a8 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH 2/2] remoteproc: qcom_q6v5_mpd: fix incorrent use of + rproc->node + + 1.817524] list_add corruption. next->prev should be prev (ffffffc0814bbfc8), but was ffffffc0814bc358. (next=ffffff8003b56800). +[ 1.822435] WARNING: CPU: 1 PID: 24 at lib/list_debug.c:29 __list_add_valid_or_report+0x8c/0xdc +[ 1.833923] Modules linked in: +[ 1.842425] CPU: 1 PID: 24 Comm: kworker/u4:1 Not tainted 6.6.47 #0 +[ 1.845552] Hardware name: Qualcomm MP03 (DT) +[ 1.851716] Workqueue: events_unbound deferred_probe_work_func +[ 1.856229] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) +[ 1.861959] pc : __list_add_valid_or_report+0x8c/0xdc +[ 1.868816] lr : __list_add_valid_or_report+0x8c/0xdc +[ 1.874022] sp : ffffffc081603a50 +[ 1.879055] x29: ffffffc081603a50 x28: ffffff8000fa2810 x27: ffffff8003cba800 +[ 1.882358] x26: ffffff8000fa2800 x25: ffffff8003cbac80 x24: 0000000000000000 +[ 1.889476] x23: ffffffc08088b968 x22: ffffffc0814bb000 x21: ffffffc0814bbfc8 +[ 1.896593] x20: ffffffc08088b8e8 x19: ffffff8003cba800 x18: 00000000000000b1 +[ 1.903713] x17: 3863666262343138 x16: 3063666666666666 x15: ffffffc081416e20 +[ 1.910830] x14: 0000000000000213 x13: 00000000000000b1 x12: 00000000ffffffea +[ 1.917948] x11: 00000000ffffefff x10: ffffffc08146ee20 x9 : ffffffc081416dc8 +[ 1.925066] x8 : 0000000000017fe8 x7 : c0000000ffffefff x6 : 0000000000057fa8 +[ 1.932184] x5 : 0000000000000fff x4 : 0000000000000000 x3 : ffffffc081603850 +[ 1.939302] x2 : ffffffc081416d60 x1 : ffffffc081416d60 x0 : 0000000000000075 +[ 1.946422] Call trace: +[ 1.953535] __list_add_valid_or_report+0x8c/0xdc +[ 1.955793] rproc_add+0x1f4/0x25c +[ 1.960653] q6_wcss_probe+0x510/0x634 +[ 1.963950] platform_probe+0x68/0xc4 +[ 1.967684] really_probe+0x148/0x2b0 +[ 1.971417] __driver_probe_device+0x78/0x128 +[ 1.975063] driver_probe_device+0x40/0xdc +[ 1.979402] __device_attach_driver+0xb8/0xf8 +[ 1.983397] bus_for_each_drv+0x70/0xb8 +[ 1.987823] __device_attach+0xa0/0x184 +[ 1.991468] device_initial_probe+0x14/0x20 +[ 1.995289] bus_probe_device+0xac/0xb0 +[ 1.999455] deferred_probe_work_func+0xa4/0xec +[ 2.003275] process_one_work+0x178/0x2d4 +[ 2.007788] worker_thread+0x2ec/0x4d8 +[ 2.011954] kthread+0xdc/0xe0 +[ 2.015600] ret_from_fork+0x10/0x20 + +Signed-off-by: Ziyang Huang +--- + drivers/remoteproc/qcom_q6v5_mpd.c | 53 +++++++++++++++++------------- + 1 file changed, 30 insertions(+), 23 deletions(-) + +--- a/drivers/remoteproc/qcom_q6v5_mpd.c ++++ b/drivers/remoteproc/qcom_q6v5_mpd.c +@@ -33,6 +33,7 @@ + + #define BUF_SIZE 35 + ++#define MAX_UPD 3 + #define MAX_FIRMWARE 3 + + #define RPD_SWID MPD_WCNSS_PAS_ID +@@ -43,8 +44,6 @@ + #define UPD_BOOT_INFO_SMEM_ID 507 + #define VERSION2 2 + +-static LIST_HEAD(upd_rproc_list); +- + /** + * struct userpd_boot_info_header - header of user pd bootinfo + * @type: type of bootinfo passing over smem +@@ -80,6 +79,7 @@ struct q6_wcss { + size_t mem_size; + const struct wcss_data *desc; + const char **firmware; ++ struct userpd *upd[MAX_UPD]; + }; + + struct userpd { +@@ -270,13 +270,12 @@ static void *q6_wcss_da_to_va(struct rpr + */ + static int share_upd_bootinfo_to_q6(struct rproc *rproc) + { +- int ret; ++ int i, ret; + size_t size; + u16 cnt = 0, version; + void *ptr; + struct q6_wcss *wcss = rproc->priv; + struct userpd *upd; +- struct rproc *upd_rproc; + struct userpd_boot_info upd_bootinfo = {0}; + const struct firmware *fw; + +@@ -301,16 +300,19 @@ static int share_upd_bootinfo_to_q6(stru + memcpy_toio(ptr, &version, sizeof(version)); + ptr += sizeof(version); + +- list_for_each_entry(upd_rproc, &upd_rproc_list, node) +- cnt++; ++ for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) ++ if (wcss->upd[i]) ++ cnt++; + + /* No of elements */ + cnt = (sizeof(upd_bootinfo) * cnt); + memcpy_toio(ptr, &cnt, sizeof(u16)); + ptr += sizeof(u16); + +- list_for_each_entry(upd_rproc, &upd_rproc_list, node) { +- upd = upd_rproc->priv; ++ for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) { ++ upd = wcss->upd[i]; ++ if (!upd) ++ continue; + + /* TYPE */ + upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE; +@@ -322,14 +324,14 @@ static int share_upd_bootinfo_to_q6(stru + /* Process ID */ + upd_bootinfo.pid = upd->pd_asid + 1; + +- ret = request_firmware(&fw, upd_rproc->firmware, upd->dev); ++ ret = request_firmware(&fw, upd->q6.rproc->firmware, upd->dev); + if (ret < 0) { + dev_err(upd->dev, "request_firmware failed: %d\n", ret); + return ret; + } + + /* Load address */ +- upd_bootinfo.bootaddr = rproc_get_boot_addr(upd_rproc, fw); ++ upd_bootinfo.bootaddr = rproc_get_boot_addr(upd->q6.rproc, fw); + + /* Firmware mem size */ + upd_bootinfo.data_size = qcom_mdt_get_size(fw); +@@ -597,18 +599,23 @@ static int init_irq(struct qcom_q6v5 *q6 + return 0; + } + +-static void q6_release_resources(void) ++static void q6_release_resources(struct q6_wcss *wcss) + { +- struct rproc *upd_rproc; ++ struct userpd *upd; ++ int i; + + /* Release userpd resources */ +- list_for_each_entry(upd_rproc, &upd_rproc_list, node) { +- rproc_del(upd_rproc); +- rproc_free(upd_rproc); ++ for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) { ++ upd = wcss->upd[i]; ++ if (!upd) ++ continue; ++ ++ rproc_del(upd->q6.rproc); ++ rproc_free(upd->q6.rproc); + } + } + +-static int q6_register_userpd(struct platform_device *pdev, ++static int q6_register_userpd(struct q6_wcss *wcss, + struct device_node *userpd_np) + { + struct userpd *upd; +@@ -633,16 +640,16 @@ static int q6_register_userpd(struct pla + return ret; + } + +- dev_info(&pdev->dev, "%s node found\n", userpd_np->name); ++ dev_info(wcss->dev, "%s node found\n", userpd_np->name); + + userpd_pdev = of_platform_device_create(userpd_np, userpd_np->name, +- &pdev->dev); ++ wcss->dev); + if (!userpd_pdev) +- return dev_err_probe(&pdev->dev, -ENODEV, ++ return dev_err_probe(wcss->dev, -ENODEV, + "failed to create %s platform device\n", + userpd_np->name); + +- userpd_pdev->dev.driver = pdev->dev.driver; ++ userpd_pdev->dev.driver = wcss->dev->driver; + rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops, + firmware_name, sizeof(*upd)); + if (!rproc) { +@@ -663,7 +670,7 @@ static int q6_register_userpd(struct pla + if (ret) + goto free_rproc; + +- list_add(&rproc->node, &upd_rproc_list); ++ wcss->upd[upd->pd_asid] = upd; + platform_set_drvdata(userpd_pdev, rproc); + qcom_add_ssr_subdev(rproc, &upd->ssr_subdev, userpd_pdev->name); + return 0; +@@ -728,10 +735,10 @@ static int q6_wcss_probe(struct platform + + /* Iterate over userpd child's and register with rproc */ + for_each_available_child_of_node(pdev->dev.of_node, userpd_np) { +- ret = q6_register_userpd(pdev, userpd_np); ++ ret = q6_register_userpd(wcss, userpd_np); + if (ret) { + /* release resources of successfully allocated userpd rproc's */ +- q6_release_resources(); ++ q6_release_resources(wcss); + return dev_err_probe(&pdev->dev, ret, + "Failed to register userpd(%s)\n", + userpd_np->name); diff --git a/target/linux/qualcommax/patches-6.6/0811-firmware-qcom_scm-support-MPD.patch b/target/linux/qualcommax/patches-6.6/0811-firmware-qcom_scm-support-MPD.patch new file mode 100644 index 0000000000..afcc15e69f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0811-firmware-qcom_scm-support-MPD.patch @@ -0,0 +1,128 @@ +From 6553d598cdb507f7ede020f25da646ba084a23c6 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH] firmware: qcom_scm: support MPD + +Add SCM calls to power up / down the SoC's internal WiFi radio and to +load PIL segments to support the MPD architecture. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + drivers/firmware/qcom_scm.c | 79 ++++++++++++++++++++++++++ + drivers/firmware/qcom_scm.h | 3 + + include/linux/firmware/qcom/qcom_scm.h | 3 + + 3 files changed, 85 insertions(+) + +--- a/drivers/firmware/qcom_scm.c ++++ b/drivers/firmware/qcom_scm.c +@@ -713,6 +713,85 @@ bool qcom_scm_pas_supported(u32 peripher + EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); + + /** ++ * qcom_scm_internal_wifi_powerup() - Bring up internal wifi ++ * @peripheral: peripheral id ++ * ++ * Return 0 on success. ++ */ ++int qcom_scm_internal_wifi_powerup(u32 peripheral) ++{ ++ struct qcom_scm_desc desc = { ++ .svc = QCOM_SCM_SVC_PIL, ++ .cmd = QCOM_SCM_INTERNAL_WIFI_POWERUP, ++ .arginfo = QCOM_SCM_ARGS(1), ++ .args[0] = peripheral, ++ .owner = ARM_SMCCC_OWNER_SIP, ++ }; ++ struct qcom_scm_res res; ++ int ret; ++ ++ ret = qcom_scm_call(__scm->dev, &desc, &res); ++ ++ return ret ? : res.result[0]; ++} ++EXPORT_SYMBOL(qcom_scm_internal_wifi_powerup); ++ ++/** ++ * qcom_scm_internal_wifi_shutdown() - Shut down internal wifi ++ * @peripheral: peripheral id ++ * ++ * Returns 0 on success. ++ */ ++int qcom_scm_internal_wifi_shutdown(u32 peripheral) ++{ ++ struct qcom_scm_desc desc = { ++ .svc = QCOM_SCM_SVC_PIL, ++ .cmd = QCOM_SCM_INTERNAL_WIFI_SHUTDOWN, ++ .arginfo = QCOM_SCM_ARGS(1), ++ .args[0] = peripheral, ++ .owner = ARM_SMCCC_OWNER_SIP, ++ }; ++ struct qcom_scm_res res; ++ int ret; ++ ++ ret = qcom_scm_call(__scm->dev, &desc, &res); ++ ++ return ret ? : res.result[0]; ++} ++EXPORT_SYMBOL(qcom_scm_internal_wifi_shutdown); ++ ++/** ++ * qcom_scm_pas_load_segment() - copy userpd PIL segments data to dma blocks ++ * @peripheral: peripheral id ++ * @segment: segment id ++ * @dma: handle of dma region ++ * @seg_cnt: no of dma blocks ++ * ++ * Returns 0 if trustzone successfully loads userpd PIL segments from dma ++ * blocks to DDR ++ */ ++int qcom_scm_pas_load_segment(u32 peripheral, int segment, dma_addr_t dma, int seg_cnt) ++{ ++ struct qcom_scm_desc desc = { ++ .svc = QCOM_SCM_SVC_PIL, ++ .cmd = QCOM_SCM_PIL_PAS_LOAD_SEG, ++ .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL), ++ .args[0] = peripheral, ++ .args[1] = segment, ++ .args[2] = dma, ++ .args[3] = seg_cnt, ++ .owner = ARM_SMCCC_OWNER_SIP, ++ }; ++ struct qcom_scm_res res; ++ int ret; ++ ++ ret = qcom_scm_call(__scm->dev, &desc, &res); ++ ++ return ret ? : res.result[0]; ++} ++EXPORT_SYMBOL(qcom_scm_pas_load_segment); ++ ++/** + * qcom_scm_msa_lock() - Lock given peripheral firmware region as MSA + * + * @peripheral: peripheral id +--- a/drivers/firmware/qcom_scm.h ++++ b/drivers/firmware/qcom_scm.h +@@ -98,6 +98,9 @@ extern int scm_legacy_call(struct device + #define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06 + #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 + #define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a ++#define QCOM_SCM_INTERNAL_WIFI_POWERUP 0x17 ++#define QCOM_SCM_INTERNAL_WIFI_SHUTDOWN 0x18 ++#define QCOM_SCM_PIL_PAS_LOAD_SEG 0x19 + #define QCOM_SCM_MSA_LOCK 0x24 + #define QCOM_SCM_MSA_UNLOCK 0x25 + +--- a/include/linux/firmware/qcom/qcom_scm.h ++++ b/include/linux/firmware/qcom/qcom_scm.h +@@ -81,6 +81,9 @@ extern int qcom_scm_pas_mem_setup(u32 pe + extern int qcom_scm_pas_auth_and_reset(u32 peripheral); + extern int qcom_scm_pas_shutdown(u32 peripheral); + extern bool qcom_scm_pas_supported(u32 peripheral); ++extern int qcom_scm_internal_wifi_powerup(u32 peripheral); ++extern int qcom_scm_internal_wifi_shutdown(u32 peripheral); ++extern int qcom_scm_pas_load_segment(u32 peripheral, int segment, dma_addr_t dma, int seg_cnt); + extern int qcom_scm_msa_lock(u32 peripheral); + extern int qcom_scm_msa_unlock(u32 peripheral); + diff --git a/target/linux/qualcommax/patches-6.6/0812-soc-qcom-mdt_loader-support-MPD.patch b/target/linux/qualcommax/patches-6.6/0812-soc-qcom-mdt_loader-support-MPD.patch new file mode 100644 index 0000000000..935a943858 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0812-soc-qcom-mdt_loader-support-MPD.patch @@ -0,0 +1,199 @@ +From bf42d84868bc82a9cb334a33930f2d1da24f7070 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH] soc: qcom: mdt_loader: support MPD + +Add support for loading user PD specific PIL segments as required by the +MPD architecture. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + drivers/soc/qcom/mdt_loader.c | 110 ++++++++++++++++++++++++++-- + include/linux/soc/qcom/mdt_loader.h | 5 ++ + 2 files changed, 110 insertions(+), 5 deletions(-) + +--- a/drivers/soc/qcom/mdt_loader.c ++++ b/drivers/soc/qcom/mdt_loader.c +@@ -16,6 +16,16 @@ + #include + #include + #include ++#include ++ ++#include "../../remoteproc/qcom_common.h" ++ ++#define QCOM_MDT_PF_ASID_MASK GENMASK(19, 16) ++ ++struct segment_load_args { ++ __le64 addr; ++ __le64 blk_size; ++}; + + static bool mdt_phdr_valid(const struct elf32_phdr *phdr) + { +@@ -69,6 +79,56 @@ static ssize_t mdt_load_split_segment(vo + return ret; + } + ++static int mdt_load_split_segment_dma(int pas_id, unsigned int segment, ++ const struct elf32_phdr *phdrs, ++ const char *fw_name, ++ struct device *dev) ++{ ++ const struct elf32_phdr *phdr = &phdrs[segment]; ++ struct segment_load_args *args; ++ dma_addr_t *addrs; ++ void *ptr; ++ dma_addr_t dma_args, dma_addrs, dma_ptr; ++ int ret; ++ ++ args = dma_alloc_coherent(dev, sizeof(*args) + sizeof(*addrs), &dma_args, GFP_DMA); ++ if (!args) { ++ dev_err(dev, "Error in dma alloc regin: %ld\n", sizeof(*args)); ++ return -ENOMEM; ++ } ++ ++ addrs = (void *) args + sizeof(*args); ++ dma_addrs = dma_args + sizeof(*args); ++ ++ ptr = dma_alloc_coherent(dev, phdr->p_filesz, &dma_ptr, GFP_DMA); ++ if (!ptr) { ++ dev_err(dev, "Error in dma alloc ptr: %d\n", phdr->p_filesz); ++ return -ENOMEM; ++ } ++ ++ args->addr = dma_addrs; ++ args->blk_size = phdr->p_filesz; ++ ++ addrs[0] = dma_ptr; ++ ++ ret = mdt_load_split_segment(ptr, phdrs, segment, fw_name, dev); ++ if (ret < 0) { ++ dev_err(dev, "Error in mdt_load_split_segment: %d\n", ret); ++ return ret; ++ } ++ ++ ret = qcom_scm_pas_load_segment(pas_id, segment, dma_args, 1); ++ if (ret < 0) { ++ dev_err(dev, "Error in qcom_scm_pas_load_segment: %d\n", ret); ++ return ret; ++ } ++ ++ dma_free_coherent(dev, phdr->p_filesz, ptr, dma_ptr); ++ dma_free_coherent(dev, sizeof(*args) + sizeof(*addrs), args, dma_args); ++ ++ return 0; ++} ++ + /** + * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt + * @fw: firmware object for the mdt file +@@ -295,7 +355,8 @@ static bool qcom_mdt_bins_are_split(cons + static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, + const char *fw_name, int pas_id, void *mem_region, + phys_addr_t mem_phys, size_t mem_size, +- phys_addr_t *reloc_base, bool pas_init) ++ phys_addr_t *reloc_base, bool pas_init, ++ bool dma_require, int pd_asid) + { + const struct elf32_phdr *phdrs; + const struct elf32_phdr *phdr; +@@ -349,6 +410,14 @@ static int __qcom_mdt_load(struct device + if (!mdt_phdr_valid(phdr)) + continue; + ++ /* ++ * While doing PD specific reloading, load only that PD ++ * specific writeable entries. Skip others ++ */ ++ if (pd_asid && (FIELD_GET(QCOM_MDT_PF_ASID_MASK, phdr->p_flags) != pd_asid || ++ (phdr->p_flags & PF_W) == 0)) ++ continue; ++ + offset = phdr->p_paddr - mem_reloc; + if (offset < 0 || offset + phdr->p_memsz > mem_size) { + dev_err(dev, "segment outside memory range\n"); +@@ -366,7 +435,11 @@ static int __qcom_mdt_load(struct device + + ptr = mem_region + offset; + +- if (phdr->p_filesz && !is_split) { ++ if (dma_require && phdr->p_filesz) { ++ ret = mdt_load_split_segment_dma(pas_id, i, phdrs, fw_name, dev); ++ if (ret) ++ break; ++ } else if (phdr->p_filesz && !is_split) { + /* Firmware is large enough to be non-split */ + if (phdr->p_offset + phdr->p_filesz > fw->size) { + dev_err(dev, "file %s segment %d would be truncated\n", +@@ -383,7 +456,7 @@ static int __qcom_mdt_load(struct device + break; + } + +- if (phdr->p_memsz > phdr->p_filesz) ++ if (!dma_require && phdr->p_memsz > phdr->p_filesz) + memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); + } + +@@ -418,7 +491,7 @@ int qcom_mdt_load(struct device *dev, co + return ret; + + return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, +- mem_size, reloc_base, true); ++ mem_size, reloc_base, true, false, 0); + } + EXPORT_SYMBOL_GPL(qcom_mdt_load); + +@@ -441,9 +514,36 @@ int qcom_mdt_load_no_init(struct device + size_t mem_size, phys_addr_t *reloc_base) + { + return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, +- mem_size, reloc_base, false); ++ mem_size, reloc_base, false, false, 0); + } + EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); + ++/** ++ * qcom_mdt_load_pd_seg() - load userpd specific PIL segements ++ * @dev: device handle to associate resources with ++ * @fw: firmware object for the mdt file ++ * @firmware: name of the firmware, for construction of segment file names ++ * @pas_id: PAS identifier ++ * @mem_region: allocated memory region to load firmware into ++ * @mem_phys: physical address of allocated memory region ++ * @mem_size: size of the allocated memory region ++ * @reloc_base: adjusted physical address after relocation ++ * ++ * Here userpd PIL segements are stitched with rootpd firmware. ++ * This function reloads userpd specific PIL segments during SSR ++ * of userpd. ++ * ++ * Returns 0 on success, negative errno otherwise. ++ */ ++int qcom_mdt_load_pd_seg(struct device *dev, const struct firmware *fw, ++ const char *firmware, int pas_id, int pd_asid, void *mem_region, ++ phys_addr_t mem_phys, size_t mem_size, ++ phys_addr_t *reloc_base) ++{ ++ return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, ++ mem_size, reloc_base, false, true, pd_asid); ++} ++EXPORT_SYMBOL_GPL(qcom_mdt_load_pd_seg); ++ + MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); + MODULE_LICENSE("GPL v2"); +--- a/include/linux/soc/qcom/mdt_loader.h ++++ b/include/linux/soc/qcom/mdt_loader.h +@@ -30,6 +30,11 @@ int qcom_mdt_load_no_init(struct device + void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, + const char *fw_name, struct device *dev); + ++int qcom_mdt_load_pd_seg(struct device *dev, const struct firmware *fw, ++ const char *firmware, int pas_id, int pd_asid, void *mem_region, ++ phys_addr_t mem_phys, size_t mem_size, ++ phys_addr_t *reloc_base); ++ + #else /* !IS_ENABLED(CONFIG_QCOM_MDT_LOADER) */ + + static inline ssize_t qcom_mdt_get_size(const struct firmware *fw) diff --git a/target/linux/qualcommax/patches-6.6/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch b/target/linux/qualcommax/patches-6.6/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch new file mode 100644 index 0000000000..f549580f3d --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch @@ -0,0 +1,38 @@ +From e83215d5d22946885fa388d375b12f1b991a43c1 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH 3/5] remoteproc: qcom_q6v5_mpd: enable clocks + +Signed-off-by: Ziyang Huang +--- + drivers/remoteproc/qcom_q6v5_mpd.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/remoteproc/qcom_q6v5_mpd.c ++++ b/drivers/remoteproc/qcom_q6v5_mpd.c +@@ -77,6 +77,8 @@ struct q6_wcss { + phys_addr_t mem_reloc; + void *mem_region; + size_t mem_size; ++ struct clk_bulk_data *clks; ++ int num_clks; + const struct wcss_data *desc; + const char **firmware; + struct userpd *upd[MAX_UPD]; +@@ -718,6 +720,16 @@ static int q6_wcss_probe(struct platform + if (ret) + goto free_rproc; + ++ wcss->num_clks = devm_clk_bulk_get_all(wcss->dev, &wcss->clks); ++ if (wcss->num_clks < 0) ++ return dev_err_probe(wcss->dev, wcss->num_clks, ++ "failed to acquire clocks\n"); ++ ++ ret = clk_bulk_prepare_enable(wcss->num_clks, wcss->clks); ++ if (ret) ++ return dev_err_probe(wcss->dev, ret, ++ "failed to enable clocks\n"); ++ + ret = qcom_q6v5_init(&wcss->q6, pdev, rproc, + WCSS_CRASH_REASON, NULL, NULL); + if (ret) diff --git a/target/linux/qualcommax/patches-6.6/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch b/target/linux/qualcommax/patches-6.6/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch new file mode 100644 index 0000000000..9316458cbd --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch @@ -0,0 +1,111 @@ +From 4ae334127f073aa5f7c9209c9f0a17fd9e331db1 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH] remoteproc: qcom_q6v5_mpd: support ipq5018 + +Signed-off-by: Ziyang Huang +--- + drivers/remoteproc/qcom_q6v5_mpd.c | 37 +++++++++++++++++++++++++++--- + 1 file changed, 34 insertions(+), 3 deletions(-) + +--- a/drivers/remoteproc/qcom_q6v5_mpd.c ++++ b/drivers/remoteproc/qcom_q6v5_mpd.c +@@ -155,6 +155,8 @@ static int q6_wcss_spawn_pd(struct rproc + static int wcss_pd_start(struct rproc *rproc) + { + struct userpd *upd = rproc->priv; ++ struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); ++ struct q6_wcss *wcss = rpd_rproc->priv; + u32 pasid = (upd->pd_asid << 8) | UPD_SWID; + int ret; + +@@ -170,6 +172,14 @@ static int wcss_pd_start(struct rproc *r + return ret; + } + ++ if (upd->pd_asid == 1) { ++ ret = qcom_scm_internal_wifi_powerup(wcss->desc->pasid); ++ if (ret) { ++ dev_err(upd->dev, "failed to power up internal radio\n"); ++ return ret; ++ } ++ } ++ + return ret; + } + +@@ -179,6 +189,12 @@ static int q6_wcss_stop(struct rproc *rp + const struct wcss_data *desc = wcss->desc; + int ret; + ++ ret = qcom_q6v5_request_stop(&wcss->q6, NULL); ++ if (ret) { ++ dev_err(wcss->dev, "pd not stopped\n"); ++ return ret; ++ } ++ + ret = qcom_scm_pas_shutdown(desc->pasid); + if (ret) { + dev_err(wcss->dev, "not able to shutdown\n"); +@@ -218,6 +234,7 @@ static int wcss_pd_stop(struct rproc *rp + { + struct userpd *upd = rproc->priv; + struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); ++ struct q6_wcss *wcss = rpd_rproc->priv; + u32 pasid = (upd->pd_asid << 8) | UPD_SWID; + int ret; + +@@ -229,6 +246,14 @@ static int wcss_pd_stop(struct rproc *rp + } + } + ++ if (upd->pd_asid == 1) { ++ ret = qcom_scm_internal_wifi_shutdown(wcss->desc->pasid); ++ if (ret) { ++ dev_err(upd->dev, "failed to power down internal radio\n"); ++ return ret; ++ } ++ } ++ + ret = qcom_scm_msa_unlock(pasid); + if (ret) { + dev_err(upd->dev, "failed to power down pd\n"); +@@ -430,15 +455,14 @@ static int wcss_pd_load(struct rproc *rp + struct userpd *upd = rproc->priv; + struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent); + struct q6_wcss *wcss = rpd_rproc->priv; +- u32 pasid = (upd->pd_asid << 8) | UPD_SWID; + int ret; + + ret = rproc_boot(rpd_rproc); + if (ret) + return ret; + +- return qcom_mdt_load(upd->dev, fw, rproc->firmware, +- pasid, wcss->mem_region, ++ return qcom_mdt_load_pd_seg(upd->dev, fw, rproc->firmware, ++ wcss->desc->pasid, upd->pd_asid, wcss->mem_region, + wcss->mem_phys, wcss->mem_size, + NULL); + } +@@ -777,6 +801,12 @@ static int q6_wcss_remove(struct platfor + return 0; + } + ++static const struct wcss_data q6_ipq5018_res_init = { ++ .pasid = MPD_WCNSS_PAS_ID, ++ // .share_upd_info_to_q6 = true, /* Version 1 */ ++ // .mdt_load_sec = qcom_mdt_load_pd_seg, ++}; ++ + static const struct wcss_data q6_ipq5332_res_init = { + .pasid = MPD_WCNSS_PAS_ID, + .share_upd_info_to_q6 = true, +@@ -787,6 +817,7 @@ static const struct wcss_data q6_ipq9574 + }; + + static const struct of_device_id q6_wcss_of_match[] = { ++ { .compatible = "qcom,ipq5018-q6-mpd", .data = &q6_ipq5018_res_init }, + { .compatible = "qcom,ipq5332-q6-mpd", .data = &q6_ipq5332_res_init }, + { .compatible = "qcom,ipq9574-q6-mpd", .data = &q6_ipq9574_res_init }, + { }, diff --git a/target/linux/qualcommax/patches-6.6/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch b/target/linux/qualcommax/patches-6.6/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch new file mode 100644 index 0000000000..fdc2879f03 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch @@ -0,0 +1,163 @@ +From: George Moussalem +Date: Mon, 09 Dec 2024 09:59:38 +0400 +Subject: [PATCH] remoteproc: qcom_q6v5_mpd: add support for passing v1 bootargs + +On multi-PD platforms such as IPQ5018, boot args are passed to the root PD +run on the Q6 processor which in turn boots the user PDs for internal +(IPQ5018) and external wifi radios (such as QCN6122). These boot args +let the user PD process know details like what PCIE index, user PD ID, and +reset GPIO is used. These are otherwise hardcoded in the firmware. + +Below is the structure expected of the version 1 boot args including the +default values hardcoded in the firmware for IPQ5018: + ++------------+------+--------------+--------------+ +| Argument | type | def val UPD2 | def val UPD3 | ++------------+------+--------------+--------------+ +| PCIE Index | u32 | 0x02 (PCIE1) | 0x01 (PCIE0) | +| Length | u32 | 0x04 | 0x04 | +| User PD ID | u32 | 0x02 | 0x03 | +| Reset GPIO | u32 | 0x12 | 0x0f | +| Reserved 1 | u32 | 0x00 | 0x00 | +| Reserved 2 | u32 | 0x00 | 0x00 | ++------------+------+--------------+--------------+ + +On IPQ5018/QCN6122 boards, the default mapping is as follows: + + +-> UPD1 ----> IPQ5018 Internal 2.4G Radio + / + / +Root PD +---> UPD2 ----> QCN6122 6G Radio on PCIE1 (if available) + \ + \ + +-> UPD3 ----> QCN6102 5G Radio on PCIE0 + +To support (future) boards with other mappings or control what UPD ID is +used, let's add support for passing boot args for more flexibility. + +Signed-off-by: George Moussalem +--- +--- a/drivers/remoteproc/qcom_q6v5_mpd.c ++++ b/drivers/remoteproc/qcom_q6v5_mpd.c +@@ -42,7 +42,11 @@ + #define UPD_BOOT_INFO_SMEM_SIZE 4096 + #define UPD_BOOT_INFO_HEADER_TYPE 0x2 + #define UPD_BOOT_INFO_SMEM_ID 507 +-#define VERSION2 2 ++ ++enum q6_bootargs_version { ++ VERSION1 = 1, ++ VERSION2, ++}; + + /** + * struct userpd_boot_info_header - header of user pd bootinfo +@@ -94,6 +98,7 @@ struct userpd { + struct wcss_data { + u32 pasid; + bool share_upd_info_to_q6; ++ u8 bootargs_version; + }; + + /** +@@ -298,10 +303,13 @@ static void *q6_wcss_da_to_va(struct rpr + static int share_upd_bootinfo_to_q6(struct rproc *rproc) + { + int i, ret; ++ u32 rd_val; + size_t size; + u16 cnt = 0, version; + void *ptr; ++ u8 *bootargs_arr; + struct q6_wcss *wcss = rproc->priv; ++ struct device_node *np = wcss->dev->of_node; + struct userpd *upd; + struct userpd_boot_info upd_bootinfo = {0}; + const struct firmware *fw; +@@ -323,10 +331,47 @@ static int share_upd_bootinfo_to_q6(stru + } + + /*Version*/ +- version = VERSION2; ++ version = (wcss->desc->bootargs_version) ? wcss->desc->bootargs_version : VERSION2; + memcpy_toio(ptr, &version, sizeof(version)); + ptr += sizeof(version); + ++ cnt = ret = of_property_count_u32_elems(np, "boot-args"); ++ if (ret < 0) { ++ if (ret == -ENODATA) { ++ dev_err(wcss->dev, "failed to read boot args ret:%d\n", ret); ++ return ret; ++ } ++ cnt = 0; ++ } ++ ++ /* No of elements */ ++ memcpy_toio(ptr, &cnt, sizeof(u16)); ++ ptr += sizeof(u16); ++ ++ bootargs_arr = kzalloc(cnt, GFP_KERNEL); ++ if (!bootargs_arr) { ++ dev_err(wcss->dev, "failed to allocate memory\n"); ++ return PTR_ERR(bootargs_arr); ++ } ++ ++ for (i = 0; i < cnt; i++) { ++ ret = of_property_read_u32_index(np, "boot-args", i, &rd_val); ++ if (ret) { ++ dev_err(wcss->dev, "failed to read boot args\n"); ++ kfree(bootargs_arr); ++ return ret; ++ } ++ bootargs_arr[i] = (u8)rd_val; ++ } ++ ++ /* Copy bootargs */ ++ memcpy_toio(ptr, bootargs_arr, cnt); ++ ptr += (cnt); ++ ++ of_node_put(np); ++ kfree(bootargs_arr); ++ cnt = 0; ++ + for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) + if (wcss->upd[i]) + cnt++; +@@ -382,12 +427,14 @@ static int q6_wcss_load(struct rproc *rp + + /* Share user pd boot info to Q6 remote processor */ + if (desc->share_upd_info_to_q6) { +- ret = share_upd_bootinfo_to_q6(rproc); +- if (ret) { +- dev_err(wcss->dev, +- "user pd boot info sharing with q6 failed %d\n", +- ret); +- return ret; ++ if (of_property_present(wcss->dev->of_node, "boot-args")) { ++ ret = share_upd_bootinfo_to_q6(rproc); ++ if (ret) { ++ dev_err(wcss->dev, ++ "user pd boot info sharing with q6 failed %d\n", ++ ret); ++ return ret; ++ } + } + } + +@@ -803,13 +850,15 @@ static int q6_wcss_remove(struct platfor + + static const struct wcss_data q6_ipq5018_res_init = { + .pasid = MPD_WCNSS_PAS_ID, +- // .share_upd_info_to_q6 = true, /* Version 1 */ ++ .share_upd_info_to_q6 = true, ++ .bootargs_version = VERSION1, + // .mdt_load_sec = qcom_mdt_load_pd_seg, + }; + + static const struct wcss_data q6_ipq5332_res_init = { + .pasid = MPD_WCNSS_PAS_ID, + .share_upd_info_to_q6 = true, ++ .bootargs_version = VERSION2, + }; + + static const struct wcss_data q6_ipq9574_res_init = { diff --git a/target/linux/qualcommax/patches-6.6/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch b/target/linux/qualcommax/patches-6.6/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch new file mode 100644 index 0000000000..786345f640 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch @@ -0,0 +1,241 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] arm64: dts: qcom: ipq5018: add wifi support + +The IPQ5018 SoC comes with an internal 2x2 2.4Ghz wifi radio. +QCN6122 is a PCIe based wifi solution specific to the IPQ5018 platform which +comes optinally packed with 1 or 2 QCN6122 chips or with an external +PCIe based wifi solution (such as QCN9074) for 5/6 Ghz support. + +As such, add wifi nodes for both IPQ5018 and QCN6122. + +Signed-off-by: George Moussalem +--- +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -692,6 +692,225 @@ + }; + }; + ++ wifi0: wifi@c000000 { ++ compatible = "qcom,ipq5018-wifi"; ++ reg = <0xc000000 0x1000000>; ++ ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ ++ interrupt-names = "misc-pulse1", ++ "misc-latch", ++ "sw-exception", ++ "watchdog", ++ "ce0", ++ "ce1", ++ "ce2", ++ "ce3", ++ "ce4", ++ "ce5", ++ "ce6", ++ "ce7", ++ "ce8", ++ "ce9", ++ "ce10", ++ "ce11", ++ "host2wbm-desc-feed", ++ "host2reo-re-injection", ++ "host2reo-command", ++ "host2rxdma-monitor-ring3", ++ "host2rxdma-monitor-ring2", ++ "host2rxdma-monitor-ring1", ++ "reo2ost-exception", ++ "wbm2host-rx-release", ++ "reo2host-status", ++ "reo2host-destination-ring4", ++ "reo2host-destination-ring3", ++ "reo2host-destination-ring2", ++ "reo2host-destination-ring1", ++ "rxdma2host-monitor-destination-mac3", ++ "rxdma2host-monitor-destination-mac2", ++ "rxdma2host-monitor-destination-mac1", ++ "ppdu-end-interrupts-mac3", ++ "ppdu-end-interrupts-mac2", ++ "ppdu-end-interrupts-mac1", ++ "rxdma2host-monitor-status-ring-mac3", ++ "rxdma2host-monitor-status-ring-mac2", ++ "rxdma2host-monitor-status-ring-mac1", ++ "host2rxdma-host-buf-ring-mac3", ++ "host2rxdma-host-buf-ring-mac2", ++ "host2rxdma-host-buf-ring-mac1", ++ "rxdma2host-destination-ring-mac3", ++ "rxdma2host-destination-ring-mac2", ++ "rxdma2host-destination-ring-mac1", ++ "host2tcl-input-ring4", ++ "host2tcl-input-ring3", ++ "host2tcl-input-ring2", ++ "host2tcl-input-ring1", ++ "wbm2host-tx-completions-ring3", ++ "wbm2host-tx-completions-ring2", ++ "wbm2host-tx-completions-ring1", ++ "tcl2host-status-ring"; ++ ++ status = "disabled"; ++ }; ++ ++ //QCN6102 5G ++ wifi1: wifi1@c000000 { ++ reg = <0x0b00a040 0x0>; ++ compatible = "qcom,qcn6122-wifi"; ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ status = "disabled"; ++ }; ++ ++ //QCN6122 5G/6G ++ wifi2: wifi2@c000000 { ++ reg = <0x0b00a040 0x0>; ++ compatible = "qcom,qcn6122-wifi"; ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ status = "disabled"; ++ }; ++ ++ q6v5_wcss: remoteproc@cd00000 { ++ compatible = "qcom,ipq5018-q6-mpd"; ++ reg = <0x0cd00000 0x4040>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ clocks = <&gcc GCC_XO_CLK>, ++ <&gcc GCC_SLEEP_CLK_SRC>, ++ <&gcc GCC_SYS_NOC_WCSS_AHB_CLK>; ++ ++ interrupts-extended = <&intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING>, ++ <&wcss_smp2p_in 0 IRQ_TYPE_NONE>, ++ <&wcss_smp2p_in 1 IRQ_TYPE_NONE>, ++ <&wcss_smp2p_in 2 IRQ_TYPE_NONE>, ++ <&wcss_smp2p_in 3 IRQ_TYPE_NONE>; ++ interrupt-names = "wdog", ++ "fatal", ++ "ready", ++ "handover", ++ "stop-ack"; ++ ++ qcom,smem-states = <&wcss_smp2p_out 0>, ++ <&wcss_smp2p_out 1>; ++ qcom,smem-state-names = "shutdown", ++ "stop"; ++ ++ status = "disabled"; ++ ++ glink-edge { ++ interrupts = ; ++ label = "rtr"; ++ qcom,remote-pid = <1>; ++ mboxes = <&apcs_glb 8>; ++ ++ qrtr_requests { ++ qcom,glink-channels = "IPCRTR"; ++ }; ++ }; ++ }; ++ ++ wcss: smp2p-wcss { ++ compatible = "qcom,smp2p"; ++ qcom,smem = <435>, <428>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = ; ++ ++ mboxes = <&apcs_glb 9>; ++ ++ qcom,local-pid = <0>; ++ qcom,remote-pid = <1>; ++ ++ wcss_smp2p_out: master-kernel { ++ qcom,entry-name = "master-kernel"; ++ qcom,smp2p-feature-ssr-ack; ++ #qcom,smem-state-cells = <1>; ++ }; ++ ++ wcss_smp2p_in: slave-kernel { ++ qcom,entry-name = "slave-kernel"; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ }; ++ + pcie1: pcie@80000000 { + compatible = "qcom,pcie-ipq5018"; + reg = <0x80000000 0xf1d>, diff --git a/target/linux/qualcommax/patches-6.6/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch b/target/linux/qualcommax/patches-6.6/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch new file mode 100644 index 0000000000..03a9c1c981 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch @@ -0,0 +1,22 @@ +From: George Moussalem +Date: Wed, 05 Feb 2025 12:12:47 +0400 +Subject: [PATCH] arm64: dts: qcom: ipq5018: add tz_apps reserved memory region + +Add tz_apps memory region needed for wifi to work. + +Signed-off-by: George Moussalem +--- +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -113,6 +113,11 @@ + #size-cells = <2>; + ranges; + ++ tz_apps@4a400000 { ++ reg = <0x0 0x4a400000 0x0 0x400000>; ++ no-map; ++ }; ++ + bootloader@4a800000 { + reg = <0x0 0x4a800000 0x0 0x200000>; + no-map; From ea83f7de2b02e65eb6c59585fab2fd47f8e752a2 Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 7 Oct 2024 15:26:32 +0400 Subject: [PATCH 11/17] ath11k-firmware: add wifi firmware for IPQ5018 Add ability to download and package ath11k firmware for IPQ5018. As part of commit 172ccf7, the source is pointed to the new QCA repo. Until QCA publishes updated firmware for IPQ5018 and QCN6122, we need to download the firmware from the old QUIC repo. As such, add a new download routine for IPQ5018/QCN6122 to fetch the firmware files from the old repo. While at it, add support for packaging BDFs for IPQ5018-based boards and update iwinfo to recognize IPQ5018 wifi. Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- package/firmware/ath11k-firmware/Makefile | 43 +++++++++++++++++++++++ package/firmware/ipq-wifi/Makefile | 4 ++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/package/firmware/ath11k-firmware/Makefile b/package/firmware/ath11k-firmware/Makefile index 0acc3f9f1a..85387289c1 100644 --- a/package/firmware/ath11k-firmware/Makefile +++ b/package/firmware/ath11k-firmware/Makefile @@ -25,6 +25,21 @@ include $(INCLUDE_DIR)/package.mk RSTRIP:=: STRIP:=: +ATH11K_LEGACY_FW_NAME:=ath11k-legacy-firmware +ATH11K_LEGACY_FW_DATE:=2024-03-14 +ATH11K_LEGACY_FW_RELEASE:=795809c7 +ATH11K_LEGACY_FW_SUBDIR:=$(ATH11K_LEGACY_FW_NAME)-$(ATH11K_LEGACY_FW_DATE)~$(ATH11K_LEGACY_FW_RELEASE) +ATH11K_LEGACY_FW_SOURCE=$(ATH11K_LEGACY_FW_SUBDIR).tar.zst + +define Download/$(ATH11K_LEGACY_FW_NAME) + FILE:=$(ATH11K_LEGACY_FW_SOURCE) + PROTO:=git + URL:=https://github.com/quic/upstream-wifi-fw.git + SOURCE_VERSION:=795809c7041582bd51bdfaa1f548b916ae8d4382 + MIRROR_HASH:=e042024b6762a5b4fe56f1a5bf8870df2e7abcca90ec22103cd46a609bee7cec + SUBDIR:=$(ATH11K_LEGACY_FW_SUBDIR) +endef + define Package/ath11k-firmware-default SECTION:=firmware CATEGORY:=Firmware @@ -32,6 +47,11 @@ define Package/ath11k-firmware-default DEPENDS:= endef +define Package/ath11k-firmware-ipq5018 +$(Package/ath11k-firmware-default) + TITLE:=IPQ5018 ath11k firmware +endef + define Package/ath11k-firmware-ipq6018 $(Package/ath11k-firmware-default) TITLE:=IPQ6018 ath11k firmware @@ -47,10 +67,32 @@ $(Package/ath11k-firmware-default) TITLE:=QCN9074 ath11k firmware endef +define Build/Clean + $(call Build/Clean/Default,) + + rm -rf \ + $(BUILD_DIR)/$(ATH11K_LEGACY_FW_SUBDIR) +endef + +define Build/Prepare + $(eval $(call Download,ath11k-legacy-firmware)) + + $(call Build/Prepare/Default,) + + $(TAR) -C $(BUILD_DIR) -xf $(DL_DIR)/$(ATH11K_LEGACY_FW_SOURCE) +endef + define Build/Compile endef +define Package/ath11k-firmware-ipq5018/install + $(INSTALL_DIR) $(1)/lib/firmware/ath11k/IPQ5018/hw1.0 + $(INSTALL_DATA) \ + $(BUILD_DIR)/$(ATH11K_LEGACY_FW_SUBDIR)/ath11k-firmware/IPQ5018_QCN6122_QCN6122/hw1.0/2.7.0.1/WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1/*.* \ + $(1)/lib/firmware/ath11k/IPQ5018/hw1.0/ +endef + define Package/ath11k-firmware-ipq6018/install $(INSTALL_DIR) $(1)/lib/firmware/IPQ6018 $(INSTALL_DATA) \ @@ -74,6 +116,7 @@ define Package/ath11k-firmware-qcn9074/install $(PKG_BUILD_DIR)/QCN9074/hw1.0/board-2.bin $(1)/lib/firmware/ath11k/QCN9074/hw1.0/board-2.bin endef +$(eval $(call BuildPackage,ath11k-firmware-ipq5018)) $(eval $(call BuildPackage,ath11k-firmware-ipq6018)) $(eval $(call BuildPackage,ath11k-firmware-ipq8074)) $(eval $(call BuildPackage,ath11k-firmware-qcn9074)) diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile index da5fd5e23e..aee0b07680 100644 --- a/package/firmware/ipq-wifi/Makefile +++ b/package/firmware/ipq-wifi/Makefile @@ -102,6 +102,8 @@ define ipq-wifi-install-one $(call ipq-wifi-install-one-to,$(1),$(2),QCA9984/hw1.0),\ $(if $(filter $(suffix $(1)),.QCA99X0 .qca99x0),\ $(call ipq-wifi-install-one-to,$(1),$(2),QCA99X0/hw2.0),\ + $(if $(filter $(suffix $(1)),.IPQ5018 .ipq5018),\ + $(call ipq-wifi-install-ath11-one-to,$(1),$(2),IPQ5018/hw1.0),\ $(if $(filter $(suffix $(1)),.IPQ6018 .ipq6018),\ $(call ipq-wifi-install-ath11-one-to,$(1),$(2),IPQ6018/hw1.0),\ $(if $(filter $(suffix $(1)),.IPQ8074 .ipq8074),\ @@ -109,7 +111,7 @@ define ipq-wifi-install-one $(if $(filter $(suffix $(1)),.QCN9074 .qcn9074),\ $(call ipq-wifi-install-ath11-one-to,$(1),$(2),QCN9074/hw1.0),\ $(error Unrecognized board-file suffix '$(suffix $(1))' for '$(1)')\ - )))))))) + ))))))))) endef # Blank line required at end of above define due to foreach context From f520f54ab261677863f2e071da9ba5b1960227ad Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 7 Oct 2024 15:54:27 +0400 Subject: [PATCH 12/17] mac80211: ath11k: fix remapped ce access on 64-bit OS https://lore.kernel.org/linux-wireless/TYZPR01MB55563B3A689D54D18179E5B4C9192@TYZPR01MB5556.apcprd01.prod.exchangelabs.com/ Signed-off-by: Ziyang Huang Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- ...apped-ce-accessing-issue-on-64bit-OS.patch | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 package/kernel/mac80211/patches/ath11k/910-ath11k-fix-remapped-ce-accessing-issue-on-64bit-OS.patch diff --git a/package/kernel/mac80211/patches/ath11k/910-ath11k-fix-remapped-ce-accessing-issue-on-64bit-OS.patch b/package/kernel/mac80211/patches/ath11k/910-ath11k-fix-remapped-ce-accessing-issue-on-64bit-OS.patch new file mode 100644 index 0000000000..5878fa08df --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/910-ath11k-fix-remapped-ce-accessing-issue-on-64bit-OS.patch @@ -0,0 +1,153 @@ +From: Ziyang Huang +Date: Thu, 2 May 2024 00:14:31 +0800 +Subject: [PATCH] wifi: ath11k: fix remapped ce accessing issue on 64bit OS + +On 64bit OS, when ab->mem_ce is lower than or 4G far away from ab->mem, +u32 is not enough to store the offsets, which makes ath11k_ahb_read32() +and ath11k_ahb_write32() access incorrect address and causes Data Abort +Exception. + +Let's use the high bits of offsets to decide where to access, which is +similar as ath11k_pci_get_window_start() done. In the future, we can merge +these functions for unified regs accessing. + +Signed-off-by: Ziyang Huang +--- + +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -198,12 +198,18 @@ static const struct ath11k_pci_ops ath11 + + static inline u32 ath11k_ahb_read32(struct ath11k_base *ab, u32 offset) + { +- return ioread32(ab->mem + offset); ++ if ((offset & ATH11K_REG_TYPE_MASK) == ATH11K_REG_TYPE_CE) ++ return ioread32(ab->mem_ce + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset)); ++ else ++ return ioread32(ab->mem + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset)); + } + + static inline void ath11k_ahb_write32(struct ath11k_base *ab, u32 offset, u32 value) + { +- iowrite32(value, ab->mem + offset); ++ if ((offset & ATH11K_REG_TYPE_MASK) == ATH11K_REG_TYPE_CE) ++ iowrite32(value, ab->mem_ce + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset)); ++ else ++ iowrite32(value, ab->mem + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset)); + } + + static void ath11k_ahb_kill_tasklets(struct ath11k_base *ab) +@@ -275,9 +281,9 @@ static void ath11k_ahb_ce_irq_enable(str + const struct ce_ie_addr *ce_ie_addr = ab->hw_params.ce_ie_addr; + u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; + +- ie1_reg_addr = ce_ie_addr->ie1_reg_addr + ATH11K_CE_OFFSET(ab); +- ie2_reg_addr = ce_ie_addr->ie2_reg_addr + ATH11K_CE_OFFSET(ab); +- ie3_reg_addr = ce_ie_addr->ie3_reg_addr + ATH11K_CE_OFFSET(ab); ++ ie1_reg_addr = ce_ie_addr->ie1_reg_addr; ++ ie2_reg_addr = ce_ie_addr->ie2_reg_addr; ++ ie3_reg_addr = ce_ie_addr->ie3_reg_addr; + + ce_attr = &ab->hw_params.host_ce_config[ce_id]; + if (ce_attr->src_nentries) +@@ -296,9 +302,9 @@ static void ath11k_ahb_ce_irq_disable(st + const struct ce_ie_addr *ce_ie_addr = ab->hw_params.ce_ie_addr; + u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; + +- ie1_reg_addr = ce_ie_addr->ie1_reg_addr + ATH11K_CE_OFFSET(ab); +- ie2_reg_addr = ce_ie_addr->ie2_reg_addr + ATH11K_CE_OFFSET(ab); +- ie3_reg_addr = ce_ie_addr->ie3_reg_addr + ATH11K_CE_OFFSET(ab); ++ ie1_reg_addr = ce_ie_addr->ie1_reg_addr; ++ ie2_reg_addr = ce_ie_addr->ie2_reg_addr; ++ ie3_reg_addr = ce_ie_addr->ie3_reg_addr; + + ce_attr = &ab->hw_params.host_ce_config[ce_id]; + if (ce_attr->src_nentries) +--- a/drivers/net/wireless/ath/ath11k/hal.c ++++ b/drivers/net/wireless/ath/ath11k/hal.c +@@ -1247,20 +1247,16 @@ static int ath11k_hal_srng_create_config + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP; + + s = &hal->srng_config[HAL_CE_SRC]; +- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB + +- ATH11K_CE_OFFSET(ab); +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP + +- ATH11K_CE_OFFSET(ab); ++ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB; ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + + s = &hal->srng_config[HAL_CE_DST]; +- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB + +- ATH11K_CE_OFFSET(ab); +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP + +- ATH11K_CE_OFFSET(ab); ++ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB; ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - +@@ -1268,9 +1264,8 @@ static int ath11k_hal_srng_create_config + + s = &hal->srng_config[HAL_CE_DST_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + +- HAL_CE_DST_STATUS_RING_BASE_LSB + ATH11K_CE_OFFSET(ab); +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP + +- ATH11K_CE_OFFSET(ab); ++ HAL_CE_DST_STATUS_RING_BASE_LSB; ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -2268,9 +2268,9 @@ const struct ce_ie_addr ath11k_ce_ie_add + }; + + const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018 = { +- .ie1_reg_addr = CE_HOST_IPQ5018_IE_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .ie2_reg_addr = CE_HOST_IPQ5018_IE_2_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .ie3_reg_addr = CE_HOST_IPQ5018_IE_3_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .ie1_reg_addr = ATH11K_REG_TYPE_CE + CE_HOST_IPQ5018_IE_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .ie2_reg_addr = ATH11K_REG_TYPE_CE + CE_HOST_IPQ5018_IE_2_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .ie3_reg_addr = ATH11K_REG_TYPE_CE + CE_HOST_IPQ5018_IE_3_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, + }; + + const struct ce_remap ath11k_ce_remap_ipq5018 = { +@@ -2801,13 +2801,13 @@ const struct ath11k_hw_regs ipq5018_regs + .hal_reo_status_hp = 0x00003070, + + /* WCSS relative address */ +- .hal_seq_wcss_umac_ce0_src_reg = 0x08400000 ++ .hal_seq_wcss_umac_ce0_src_reg = ATH11K_REG_TYPE_CE + 0x08400000 + - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .hal_seq_wcss_umac_ce0_dst_reg = 0x08401000 ++ .hal_seq_wcss_umac_ce0_dst_reg = ATH11K_REG_TYPE_CE + 0x08401000 + - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .hal_seq_wcss_umac_ce1_src_reg = 0x08402000 ++ .hal_seq_wcss_umac_ce1_src_reg = ATH11K_REG_TYPE_CE + 0x08402000 + - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .hal_seq_wcss_umac_ce1_dst_reg = 0x08403000 ++ .hal_seq_wcss_umac_ce1_dst_reg = ATH11K_REG_TYPE_CE + 0x08403000 + - HAL_IPQ5018_CE_WFSS_REG_BASE, + + /* WBM Idle address */ +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -81,7 +81,12 @@ + #define ATH11K_M3_FILE "m3.bin" + #define ATH11K_REGDB_FILE_NAME "regdb.bin" + +-#define ATH11K_CE_OFFSET(ab) (ab->mem_ce - ab->mem) ++#define ATH11K_REG_TYPE_MASK GENMASK(31, 28) ++#define ATH11K_REG_TYPE(x) FIELD_PREP_CONST(ATH11K_REG_TYPE_MASK, x) ++#define ATH11K_REG_TYPE_NORMAL ATH11K_REG_TYPE(0) ++#define ATH11K_REG_TYPE_DP ATH11K_REG_TYPE(1) ++#define ATH11K_REG_TYPE_CE ATH11K_REG_TYPE(2) ++#define ATH11K_REG_OFFSET_MASK GENMASK(27, 0) + + enum ath11k_hw_rate_cck { + ATH11K_HW_RATE_CCK_LP_11M = 0, From 432f2f83decb826df7e20324919a32bd4491f862 Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 7 Oct 2024 15:31:17 +0400 Subject: [PATCH 13/17] mac80211: ath11k: Support setting bdf-addr and caldb-addr via DT IPQ5018 uses different BDF and caldb addresses for vairous boards, so let's support reading these addresses from the device tree. Signed-off-by: Ziyang Huang Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- ...ort-setting-bdf-addr-and-caldb-addr-.patch | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 package/kernel/mac80211/patches/ath11k/201-wifi-ath11k-Support-setting-bdf-addr-and-caldb-addr-.patch diff --git a/package/kernel/mac80211/patches/ath11k/201-wifi-ath11k-Support-setting-bdf-addr-and-caldb-addr-.patch b/package/kernel/mac80211/patches/ath11k/201-wifi-ath11k-Support-setting-bdf-addr-and-caldb-addr-.patch new file mode 100644 index 0000000000..109daf02f2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/201-wifi-ath11k-Support-setting-bdf-addr-and-caldb-addr-.patch @@ -0,0 +1,62 @@ +From 824dde8652815aa67b4e2bf2d8a9455a8ef82b8f Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Thu, 29 Jun 2023 06:12:45 +0000 +Subject: [PATCH] wifi: ath11k: Support setting bdf-addr and caldb-addr via DT + +Signed-off-by: Ziyang Huang +--- + drivers/net/wireless/ath/ath11k/qmi.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2028,6 +2028,7 @@ static int ath11k_qmi_assign_target_mem_ + struct device_node *hremote_node = NULL; + struct resource res; + u32 host_ddr_sz; ++ u32 addr; + int i, idx, ret; + + for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { +@@ -2067,7 +2068,9 @@ static int ath11k_qmi_assign_target_mem_ + idx++; + break; + case BDF_MEM_REGION_TYPE: +- ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr; ++ if(of_property_read_u32(dev->of_node, "qcom,bdf-addr", &addr)) ++ addr = ab->hw_params.bdf_addr; ++ ab->qmi.target_mem[idx].paddr = addr; + ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; +@@ -2089,8 +2092,9 @@ static int ath11k_qmi_assign_target_mem_ + if (!ab->qmi.target_mem[idx].iaddr) + return -EIO; + } else { +- ab->qmi.target_mem[idx].paddr = +- ATH11K_QMI_CALDB_ADDRESS; ++ if(of_property_read_u32(dev->of_node, "qcom,caldb-addr", &addr)) ++ addr = ATH11K_QMI_CALDB_ADDRESS; ++ ab->qmi.target_mem[idx].paddr = addr; + } + } else { + ab->qmi.target_mem[idx].paddr = 0; +@@ -2292,6 +2296,7 @@ static int ath11k_qmi_load_file_target_m + struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; + struct qmi_txn txn; + const u8 *temp = data; ++ u32 addr; + void __iomem *bdf_addr = NULL; + int ret = 0; + u32 remaining = len; +@@ -2303,7 +2308,9 @@ static int ath11k_qmi_load_file_target_m + memset(&resp, 0, sizeof(resp)); + + if (ab->hw_params.fixed_bdf_addr) { +- bdf_addr = ioremap(ab->hw_params.bdf_addr, ab->hw_params.fw.board_size); ++ if(of_property_read_u32(ab->dev->of_node, "qcom,bdf-addr", &addr)) ++ addr = ab->hw_params.bdf_addr; ++ bdf_addr = ioremap(addr, ab->hw_params.fw.board_size); + if (!bdf_addr) { + ath11k_warn(ab, "qmi ioremap error for bdf_addr\n"); + ret = -EIO; From 552056622dc579b1aa0520cef9c0c9ec6c443c4e Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 7 Oct 2024 15:39:04 +0400 Subject: [PATCH 14/17] ath11k-firmware: add wifi firmware for QCN6122 Add ability to download and package ath11k firmware for QCN6122. QCN6122 is specific/exclusive to the IPQ5018 platform and firmware files are publishes in a subdirectory of the IPQ5018 firmware files. While at it, add support for packaging BDFs for QCN6122 wifi and update iwinfo to recognize QCN6122 wifi. Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- package/firmware/ath11k-firmware/Makefile | 14 ++++++++++++++ package/firmware/ipq-wifi/Makefile | 4 +++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/package/firmware/ath11k-firmware/Makefile b/package/firmware/ath11k-firmware/Makefile index 85387289c1..a333d1b154 100644 --- a/package/firmware/ath11k-firmware/Makefile +++ b/package/firmware/ath11k-firmware/Makefile @@ -62,6 +62,12 @@ $(Package/ath11k-firmware-default) TITLE:=IPQ8074 ath11k firmware endef +define Package/ath11k-firmware-qcn6122 +$(Package/ath11k-firmware-default) + TITLE:=QCN6122 ath11k firmware + DEPENDS:=ath11k-firmware-ipq5018 +endef + define Package/ath11k-firmware-qcn9074 $(Package/ath11k-firmware-default) TITLE:=QCN9074 ath11k firmware @@ -107,6 +113,13 @@ define Package/ath11k-firmware-ipq8074/install $(1)/lib/firmware/IPQ8074/ endef +define Package/ath11k-firmware-qcn6122/install + $(INSTALL_DIR) $(1)/lib/firmware/ath11k/QCN6122/hw1.0 + $(INSTALL_DATA) \ + $(BUILD_DIR)/$(ATH11K_LEGACY_FW_SUBDIR)/ath11k-firmware/IPQ5018_QCN6122_QCN6122/hw1.0/2.7.0.1/WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1/qcn6122/* \ + $(1)/lib/firmware/ath11k/QCN6122/hw1.0/ +endef + define Package/ath11k-firmware-qcn9074/install $(INSTALL_DIR) $(1)/lib/firmware/ath11k/QCN9074/hw1.0 $(INSTALL_DATA) \ @@ -119,4 +132,5 @@ endef $(eval $(call BuildPackage,ath11k-firmware-ipq5018)) $(eval $(call BuildPackage,ath11k-firmware-ipq6018)) $(eval $(call BuildPackage,ath11k-firmware-ipq8074)) +$(eval $(call BuildPackage,ath11k-firmware-qcn6122)) $(eval $(call BuildPackage,ath11k-firmware-qcn9074)) diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile index aee0b07680..ef67490a5f 100644 --- a/package/firmware/ipq-wifi/Makefile +++ b/package/firmware/ipq-wifi/Makefile @@ -108,10 +108,12 @@ define ipq-wifi-install-one $(call ipq-wifi-install-ath11-one-to,$(1),$(2),IPQ6018/hw1.0),\ $(if $(filter $(suffix $(1)),.IPQ8074 .ipq8074),\ $(call ipq-wifi-install-ath11-one-to,$(1),$(2),IPQ8074/hw2.0),\ + $(if $(filter $(suffix $(1)),.QCN6122 .qcn6122),\ + $(call ipq-wifi-install-ath11-one-to,$(1),$(2),QCN6122/hw1.0),\ $(if $(filter $(suffix $(1)),.QCN9074 .qcn9074),\ $(call ipq-wifi-install-ath11-one-to,$(1),$(2),QCN9074/hw1.0),\ $(error Unrecognized board-file suffix '$(suffix $(1))' for '$(1)')\ - ))))))))) + )))))))))) endef # Blank line required at end of above define due to foreach context From f76b846eea288be452be070413353185e7abe822 Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 7 Oct 2024 15:53:48 +0400 Subject: [PATCH 15/17] mac80211: ath11k: add support for QCN6122 wifi Add QCN6122 platform support. QCN6122 is a PCIe based solution that is attached to and enumerated by the WPSS (Wireless Processor SubSystem) Q6 processor. Though it is a PCIe device, since it is not attached to APSS processor (Application Processor SubSystem), APSS will be unaware of such a decice and hence it is registered to the APSS processor as a platform device(AHB). Because of this hybrid nature, it is called as a hybrid bus device. As such, QCN6122 is a hybrid bus type device and follows the same codepath as for WCN6750. This is a reversed engineered and heavily simplified version of below downstream patch: https://git.codelinaro.org/clo/qsdk/oss/system/feeds/wlan-open/-/ \ blob/NHSS.QSDK.12.4.5.r2/mac80211/patches/232-ath11k-qcn6122-support.patch Co-developed-by: George Moussalem Signed-off-by: Sowmiya Sree Elavalagan Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- ...ifi-ath11k-add-hw-params-for-QCN6122.patch | 115 ++++++++++++++++++ ...wifi-ath11k-add-hal-regs-for-QCN6122.patch | 114 +++++++++++++++++ ...-ath11k-add-hw-ring-mask-for-QCN6122.patch | 74 +++++++++++ ...k-update-hif_and-pci_ops-for-QCN6122.patch | 102 ++++++++++++++++ ...h11k-add-multipd-support-for-QCN6122.patch | 110 +++++++++++++++++ ...fi-ath11k-add-QCN6122-device-support.patch | 55 +++++++++ ...11k-Support-to-assign-m3-dump-memory.patch | 31 +++++ ...less-ath11k-add-bindings-for-QCN6122.patch | 62 ++++++++++ 8 files changed, 663 insertions(+) create mode 100644 package/kernel/mac80211/patches/ath11k/920-wifi-ath11k-add-hw-params-for-QCN6122.patch create mode 100644 package/kernel/mac80211/patches/ath11k/921-wifi-ath11k-add-hal-regs-for-QCN6122.patch create mode 100644 package/kernel/mac80211/patches/ath11k/922-wifi-ath11k-add-hw-ring-mask-for-QCN6122.patch create mode 100644 package/kernel/mac80211/patches/ath11k/923-wifi-ath11k-update-hif_and-pci_ops-for-QCN6122.patch create mode 100644 package/kernel/mac80211/patches/ath11k/924-wifi-ath11k-add-multipd-support-for-QCN6122.patch create mode 100644 package/kernel/mac80211/patches/ath11k/925-wifi-ath11k-add-QCN6122-device-support.patch create mode 100644 package/kernel/mac80211/patches/ath11k/931-wifi-ath11k-Support-to-assign-m3-dump-memory.patch create mode 100644 target/linux/qualcommax/patches-6.6/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch diff --git a/package/kernel/mac80211/patches/ath11k/920-wifi-ath11k-add-hw-params-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/920-wifi-ath11k-add-hw-params-for-QCN6122.patch new file mode 100644 index 0000000000..6d5aa7e2c1 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/920-wifi-ath11k-add-hw-params-for-QCN6122.patch @@ -0,0 +1,115 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add hw params for QCN6122 + +Add QCN6122 platform support. + +QCN6122 is a PCIe based solution that is attached to and enumerated +by the WPSS (Wireless Processor SubSystem) Q6 processor. + +Though it is a PCIe device, since it is not attached to APSS processor +(Application Processor SubSystem), APSS will be unaware of such a decice +and hence it is registered to the APSS processor as a platform device(AHB). +Because of this hybrid nature, it is called as a hybrid bus device. + +As such, QCN6122 is a hybrid bus type device and follows the same codepath +as for WCN6750. + +This is a heavily simplified version of below downstream patch: +Download from https://git.codelinaro.org/clo/qsdk/oss/system/feeds/wlan-open/-/blob/NHSS.QSDK.12.4.5.r2/mac80211/patches/232-ath11k-qcn6122-support.patch + +Co-developed-by: George Moussalem +Signed-off-by: Sowmiya Sree Elavalagan +Signed-off-by: George Moussalem +--- + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -809,6 +809,67 @@ static struct ath11k_hw_params ath11k_hw + .support_fw_mac_sequence = true, + .support_dual_stations = true, + }, ++ { ++ .hw_rev = ATH11K_HW_QCN6122_HW10, ++ .name = "qcn6122 hw1.0", ++ .fw = { ++ .dir = "QCN6122/hw1.0", ++ .board_size = 256 * 1024, ++ .cal_offset = 128 * 1024, ++ }, ++ .hal_params = &ath11k_hw_hal_params_ipq8074, ++ .max_radios = MAX_RADIOS_5018, ++ .bdf_addr = 0x4D200000, ++ .hw_ops = &ipq5018_ops, ++ .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), ++ .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122, ++ .interface_modes = BIT(NL80211_IFTYPE_STATION) | ++ BIT(NL80211_IFTYPE_AP) | ++ BIT(NL80211_IFTYPE_MESH_POINT), ++ .spectral = { ++ .fft_sz = 2, ++ .fft_pad_sz = 0, ++ .summary_pad_sz = 16, ++ .fft_hdr_len = 24, ++ .max_fft_bins = 1024, ++ }, ++ .credit_flow = false, ++ .max_tx_ring = 1, ++ .supports_monitor = true, ++ .supports_shadow_regs = false, ++ .idle_ps = false, ++ .supports_suspend = false, ++ .host_ce_config = ath11k_host_ce_config_qcn9074, ++ .ce_count = CE_CNT_5018, ++ .target_ce_config = ath11k_target_ce_config_wlan_ipq5018, ++ .target_ce_count = TARGET_CE_CNT_5018, ++ .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq5018, ++ .svc_to_ce_map_len = SVC_CE_MAP_LEN_5018, ++ .single_pdev_only = false, ++ .rxdma1_enable = true, ++ .num_rxdma_per_pdev = RXDMA_PER_PDEV_5018, ++ .rx_mac_buf_ring = false, ++ .vdev_start_delay = false, ++ .htt_peer_map_v2 = true, ++ .coldboot_cal_mm = true, ++ .coldboot_cal_ftm = true, ++ .cbcal_restart_fw = true, ++ .fix_l1ss = true, ++ .alloc_cacheable_memory = true, ++ .m3_fw_support = false, ++ .fixed_bdf_addr = true, ++ .fixed_mem_region = true, ++ .static_window_map = true, ++ .hybrid_bus_type = true, ++ .fw_mem_mode = 1, ++ .supports_sta_ps = false, ++ .dbr_debug_support = true, ++ .bios_sar_capa = NULL, ++ .fixed_fw_mem = false, ++ .support_off_channel_tx = false, ++ .tcl_ring_retry = true, ++ .tx_ring_size = DP_TCL_DATA_RING_SIZE, ++ }, + }; + + static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab) +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -148,6 +148,7 @@ enum ath11k_hw_rev { + ATH11K_HW_WCN6750_HW10, + ATH11K_HW_IPQ5018_HW10, + ATH11K_HW_QCA2066_HW21, ++ ATH11K_HW_QCN6122_HW10, + }; + + enum ath11k_firmware_mode { +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -22,6 +22,7 @@ + #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074 0x02 + #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9074 0x07 + #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750 0x03 ++#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122 0x40 + #define ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32 + #define ATH11K_QMI_RESP_LEN_MAX 8192 + #define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52 diff --git a/package/kernel/mac80211/patches/ath11k/921-wifi-ath11k-add-hal-regs-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/921-wifi-ath11k-add-hal-regs-for-QCN6122.patch new file mode 100644 index 0000000000..eae73170b3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/921-wifi-ath11k-add-hal-regs-for-QCN6122.patch @@ -0,0 +1,114 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add hal regs for QCN6122 + +Add HAL changes required to support QCN6122. Offsets are similar to those of +WCN6750 but QCN6122 does not use the hal_shadow_base_addr, so add platform +specific ath11k_hw_regs and register them in hw params. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -822,6 +822,7 @@ static struct ath11k_hw_params ath11k_hw + .bdf_addr = 0x4D200000, + .hw_ops = &ipq5018_ops, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), ++ .regs = &qcn6122_regs, + .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122, + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -2822,6 +2822,81 @@ const struct ath11k_hw_regs ipq5018_regs + .hal_wbm1_release_ring_base_lsb = 0x0000097c, + }; + ++const struct ath11k_hw_regs qcn6122_regs = { ++ /* SW2TCL(x) R0 ring configuration address */ ++ .hal_tcl1_ring_base_lsb = 0x00000694, ++ .hal_tcl1_ring_base_msb = 0x00000698, ++ .hal_tcl1_ring_id = 0x0000069c, ++ .hal_tcl1_ring_misc = 0x000006a4, ++ .hal_tcl1_ring_tp_addr_lsb = 0x000006b0, ++ .hal_tcl1_ring_tp_addr_msb = 0x000006b4, ++ .hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006c4, ++ .hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006c8, ++ .hal_tcl1_ring_msi1_base_lsb = 0x000006dc, ++ .hal_tcl1_ring_msi1_base_msb = 0x000006e0, ++ .hal_tcl1_ring_msi1_data = 0x000006e4, ++ .hal_tcl2_ring_base_lsb = 0x000006ec, ++ .hal_tcl_ring_base_lsb = 0x0000079c, ++ ++ /* TCL STATUS ring address */ ++ .hal_tcl_status_ring_base_lsb = 0x000008a4, ++ ++ /* REO2SW(x) R0 ring configuration address */ ++ .hal_reo1_ring_base_lsb = 0x000001ec, ++ .hal_reo1_ring_base_msb = 0x000001f0, ++ .hal_reo1_ring_id = 0x000001f4, ++ .hal_reo1_ring_misc = 0x000001fc, ++ .hal_reo1_ring_hp_addr_lsb = 0x00000200, ++ .hal_reo1_ring_hp_addr_msb = 0x00000204, ++ .hal_reo1_ring_producer_int_setup = 0x00000210, ++ .hal_reo1_ring_msi1_base_lsb = 0x00000234, ++ .hal_reo1_ring_msi1_base_msb = 0x00000238, ++ .hal_reo1_ring_msi1_data = 0x0000023c, ++ .hal_reo2_ring_base_lsb = 0x00000244, ++ .hal_reo1_aging_thresh_ix_0 = 0x00000564, ++ .hal_reo1_aging_thresh_ix_1 = 0x00000568, ++ .hal_reo1_aging_thresh_ix_2 = 0x0000056c, ++ .hal_reo1_aging_thresh_ix_3 = 0x00000570, ++ ++ /* REO2SW(x) R2 ring pointers (head/tail) address */ ++ .hal_reo1_ring_hp = 0x00003028, ++ .hal_reo1_ring_tp = 0x0000302c, ++ .hal_reo2_ring_hp = 0x00003030, ++ ++ /* REO2TCL R0 ring configuration address */ ++ .hal_reo_tcl_ring_base_lsb = 0x000003fc, ++ .hal_reo_tcl_ring_hp = 0x00003058, ++ ++ /* SW2REO ring address */ ++ .hal_sw2reo_ring_base_lsb = 0x0000013c, ++ .hal_sw2reo_ring_hp = 0x00003018, ++ ++ /* REO CMD ring address */ ++ .hal_reo_cmd_ring_base_lsb = 0x000000e4, ++ .hal_reo_cmd_ring_hp = 0x00003010, ++ ++ /* REO status address */ ++ .hal_reo_status_ring_base_lsb = 0x00000504, ++ .hal_reo_status_hp = 0x00003070, ++ ++ /* WCSS relative address */ ++ .hal_seq_wcss_umac_ce0_src_reg = 0x01b80000, ++ .hal_seq_wcss_umac_ce0_dst_reg = 0x01b81000, ++ .hal_seq_wcss_umac_ce1_src_reg = 0x01b82000, ++ .hal_seq_wcss_umac_ce1_dst_reg = 0x01b83000, ++ ++ /* WBM Idle address */ ++ .hal_wbm_idle_link_ring_base_lsb = 0x00000874, ++ .hal_wbm_idle_link_ring_misc = 0x00000884, ++ ++ /* SW2WBM release address */ ++ .hal_wbm_release_ring_base_lsb = 0x000001ec, ++ ++ /* WBM2SW release address */ ++ .hal_wbm0_release_ring_base_lsb = 0x00000924, ++ .hal_wbm1_release_ring_base_lsb = 0x0000097c, ++}; ++ + const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = { + .rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM, + .tcl2wbm_rbm_map = ath11k_hw_tcl2wbm_rbm_map_ipq8074, +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -425,6 +425,7 @@ extern const struct ath11k_hw_regs qcn90 + extern const struct ath11k_hw_regs wcn6855_regs; + extern const struct ath11k_hw_regs wcn6750_regs; + extern const struct ath11k_hw_regs ipq5018_regs; ++extern const struct ath11k_hw_regs qcn6122_regs; + + static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type) + { diff --git a/package/kernel/mac80211/patches/ath11k/922-wifi-ath11k-add-hw-ring-mask-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/922-wifi-ath11k-add-hw-ring-mask-for-QCN6122.patch new file mode 100644 index 0000000000..f93a3bc60a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/922-wifi-ath11k-add-hw-ring-mask-for-QCN6122.patch @@ -0,0 +1,74 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add hw ring mask for QCN6122 + +Add ring mask for QCN6122 and register them in hw params. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -821,6 +821,7 @@ static struct ath11k_hw_params ath11k_hw + .max_radios = MAX_RADIOS_5018, + .bdf_addr = 0x4D200000, + .hw_ops = &ipq5018_ops, ++ .ring_mask = &ath11k_hw_ring_mask_qcn6122, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .regs = &qcn6122_regs, + .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122, +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -2070,6 +2070,43 @@ const struct ath11k_hw_ring_mask ath11k_ + }, + }; + ++const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122 = { ++ .tx = { ++ ATH11K_TX_RING_MASK_0, ++ ATH11K_TX_RING_MASK_1, ++ ATH11K_TX_RING_MASK_2, ++ }, ++ .rx_mon_status = { ++ 0, 0, 0, ++ ATH11K_RX_MON_STATUS_RING_MASK_0, ++ }, ++ .rx = { ++ 0, 0, 0, 0, ++ ATH11K_RX_RING_MASK_0, ++ ATH11K_RX_RING_MASK_1, ++ ATH11K_RX_RING_MASK_2, ++ ATH11K_RX_RING_MASK_3, ++ }, ++ .rx_err = { ++ 0, 0, 0, 0, 0, 0, 0, 0, ++ ATH11K_RX_ERR_RING_MASK_0, ++ }, ++ .rx_wbm_rel = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ ATH11K_RX_WBM_REL_RING_MASK_0, ++ }, ++ .reo_status = { ++ 0, 0, 0, ++ ATH11K_REO_STATUS_RING_MASK_0, ++ }, ++ .rxdma2host = { ++ ATH11K_RXDMA2HOST_RING_MASK_0, ++ }, ++ .host2rxdma = { ++ ATH11K_HOST2RXDMA_RING_MASK_0, ++ }, ++}; ++ + /* Target firmware's Copy Engine configuration for IPQ5018 */ + const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq5018[] = { + /* CE0: host->target HTC control and raw streams */ +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -289,6 +289,7 @@ extern const struct ath11k_hw_ring_mask + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390; + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn9074; + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_wcn6750; ++extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122; + + extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq8074; + extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018; diff --git a/package/kernel/mac80211/patches/ath11k/923-wifi-ath11k-update-hif_and-pci_ops-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/923-wifi-ath11k-update-hif_and-pci_ops-for-QCN6122.patch new file mode 100644 index 0000000000..9c9caef4f2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/923-wifi-ath11k-update-hif_and-pci_ops-for-QCN6122.patch @@ -0,0 +1,102 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: update hif and pci ops for QCN6122 + +Add HIF and PCI ops for QCN6122. QCN6122 by default uses DP window 3. +However, this is configurable, so let's introduce a function to do that and +follow the existing register access code for (hybrid)AHB devices and use +DP window 1. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -768,6 +768,18 @@ static int ath11k_ahb_hif_resume(struct + return 0; + } + ++static void ath11k_ahb_config_static_window_qcn6122(struct ath11k_base *ab) ++{ ++ u32 umac_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET); ++ u32 ce_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE); ++ u32 window; ++ ++ window = (umac_window) | (ce_window << 6); ++ ++ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, ++ ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); ++} ++ + static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = { + .start = ath11k_ahb_start, + .stop = ath11k_ahb_stop, +@@ -800,6 +812,24 @@ static const struct ath11k_hif_ops ath11 + .ce_irq_disable = ath11k_pci_disable_ce_irqs_except_wake_irq, + }; + ++static const struct ath11k_hif_ops ath11k_ahb_hif_ops_qcn6122 = { ++ .start = ath11k_pcic_start, ++ .stop = ath11k_pcic_stop, ++ .read32 = ath11k_pcic_read32, ++ .write32 = ath11k_pcic_write32, ++ .read = NULL, ++ .irq_enable = ath11k_pcic_ext_irq_enable, ++ .irq_disable = ath11k_pcic_ext_irq_disable, ++ .get_msi_address = ath11k_pcic_get_msi_address, ++ .get_user_msi_vector = ath11k_pcic_get_user_msi_assignment, ++ .map_service_to_pipe = ath11k_pcic_map_service_to_pipe, ++ .power_down = ath11k_ahb_power_down, ++ .power_up = ath11k_ahb_power_up, ++ .ce_irq_enable = ath11k_pci_enable_ce_irqs_except_wake_irq, ++ .ce_irq_disable = ath11k_pci_disable_ce_irqs_except_wake_irq, ++ .config_static_window = ath11k_ahb_config_static_window_qcn6122, ++}; ++ + static int ath11k_core_get_rproc(struct ath11k_base *ab) + { + struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); +@@ -1144,6 +1174,10 @@ static int ath11k_ahb_probe(struct platf + hif_ops = &ath11k_ahb_hif_ops_wcn6750; + pci_ops = &ath11k_ahb_pci_ops_wcn6750; + break; ++ case ATH11K_HW_QCN6122_HW10: ++ hif_ops = &ath11k_ahb_hif_ops_qcn6122; ++ pci_ops = &ath11k_ahb_pci_ops_wcn6750; ++ break; + default: + dev_err(&pdev->dev, "unsupported device type %d\n", hw_rev); + return -EOPNOTSUPP; +--- a/drivers/net/wireless/ath/ath11k/hif.h ++++ b/drivers/net/wireless/ath/ath11k/hif.h +@@ -31,6 +31,7 @@ struct ath11k_hif_ops { + void (*ce_irq_enable)(struct ath11k_base *ab); + void (*ce_irq_disable)(struct ath11k_base *ab); + void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); ++ void (*config_static_window)(struct ath11k_base *ab); + }; + + static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab) +@@ -146,4 +147,12 @@ static inline void ath11k_get_ce_msi_idx + *msi_data_idx = ce_id; + } + ++static inline void ath11k_hif_config_static_window(struct ath11k_base *ab) ++{ ++ if (!ab->hw_params.static_window_map || !ab->hif.ops->config_static_window) ++ return; ++ ++ ab->hif.ops->config_static_window(ab); ++} ++ + #endif /* _HIF_H_ */ +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2184,6 +2184,8 @@ static int ath11k_qmi_request_device_inf + ab->mem = bar_addr_va; + ab->mem_len = resp.bar_size; + ++ ath11k_hif_config_static_window(ab); ++ + return 0; + out: + return ret; diff --git a/package/kernel/mac80211/patches/ath11k/924-wifi-ath11k-add-multipd-support-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/924-wifi-ath11k-add-multipd-support-for-QCN6122.patch new file mode 100644 index 0000000000..e6170a74e4 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/924-wifi-ath11k-add-multipd-support-for-QCN6122.patch @@ -0,0 +1,110 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add multipd support for QCN6122 + +IPQ5018/QCN6122 platforms use multi PD (protection domains) to avoid having +one instance of the running Q6 firmware crashing resulting in crashing the +others. See below patch for more info: +https://lore.kernel.org/all/20231110091939.3025413-1-quic_mmanikan@quicinc.com/ + +The IPQ5018 platform can have multiple (2) QCN6122 wifi cards. To differentiate +the two, the PD instance number (1 or 2) is added to the QMI service instance +ID, which the QCN6122 firmware also expects. IPQ5018 is always the first PD, so +the QCN6122 cards should be the second or third. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -435,6 +435,7 @@ static void ath11k_ahb_init_qmi_ce_confi + cfg->svc_to_ce_map_len = ab->hw_params.svc_to_ce_map_len; + cfg->svc_to_ce_map = ab->hw_params.svc_to_ce_map; + ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id; ++ ab->qmi.service_ins_id += ab->userpd_id; + } + + static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) +@@ -1118,6 +1119,27 @@ err_unregister: + return ret; + } + ++static int ath11k_get_userpd_id(struct device *dev) ++{ ++ int ret; ++ int userpd_id = 0; ++ const char *subsys_name; ++ ++ ret = of_property_read_string(dev->of_node, ++ "qcom,userpd-subsys-name", ++ &subsys_name); ++ if (ret) ++ return 0; ++ ++ if (strcmp(subsys_name, "q6v5_wcss_userpd2") == 0) ++ userpd_id = ATH11K_QCN6122_USERPD_2; ++ else if (strcmp(subsys_name, "q6v5_wcss_userpd3") == 0) ++ userpd_id = ATH11K_QCN6122_USERPD_3; ++ dev_info(dev, "Multipd architecture - userpd: %d\n", userpd_id + 1); ++ ++ return userpd_id; ++} ++ + static int ath11k_ahb_fw_resource_deinit(struct ath11k_base *ab) + { + struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); +@@ -1159,7 +1181,7 @@ static int ath11k_ahb_probe(struct platf + const struct ath11k_hif_ops *hif_ops; + const struct ath11k_pci_ops *pci_ops; + enum ath11k_hw_rev hw_rev; +- int ret; ++ int ret, userpd_id; + + hw_rev = (uintptr_t)device_get_match_data(&pdev->dev); + +@@ -1183,6 +1205,7 @@ static int ath11k_ahb_probe(struct platf + return -EOPNOTSUPP; + } + ++ userpd_id = ath11k_get_userpd_id(&pdev->dev); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "failed to set 32-bit consistent dma\n"); +@@ -1199,6 +1222,7 @@ static int ath11k_ahb_probe(struct platf + ab->hif.ops = hif_ops; + ab->pdev = pdev; + ab->hw_rev = hw_rev; ++ ab->userpd_id = userpd_id; + ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL; + platform_set_drvdata(pdev, ab); + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -45,6 +45,9 @@ + #define ATH11K_INVALID_HW_MAC_ID 0xFF + #define ATH11K_CONNECTION_LOSS_HZ (3 * HZ) + ++#define ATH11K_QCN6122_USERPD_2 1 ++#define ATH11K_QCN6122_USERPD_3 2 ++ + /* SMBIOS type containing Board Data File Name Extension */ + #define ATH11K_SMBIOS_BDF_EXT_TYPE 0xF8 + +@@ -945,6 +948,7 @@ struct ath11k_base { + struct list_head peers; + wait_queue_head_t peer_mapping_wq; + u8 mac_addr[ETH_ALEN]; ++ int userpd_id; + int irq_num[ATH11K_IRQ_NUM_MAX]; + struct ath11k_ext_irq_grp ext_irq_grp[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + struct ath11k_targ_cap target_caps; +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -389,6 +389,8 @@ static void ath11k_pci_init_qmi_ce_confi + } else + ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id; + ++ ab->qmi.service_ins_id += ab->userpd_id; ++ + ath11k_ce_get_shadow_config(ab, &cfg->shadow_reg_v2, + &cfg->shadow_reg_v2_len); + } diff --git a/package/kernel/mac80211/patches/ath11k/925-wifi-ath11k-add-QCN6122-device-support.patch b/package/kernel/mac80211/patches/ath11k/925-wifi-ath11k-add-QCN6122-device-support.patch new file mode 100644 index 0000000000..58627ce07e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/925-wifi-ath11k-add-QCN6122-device-support.patch @@ -0,0 +1,55 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add QCN6122 device support + +QCN6122 is a 2x2 11AX PCIe based chipset, but it is attached to the WPSS +(Wireless Processor SubSystem) Q6 processor, hence it is enumerated +by the Q6 processor. It is registered to the APSS processor +(Application Processor SubSystem) as a platform device (AHB) and remoteproc +APIs are used to boot up or shutdown the device like other AHB devices. + +Also, device information like BAR and its size is not known to the +APSS processor as the chip is enumerated by WPSS Q6. These details +are fetched over QMI. + +STA, AP, and MESH modes are supported. +Tested on: Linksys MX2000 and GLiNET B3000 access points for prolonged duration +tests spanning multiple days with multiple clients connected with firmware +WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 + +An important point to note is that though QCN6122 is a PCIe device, +it is an IPQ5018 SoC specific solution and cannot be attached to any other +platform. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -37,6 +37,9 @@ static const struct of_device_id ath11k_ + { .compatible = "qcom,ipq5018-wifi", + .data = (void *)ATH11K_HW_IPQ5018_HW10, + }, ++ { .compatible = "qcom,qcn6122-wifi", ++ .data = (void *)ATH11K_HW_QCN6122_HW10, ++ }, + { } + }; + +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -126,6 +126,15 @@ static const struct ath11k_msi_config at + }, + .hw_rev = ATH11K_HW_QCA2066_HW21, + }, ++ { ++ .total_vectors = 13, ++ .total_users = 2, ++ .users = (struct ath11k_msi_user[]) { ++ { .name = "CE", .num_vectors = 5, .base_vector = 0 }, ++ { .name = "DP", .num_vectors = 8, .base_vector = 5 }, ++ }, ++ .hw_rev = ATH11K_HW_QCN6122_HW10, ++ }, + }; + + int ath11k_pcic_init_msi_config(struct ath11k_base *ab) diff --git a/package/kernel/mac80211/patches/ath11k/931-wifi-ath11k-Support-to-assign-m3-dump-memory.patch b/package/kernel/mac80211/patches/ath11k/931-wifi-ath11k-Support-to-assign-m3-dump-memory.patch new file mode 100644 index 0000000000..834a4b5797 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/931-wifi-ath11k-Support-to-assign-m3-dump-memory.patch @@ -0,0 +1,31 @@ +From 64f6f6cdde0b6b763181145a698207fad4536c06 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Wed, 9 Aug 2023 17:44:49 +0000 +Subject: [PATCH] wifi: ath11k: Support to assign m3 dump memory + +Signed-off-by: Ziyang Huang +--- + drivers/net/wireless/ath/ath11k/qmi.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2104,6 +2104,18 @@ static int ath11k_qmi_assign_target_mem_ + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; ++ case M3_DUMP_REGION_TYPE: ++ if (of_property_read_u32(dev->of_node, "qcom,m3-dump-addr", &addr)) { ++ ath11k_warn(ab, "qmi fail to get qcom,m3-dump-addr, ignore m3 dump mem req\n"); ++ break; ++ } ++ ++ ab->qmi.target_mem[idx].paddr = (phys_addr_t) addr; ++ ab->qmi.target_mem[idx].vaddr = NULL; ++ ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ++ ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; ++ idx++; ++ break; + default: + ath11k_warn(ab, "qmi ignore invalid mem req type %d\n", + ab->qmi.target_mem[i].type); diff --git a/target/linux/qualcommax/patches-6.6/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch b/target/linux/qualcommax/patches-6.6/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch new file mode 100644 index 0000000000..d9eaf582c7 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch @@ -0,0 +1,62 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] dt: bindings: net: add bindings for QCN6122 + +QCN6122 is a PCIe based solution that is attached to and enumerated +by the WPSS (Wireless Processor SubSystem) Q6 processor. + +Though it is a PCIe device, since it is not attached to APSS processor +(Application Processor SubSystem), APSS will be unaware of such a decice +and hence it is registered to the APSS processor as a platform device(AHB). +Because of this hybrid nature, it is called as a hybrid bus device. + +As such, QCN6122 is a hybrid bus type device and follows the same codepath +as for WCN6750. + +This is a reversed engineered and heavily simplified version of below +downstream patch: +https://git.codelinaro.org/clo/qsdk/oss/system/feeds/wlan-open/-/ \ +blob/NHSS.QSDK.12.4.5.r2/mac80211/patches/232-ath11k-qcn6122-support.patch + +Signed-off-by: George Moussalem +--- +--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml ++++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml +@@ -21,6 +21,7 @@ properties: + - qcom,ipq6018-wifi + - qcom,wcn6750-wifi + - qcom,ipq5018-wifi ++ - qcom,qcn6122-wifi + + reg: + maxItems: 1 +@@ -258,6 +259,29 @@ allOf: + - description: interrupt event for ring DP20 + - description: interrupt event for ring DP21 + - description: interrupt event for ring DP22 ++ - if: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - qcom,qcn6122-wifi ++ then: ++ properties: ++ interrupts: ++ items: ++ - description: interrupt event for ring CE1 ++ - description: interrupt event for ring CE2 ++ - description: interrupt event for ring CE3 ++ - description: interrupt event for ring CE4 ++ - description: interrupt event for ring CE5 ++ - description: interrupt event for ring DP1 ++ - description: interrupt event for ring DP2 ++ - description: interrupt event for ring DP3 ++ - description: interrupt event for ring DP4 ++ - description: interrupt event for ring DP5 ++ - description: interrupt event for ring DP6 ++ - description: interrupt event for ring DP7 ++ - description: interrupt event for ring DP8 + + examples: + - | From 398f4a97378e2f645badc1aef3d0e9fd76f6665d Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 7 Oct 2024 16:09:40 +0400 Subject: [PATCH 16/17] qualcommax: ipq50xx: add support for Linksys MX2000 and MX5500 Add support for Linksys MX2000 (Atlas 6) and MX5500 (Atlas 6 Pro). These devices are completely identical except for the secondary wifi chip used for 5Ghz: QCN6102 is used on MX2000 while QCN9024 is used on MX5500 Speficiations: * SoC: Qualcomm IPQ5018 (64-bit dual-core ARM Cortex-A53 @ 1.0Ghz) * Memory: Winbond W634GU6NB-11 (512 MiB DDR3-933) * Serial Port: 3v3 TTL 115200n8 * Wi-Fi: IPQ5018 (2x2 2.4 Ghz 802.11b/g/n/ax) * Wi-Fi: MX2000: QCN6102 (2x2:2 5 Ghz 802.11an/ac/ax) MX5500: QCN9024 (4x4:4 5 Ghz 802.11an/ac/ax) * Ethernet: IPQ5018 integrated virtual switch connected to an external QCA8337 switch (4 Ports 10/100/1000 GBASE-T) * Flash: Macronix MX35UF2GE4AD (256 MiB) * LEDs: 1x multi-color PWM LED * Buttons: 1x WPS (GPIO 27 Active Low) 1x Reset (GPIO 28 Acive Low) Flash instructions (in case of MX2000, else replace with MX5500 images): 1. On OEM firmware, login to the device (typically at http://192.168.1.1) and click 'CA' in the bottom right corner -> Connectivity -> Manual Upgrade. Alternatively, browse to http:///fwupdate.html. Upgrade firmware using openwrt-qualcommax-ipq50xx-linksys_mx2000-squashfs-factory.bin image. Optionally install on second partition, after first boot check actual partition: fw_printenv -n boot_part and install firmware on second partition using command in case of 2: mtd -r -e kernel -n write openwrt-qualcommax-ipq50xx-linksys_mx2000-squashfs-factory.bin kernel and in case of 1: mtd -r -e alt_kernel -n write openwrt-qualcommax-ipq50xx-linksys_mx2000-squashfs-factory.bin alt_kernel 2. Installation using serial connection from OEM firmware (default login: root, password: admin): fw_printenv -n boot_part In case of 2: flash_erase /dev/mtd12 0 0 nandwrite -p /dev/mtd12 openwrt-qualcommax-ipq50xx-linksys_mx2000-squashfs-factory.bin or in case of 1: flash_erase /dev/mtd14 0 0 nandwrite -p /dev/mtd14 openwrt-qualcommax-ipq50xx-linksys_mx2000-squashfs-factory.bin After first boot install firmware on second partition: mtd -r -e kernel -n write openwrt-qualcommax-ipq50xx-linksys_mx2000-squashfs-factory.bin kernel or: mtd -r -e alt_kernel -n write openwrt-qualcommax-ipq50xx-linksys_mx2000-squashfs-factory.bin alt_kernel 3. Back to the OEM firmware. Download firmware from OEM website: MX2000: https://support.linksys.com/kb/article/585-en/ MX5500: https://support.linksys.com/kb/article/587-en/ From serial or SSH: fw_printenv boot_part in case of 1: mtd -r -e alt_kernel -n write FW_MX2000_1.1.7.210469_prod.img alt_kernel else in case of 2: mtd -r -e kernel -n write FW_MX2000_1.1.7.210469_prod.img kernel Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- .../uboot-envtools/files/qualcommax_ipq50xx | 22 ++ package/firmware/ipq-wifi/Makefile | 4 + .../arm64/boot/dts/qcom/ipq5018-mx-base.dtsi | 325 ++++++++++++++++++ .../arm64/boot/dts/qcom/ipq5018-mx2000.dts | 262 ++++++++++++++ .../arm64/boot/dts/qcom/ipq5018-mx5500.dts | 240 +++++++++++++ target/linux/qualcommax/image/ipq50xx.mk | 31 ++ .../ipq50xx/base-files/etc/board.d/02_network | 45 +++ .../etc/hotplug.d/firmware/11-ath11k-caldata | 47 +++ .../ipq50xx/base-files/etc/init.d/bootcount | 12 + .../base-files/lib/upgrade/platform.sh | 84 +++++ 10 files changed, 1072 insertions(+) create mode 100644 package/boot/uboot-envtools/files/qualcommax_ipq50xx create mode 100644 target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx-base.dtsi create mode 100644 target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx2000.dts create mode 100644 target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx5500.dts create mode 100644 target/linux/qualcommax/ipq50xx/base-files/etc/board.d/02_network create mode 100644 target/linux/qualcommax/ipq50xx/base-files/etc/hotplug.d/firmware/11-ath11k-caldata create mode 100755 target/linux/qualcommax/ipq50xx/base-files/etc/init.d/bootcount create mode 100644 target/linux/qualcommax/ipq50xx/base-files/lib/upgrade/platform.sh diff --git a/package/boot/uboot-envtools/files/qualcommax_ipq50xx b/package/boot/uboot-envtools/files/qualcommax_ipq50xx new file mode 100644 index 0000000000..0e78bb8060 --- /dev/null +++ b/package/boot/uboot-envtools/files/qualcommax_ipq50xx @@ -0,0 +1,22 @@ +[ -e /etc/config/ubootenv ] && exit 0 + +touch /etc/config/ubootenv + +. /lib/uboot-envtools.sh +. /lib/functions.sh + +board=$(board_name) + +case "$board" in +linksys,mx2000|\ +linksys,mx5500) + idx="$(find_mtd_index u_env)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x40000" "0x20000" + ;; +esac + +config_load ubootenv +config_foreach ubootenv_add_app_config + +exit 0 diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile index ef67490a5f..408c821035 100644 --- a/package/firmware/ipq-wifi/Makefile +++ b/package/firmware/ipq-wifi/Makefile @@ -39,8 +39,10 @@ ALLWIFIBOARDS:= \ edimax_cax1800 \ linksys_homewrk \ linksys_mr7350 \ + linksys_mx2000 \ linksys_mx4200 \ linksys_mx5300 \ + linksys_mx5500 \ linksys_mx8500 \ linksys_whw03 \ netgear_lbr20 \ @@ -175,8 +177,10 @@ $(eval $(call generate-ipq-wifi-package,edgecore_eap102,Edgecore EAP102)) $(eval $(call generate-ipq-wifi-package,edimax_cax1800,Edimax CAX1800)) $(eval $(call generate-ipq-wifi-package,linksys_homewrk,Linksys HomeWRK)) $(eval $(call generate-ipq-wifi-package,linksys_mr7350,Linksys MR7350)) +$(eval $(call generate-ipq-wifi-package,linksys_mx2000,Linksys MX2000)) $(eval $(call generate-ipq-wifi-package,linksys_mx4200,Linksys MX4200)) $(eval $(call generate-ipq-wifi-package,linksys_mx5300,Linksys MX5300)) +$(eval $(call generate-ipq-wifi-package,linksys_mx5500,Linksys MX5500)) $(eval $(call generate-ipq-wifi-package,linksys_mx8500,Linksys MX8500)) $(eval $(call generate-ipq-wifi-package,linksys_whw03,Linksys WHW03)) $(eval $(call generate-ipq-wifi-package,netgear_lbr20,Netgear LBR20)) diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx-base.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx-base.dtsi new file mode 100644 index 0000000000..57fe5c53a9 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx-base.dtsi @@ -0,0 +1,325 @@ +#include "ipq5018.dtsi" +#include "ipq5018-ess.dtsi" + +#include +#include +#include + +/ { + + aliases { + ethernet0 = &dp1; + ethernet1 = &dp2; + led-boot = &led_system_blue; + led-failsafe = &led_system_red; + led-running = &led_system_blue; + led-upgrade = &led_system_red; + serial0 = &blsp1_uart1; + }; + + chosen { + bootargs-append = " root=/dev/ubiblock0_0 coherent_pool=2M"; + stdout-path = "serial0:115200n8"; + }; + + keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wps-button { + label = "wps"; + gpios = <&tlmm 27 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset-button { + label = "reset"; + gpios = <&tlmm 28 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "pwm-leds"; + + led_system_red: red { + color = ; + function = LED_FUNCTION_POWER; + pwms = <&pwm 3 1250000>; + max-brightness = <255>; + }; + + green { + color = ; + function = LED_FUNCTION_POWER; + pwms = <&pwm 0 1250000>; + max-brightness = <255>; + }; + + led_system_blue: blue { + color = ; + function = LED_FUNCTION_POWER; + pwms = <&pwm 1 1250000>; + max-brightness = <255>; + }; + }; + + reserved-memory { + q6_mem_regions: q6_mem_regions@4b000000 { + no-map; + reg = <0x0 0x4b000000 0x0 0x3000000>; + }; + }; +}; + +&sleep_clk { + clock-frequency = <32000>; +}; + +&xo_board_clk { + clock-frequency = <24000000>; +}; + +&blsp1_uart1 { + status = "okay"; + + pinctrl-0 = <&serial_0_pins>; + pinctrl-names = "default"; +}; + +&crypto { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&pwm { + status = "okay"; + + #pwm-cells = <2>; + pinctrl-0 = <&pwm_pins>; + pinctrl-names = "default"; +}; + +&qfprom { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + pinctrl-0 = <&qpic_pins>; + pinctrl-names = "default"; + status = "okay"; + + partitions { + status = "disabled"; + }; + + nand@0 { + compatible = "spi-nand"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + + nand-ecc-engine = <&qpic_nand>; + + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:SBL1"; + reg = <0x00000000 0x80000>; + read-only; + }; + + partition@80000 { + label = "0:MIBIB"; + reg = <0x00080000 0x80000>; + read-only; + }; + + partition@100000 { + label = "0:QSEE"; + reg = <0x00100000 0x100000>; + read-only; + }; + + partition@200000 { + label = "0:DEVCFG"; + reg = <0x00200000 0x40000>; + read-only; + }; + + partition@240000 { + label = "0:CDT"; + reg = <0x00240000 0x40000>; + read-only; + }; + + partition@280000 { + label = "0:APPSBLENV"; + reg = <0x00280000 0x80000>; + }; + + partition@300000 { + label = "0:APPSBL"; + reg = <0x00300000 0x140000>; + read-only; + }; + + partition@440000 { + label = "0:ART"; + reg = <0x00440000 0x100000>; + read-only; + }; + + partition@540000 { + label = "0:TRAINING"; + reg = <0x00540000 0x80000>; + read-only; + }; + + partition@5c0000 { + label = "u_env"; + reg = <0x005c0000 0x80000>; + }; + + partition@640000 { + label = "s_env"; + reg = <0x00640000 0x40000>; + }; + + partition@680000 { + label = "devinfo"; + reg = <0x00680000 0x40000>; + read-only; + }; + + partition@6c0000 { + label = "kernel"; + reg = <0x006c0000 0x5200000>; + }; + + partition@ec0000 { + label = "rootfs"; + reg = <0x0ec0000 0x4a00000>; + }; + + partition@58c0000 { + label = "alt_kernel"; + reg = <0x058c0000 0x5200000>; + }; + + partition@60c0000 { + label = "alt_rootfs"; + reg = <0x060c0000 0x4a00000>; + }; + + partition@aac0000 { + label = "sysdiag"; + reg = <0x0aac0000 0x200000>; + read-only; + }; + + partition@acc0000 { + label = "syscfg"; + reg = <0x0acc0000 0x4400000>; + read-only; + }; + }; + }; +}; + +&tlmm { + button_pins: button-state { + pins = "gpio27", "gpio28"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio1_pins: mdio-state { + mdc-pins { + pins = "gpio36"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio-pins { + pins = "gpio37"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + pwm_pins: pwm-state { + mux_1 { + pins = "gpio1"; + function = "pwm1"; + drive-strength = <8>; + }; + + mux_2 { + pins = "gpio30"; + function = "pwm3"; + drive-strength = <8>; + }; + + mux_3 { + pins = "gpio46"; + function = "pwm0"; + drive-strength = <8>; + }; + }; + + qpic_pins: qpic-state { + clock-pins { + pins = "gpio9"; + function = "qspi_clk"; + drive-strength = <8>; + bias-disable; + }; + + cs-pins { + pins = "gpio8"; + function = "qspi_cs"; + drive-strength = <8>; + bias-disable; + }; + + data-pins { + pins = "gpio4", "gpio5", "gpio6", "gpio7"; + function = "qspi_data"; + drive-strength = <8>; + bias-disable; + }; + }; + + serial_0_pins: uart0-state { + pins = "gpio20", "gpio21"; + function = "blsp0_uart0"; + bias-disable; + }; +}; + +&tsens { + status = "okay"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx2000.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx2000.dts new file mode 100644 index 0000000000..108b26f8d1 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx2000.dts @@ -0,0 +1,262 @@ +/dts-v1/; + +#include "ipq5018.dtsi" +#include "ipq5018-mx-base.dtsi" + +/ { + model = "Linksys MX2000"; + compatible = "linksys,mx2000", "qcom,ipq5018"; +}; + +/* + * =============================================================== + * _______________________ _______________________ + * | IPQ5018 | | QCA8337 | + * | +------+ +--------+ | | +--------+ +------+ | + * | | MAC0 |---| GE Phy | | | | Phy0 |---| MAC1 | | + * | +------+ +--------+ | | +--------+ +------+ | + * | +------+ +--------+ | | +--------+ +------+ | + * | | MAC1 |---| Uniphy |-+-SGMII-+-| SerDes |---| MAC6 | | + * | +------+ +--------+ | | +--------+ +------+ | + * |_______________________| |_______________________| + * + * =============================================================== + */ + +&switch { + status = "okay"; + + switch_mac_mode = ; + + qcom,port_phyinfo { + // MAC0 -> GE Phy -> QCA8337 Phy4 + port@0 { + port_id = <1>; + mdiobus = <&mdio0>; + phy_address = <7>; + }; + + // MAC1 ---SGMII---> QCA8337 SerDes + port@1 { + port_id = <2>; + forced-speed = <1000>; + forced-duplex = <1>; + }; + }; +}; + +// MAC1 ---SGMII---> QCA8337 SerDes +&dp2 { + status = "okay"; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&mdio0 { + status = "okay"; +}; + +/* IPQ5018 GE Phy -> Not connected + * needs to be enabled for QSDK to identify the IPQ5018 dummy switch + */ +&ge_phy { + status = "okay"; +}; + +&mdio1 { + status = "okay"; + + pinctrl-0 = <&mdio1_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 39 GPIO_ACTIVE_LOW>; + + // QCA8337 Phy0 not connected + qca8337_0: ethernet-phy@0 { + reg = <0>; + }; + + // QCA8337 Phy1 -> WAN + qca8337_1: ethernet-phy@1 { + reg = <1>; + }; + + // QCA8337 Phy2 -> LAN1 + qca8337_2: ethernet-phy@2 { + reg = <2>; + }; + + // QCA8337 Phy3 -> LAN2 + qca8337_3: ethernet-phy@3 { + reg = <3>; + }; + + // QCA8337 Phy4 -> LAN3 + qca8337_4: ethernet-phy@4 { + reg = <4>; + }; + + // QCA8337 switch + switch1: ethernet-switch@17 { + compatible = "qca,qca8337"; + reg = <17>; + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + label = "wan"; + phy-handle = <&qca8337_1>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-handle = <&qca8337_2>; + }; + + port@4 { + reg = <4>; + label = "lan2"; + phy-handle = <&qca8337_3>; + }; + + port@5 { + reg = <5>; + label = "lan1"; + phy-handle = <&qca8337_4>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + phy-mode = "sgmii"; + ethernet = <&dp2>; + qca,sgmii-enable-pll; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&q6v5_wcss { + status = "okay"; + + memory-region = <&q6_mem_regions>; + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt", + "ath11k/IPQ5018/hw1.0/m3_fw.mdt", + "ath11k/QCN6122/hw1.0/m3_fw.mdt"; + + /* The QCN6102 radio should map to UPD ID 2. Without */ + /* bootargs, the firmware will expect it to be on UPD ID 3 */ + boot-args = < + /* type: */ 0x1 /* PCIE0 */ + /* length: */ 4 + /* UPD ID: */ 2 + /* reset GPIO: */ 15 + /* reserved: */ 0 0>; + + // IPQ5018 + q6_wcss_pd1: pd-1 { + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt"; + + resets = + <&gcc GCC_WCSSAON_RESET>, + <&gcc GCC_WCSS_BCR>, + <&gcc GCC_CE_BCR>; + reset-names = + "wcss_aon_reset", + "wcss_reset", + "ce_reset"; + + clocks = + <&gcc GCC_WCSS_AHB_S_CLK>, + <&gcc GCC_WCSS_ACMT_CLK>, + <&gcc GCC_WCSS_AXI_M_CLK>; + clock-names = + "gcc_wcss_ahb_s_clk", + "gcc_wcss_acmt_clk", + "gcc_wcss_axi_m_clk"; + + // qcom,halt-regs = <&tcsr_q6_block 0xa000 0xd000 0x0>; + interrupts-extended = + <&wcss_smp2p_in 8 IRQ_TYPE_NONE>, + <&wcss_smp2p_in 9 IRQ_TYPE_NONE>, + <&wcss_smp2p_in 12 IRQ_TYPE_NONE>, + <&wcss_smp2p_in 11 IRQ_TYPE_NONE>; + interrupt-names = + "fatal", + "ready", + "spawn-ack", + "stop-ack"; + + qcom,smem-states = + <&wcss_smp2p_out 8>, + <&wcss_smp2p_out 9>, + <&wcss_smp2p_out 10>; + qcom,smem-state-names = + "shutdown", + "stop", + "spawn"; + status = "okay"; + }; + + // QCN6102 5G + q6_wcss_pd2: pd-2 { + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt"; + + interrupts-extended = + <&wcss_smp2p_in 16 IRQ_TYPE_NONE>, + <&wcss_smp2p_in 17 IRQ_TYPE_NONE>, + <&wcss_smp2p_in 20 IRQ_TYPE_NONE>, + <&wcss_smp2p_in 19 IRQ_TYPE_NONE>; + interrupt-names = + "fatal", + "ready", + "spawn-ack", + "stop-ack"; + + qcom,smem-states = + <&wcss_smp2p_out 16>, + <&wcss_smp2p_out 17>, + <&wcss_smp2p_out 18>; + qcom,smem-state-names = + "shutdown", + "stop", + "spawn"; + status = "okay"; + }; +}; + +&wifi0 { + // IPQ5018 + qcom,rproc = <&q6_wcss_pd1>; + qcom,ath11k-calibration-variant = "Linksys-MX2000"; + qcom,ath11k-fw-memory-mode = <2>; + qcom,bdf-addr = <0x4c400000>; + + status = "okay"; +}; + +&wifi1 { + // QCN6102 5G + qcom,rproc = <&q6_wcss_pd2>; + qcom,userpd-subsys-name = "q6v5_wcss_userpd2"; + qcom,ath11k-calibration-variant = "Linksys-MX2000"; + qcom,ath11k-fw-memory-mode = <2>; + qcom,bdf-addr = <0x4d100000>; + qcom,m3-dump-addr = <0x4df00000>; + + status = "okay"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx5500.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx5500.dts new file mode 100644 index 0000000000..630e8ba310 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx5500.dts @@ -0,0 +1,240 @@ +/dts-v1/; + +#include "ipq5018.dtsi" +#include "ipq5018-mx-base.dtsi" + +/ { + model = "Linksys MX5500"; + compatible = "linksys,mx5500", "qcom,ipq5018"; +}; + +/* + * =============================================================== + * _______________________ _______________________ + * | IPQ5018 | | QCA8337 | + * | +------+ +--------+ | | +--------+ +------+ | + * | | MAC0 |---| GE Phy | | | | Phy0 |---| MAC1 | | + * | +------+ +--------+ | | +--------+ +------+ | + * | +------+ +--------+ | | +--------+ +------+ | + * | | MAC1 |---| Uniphy |-+-SGMII-+-| SerDes |---| MAC6 | | + * | +------+ +--------+ | | +--------+ +------+ | + * |_______________________| |_______________________| + * + * =============================================================== + */ + +&switch { + status = "okay"; + + switch_mac_mode = ; + + qcom,port_phyinfo { + // MAC0 -> GE Phy -> QCA8337 Phy4 + port@0 { + port_id = <1>; + mdiobus = <&mdio0>; + phy_address = <7>; + }; + + // MAC1 ---SGMII---> QCA8337 SerDes + port@1 { + port_id = <2>; + forced-speed = <1000>; + forced-duplex = <1>; + }; + }; +}; + +// MAC1 ---SGMII---> QCA8337 SerDes +&dp2 { + status = "okay"; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&mdio0 { + status = "okay"; +}; + +/* IPQ5018 GE Phy -> Not connected + * needs to be enabled for QSDK to identify the IPQ5018 dummy switch + */ +&ge_phy { + status = "okay"; +}; + +&mdio1 { + status = "okay"; + + pinctrl-0 = <&mdio1_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 39 GPIO_ACTIVE_LOW>; + + // QCA8337 Phy0 not connected + qca8337_0: ethernet-phy@0 { + reg = <0>; + }; + + // QCA8337 Phy1 -> WAN + qca8337_1: ethernet-phy@1 { + reg = <1>; + }; + + // QCA8337 Phy2 -> LAN1 + qca8337_2: ethernet-phy@2 { + reg = <2>; + }; + + // QCA8337 Phy3 -> LAN2 + qca8337_3: ethernet-phy@3 { + reg = <3>; + }; + + // QCA8337 Phy4 -> LAN3 + qca8337_4: ethernet-phy@4 { + reg = <4>; + }; + + // QCA8337 switch + switch1: ethernet-switch@17 { + compatible = "qca,qca8337"; + reg = <17>; + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + label = "wan"; + phy-handle = <&qca8337_1>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-handle = <&qca8337_2>; + }; + + port@4 { + reg = <4>; + label = "lan2"; + phy-handle = <&qca8337_3>; + }; + + port@5 { + reg = <5>; + label = "lan1"; + phy-handle = <&qca8337_4>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + phy-mode = "sgmii"; + ethernet = <&dp2>; + qca,sgmii-enable-pll; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&pcie0_phy { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + + perst-gpios = <&tlmm 15 GPIO_ACTIVE_LOW>; + + bridge@0,0 { + reg = <0x00000000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi@1,0 { + status = "okay"; + + /* QCN9074: ath11k lacks DT compatible for PCI cards */ + compatible = "pci17cb,1104"; + reg = <0x00010000 0 0 0 0>; + + qcom,ath11k-calibration-variant = "Linksys-MX5500"; + }; + }; +}; + +&q6v5_wcss { + status = "okay"; + + memory-region = <&q6_mem_regions>; + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt", + "ath11k/IPQ5018/hw1.0/m3_fw.mdt"; + + // IPQ5018 + q6_wcss_pd1: pd-1 { + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt"; + + resets = + <&gcc GCC_WCSSAON_RESET>, + <&gcc GCC_WCSS_BCR>, + <&gcc GCC_CE_BCR>; + reset-names = + "wcss_aon_reset", + "wcss_reset", + "ce_reset"; + + clocks = + <&gcc GCC_WCSS_AHB_S_CLK>, + <&gcc GCC_WCSS_ACMT_CLK>, + <&gcc GCC_WCSS_AXI_M_CLK>; + clock-names = + "gcc_wcss_ahb_s_clk", + "gcc_wcss_acmt_clk", + "gcc_wcss_axi_m_clk"; + + interrupts-extended = + <&wcss_smp2p_in 8 0>, + <&wcss_smp2p_in 9 0>, + <&wcss_smp2p_in 12 0>, + <&wcss_smp2p_in 11 0>; + interrupt-names = + "fatal", + "ready", + "spawn-ack", + "stop-ack"; + + qcom,smem-states = + <&wcss_smp2p_out 8>, + <&wcss_smp2p_out 9>, + <&wcss_smp2p_out 10>; + qcom,smem-state-names = + "shutdown", + "stop", + "spawn"; + status = "okay"; + }; +}; + +&wifi0 { + // IPQ5018 + qcom,rproc = <&q6_wcss_pd1>; + qcom,ath11k-calibration-variant = "Linksys-MX5500"; + qcom,ath11k-fw-memory-mode = <2>; + qcom,bdf-addr = <0x4c400000>; + + status = "okay"; +}; diff --git a/target/linux/qualcommax/image/ipq50xx.mk b/target/linux/qualcommax/image/ipq50xx.mk index e69de29bb2..affd5e6345 100644 --- a/target/linux/qualcommax/image/ipq50xx.mk +++ b/target/linux/qualcommax/image/ipq50xx.mk @@ -0,0 +1,31 @@ +define Device/linksys_ipq50xx_mx_base + $(call Device/FitImageLzma) + DEVICE_VENDOR := Linksys + BLOCKSIZE := 128k + PAGESIZE := 2048 + KERNEL_SIZE := 8192k + IMAGE_SIZE := 83968k + NAND_SIZE := 256m + SOC := ipq5018 + IMAGES += factory.bin + IMAGE/factory.bin := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-ubi | linksys-image type=$$$$(DEVICE_MODEL) +endef + +define Device/linksys_mx2000 + $(call Device/linksys_ipq50xx_mx_base) + DEVICE_MODEL := MX2000 + DEVICE_DTS_CONFIG := config@mp03.5-c1 + DEVICE_PACKAGES := ath11k-firmware-qcn6122 \ + ipq-wifi-linksys_mx2000 +endef +TARGET_DEVICES += linksys_mx2000 + +define Device/linksys_mx5500 + $(call Device/linksys_ipq50xx_mx_base) + DEVICE_MODEL := MX5500 + DEVICE_DTS_CONFIG := config@mp03.1 + DEVICE_PACKAGES := kmod-ath11k-pci \ + ath11k-firmware-qcn9074 \ + ipq-wifi-linksys_mx5500 +endef +TARGET_DEVICES += linksys_mx5500 diff --git a/target/linux/qualcommax/ipq50xx/base-files/etc/board.d/02_network b/target/linux/qualcommax/ipq50xx/base-files/etc/board.d/02_network new file mode 100644 index 0000000000..57f11bad84 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/base-files/etc/board.d/02_network @@ -0,0 +1,45 @@ +#!/bin/sh + +. /lib/functions/uci-defaults.sh +. /lib/functions/system.sh + +ipq50xx_setup_interfaces() +{ + local board="$1" + case $board in + linksys,mx2000|\ + linksys,mx5500) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" "wan" + ;; + esac +} + +ipq50xx_setup_macs() +{ + local board="$1" + local lan_mac="" + local wan_mac="" + local label_mac="" + + case "$board" in + linksys,mx2000|\ + linksys,mx5500) + label_mac=$(mtd_get_mac_ascii devinfo hw_mac_addr) + lan_mac=$label_mac + wan_mac=$label_mac + ucidef_set_network_device_mac eth0 $label_mac + ;; + esac + + [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" $lan_mac + [ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" $wan_mac + [ -n "$label_mac" ] && ucidef_set_label_macaddr $label_mac +} + +board_config_update +board=$(board_name) +ipq50xx_setup_interfaces $board +ipq50xx_setup_macs $board +board_config_flush + +exit 0 diff --git a/target/linux/qualcommax/ipq50xx/base-files/etc/hotplug.d/firmware/11-ath11k-caldata b/target/linux/qualcommax/ipq50xx/base-files/etc/hotplug.d/firmware/11-ath11k-caldata new file mode 100644 index 0000000000..7c8cc99ee3 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/base-files/etc/hotplug.d/firmware/11-ath11k-caldata @@ -0,0 +1,47 @@ +#!/bin/sh + +[ -e /lib/firmware/$FIRMWARE ] && exit 0 + +. /lib/functions/caldata.sh + +board=$(board_name) + +case "$FIRMWARE" in +"ath11k/IPQ5018/hw1.0/cal-ahb-c000000.wifi.bin") + case "$board" in + linksys,mx2000|\ + linksys,mx5500) + caldata_extract "0:ART" 0x1000 0x20000 + label_mac=$(mtd_get_mac_ascii devinfo hw_mac_addr) + ath11k_patch_mac $(macaddr_add $label_mac 1) 0 + ath11k_remove_regdomain + ath11k_set_macflag + ;; + esac + ;; +"ath11k/QCN6122/hw1.0/cal-ahb-b00a040.wifi1.bin") + case "$board" in + linksys,mx2000) + caldata_extract "0:ART" 0x26800 0x20000 + label_mac=$(mtd_get_mac_ascii devinfo hw_mac_addr) + ath11k_patch_mac $(macaddr_add $label_mac 2) 0 + ath11k_remove_regdomain + ath11k_set_macflag + ;; + esac + ;; +"ath11k/QCN9074/hw1.0/cal-pci-0001:01:00.0.bin") + case "$board" in + linksys,mx5500) + caldata_extract "0:ART" 0x26800 0x20000 + label_mac=$(mtd_get_mac_ascii devinfo hw_mac_addr) + ath11k_patch_mac $(macaddr_add $label_mac 2) 0 + ath11k_remove_regdomain + ath11k_set_macflag + ;; + esac + ;; +*) + exit 1 + ;; +esac diff --git a/target/linux/qualcommax/ipq50xx/base-files/etc/init.d/bootcount b/target/linux/qualcommax/ipq50xx/base-files/etc/init.d/bootcount new file mode 100755 index 0000000000..b570428aef --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/base-files/etc/init.d/bootcount @@ -0,0 +1,12 @@ +#!/bin/sh /etc/rc.common + +START=99 + +boot() { + case $(board_name) in + linksys,mx2000|\ + linksys,mx5500) + mtd resetbc s_env || true + ;; + esac +} diff --git a/target/linux/qualcommax/ipq50xx/base-files/lib/upgrade/platform.sh b/target/linux/qualcommax/ipq50xx/base-files/lib/upgrade/platform.sh new file mode 100644 index 0000000000..a75b6798d2 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/base-files/lib/upgrade/platform.sh @@ -0,0 +1,84 @@ +PART_NAME=firmware +REQUIRE_IMAGE_METADATA=1 + +RAMFS_COPY_BIN='fw_printenv fw_setenv head' +RAMFS_COPY_DATA='/etc/fw_env.config /var/lock/fw_printenv.lock' + +remove_oem_ubi_volume() { + local oem_volume_name="$1" + local oem_ubivol + local mtdnum + local ubidev + + mtdnum=$(find_mtd_index "$CI_UBIPART") + if [ ! "$mtdnum" ]; then + return + fi + + ubidev=$(nand_find_ubi "$CI_UBIPART") + if [ ! "$ubidev" ]; then + ubiattach --mtdn="$mtdnum" + ubidev=$(nand_find_ubi "$CI_UBIPART") + fi + + if [ "$ubidev" ]; then + oem_ubivol=$(nand_find_volume "$ubidev" "$oem_volume_name") + [ "$oem_ubivol" ] && ubirmvol "/dev/$ubidev" --name="$oem_volume_name" + fi +} + +linksys_mx_do_upgrade() { + local setenv_script="/tmp/fw_env_upgrade" + + CI_UBIPART="rootfs" + boot_part="$(fw_printenv -n boot_part)" + if [ -n "$UPGRADE_OPT_USE_CURR_PART" ]; then + if [ "$boot_part" -eq "2" ]; then + CI_KERNPART="alt_kernel" + CI_UBIPART="alt_rootfs" + fi + else + if [ "$boot_part" -eq "1" ]; then + echo "boot_part 2" >> $setenv_script + CI_KERNPART="alt_kernel" + CI_UBIPART="alt_rootfs" + else + echo "boot_part 1" >> $setenv_script + fi + fi + + boot_part_ready="$(fw_printenv -n boot_part_ready)" + if [ "$boot_part_ready" -ne "3" ]; then + echo "boot_part_ready 3" >> $setenv_script + fi + + auto_recovery="$(fw_printenv -n auto_recovery)" + if [ "$auto_recovery" != "yes" ]; then + echo "auto_recovery yes" >> $setenv_script + fi + + if [ -f "$setenv_script" ]; then + fw_setenv -s $setenv_script || { + echo "failed to update U-Boot environment" + return 1 + } + fi + nand_do_upgrade "$1" +} + +platform_check_image() { + return 0; +} + +platform_do_upgrade() { + case "$(board_name)" in + linksys,mx2000|\ + linksys,mx5500) + remove_oem_ubi_volume rootfs + linksys_mx_do_upgrade "$1" + ;; + *) + default_do_upgrade "$1" + ;; + esac +} From c9c0f1d8e5d1f97d6c2f2251b101606c3493fcfb Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Mon, 16 Dec 2024 17:44:01 +0400 Subject: [PATCH 17/17] mac80211: ath11k: poll reo status ring for IPQ5018 This downstream patch fixes a bug which could flood the logs with the following message and would eventually lead to a crash. ath11k c000000.wifi: failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid 0 (-105) Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/17182 Signed-off-by: Robert Marko --- ...-wifi-ath11k-poll-reo-status-ipq5018.patch | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 package/kernel/mac80211/patches/ath11k/932-wifi-ath11k-poll-reo-status-ipq5018.patch diff --git a/package/kernel/mac80211/patches/ath11k/932-wifi-ath11k-poll-reo-status-ipq5018.patch b/package/kernel/mac80211/patches/ath11k/932-wifi-ath11k-poll-reo-status-ipq5018.patch new file mode 100644 index 0000000000..d89fb92e22 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/932-wifi-ath11k-poll-reo-status-ipq5018.patch @@ -0,0 +1,164 @@ +From d890c6d602307c9297df12c7d0287f9ffd26208b Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Wed, 12 May 2021 19:21:09 +0530 +Subject: [PATCH] ath11k: poll reo status ring for IPQ5018 + +Currently reo status interrupts are not received +due to wrong mapping of the reo status interrupt +line in IPQ5018. + +Hence, until the mapping is resolved in HW, use +polling to reap the reo status ring. Rather than +a period timer to reap the ring, the timer is +triggered only on sending a reo command with +status request. + +Without proper reaping of the ring, backpressure +and ring full issues are seen in multi client test +setups which leads to flooding the console with +error messages reporting failure to send reo cmds. + +Can be reverted once HW solution is available. + +Signed-off-by: Sriram R +--- +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -719,6 +719,7 @@ static struct ath11k_hw_params ath11k_hw + .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, + .support_dual_stations = false, ++ .reo_status_poll = true, + }, + { + .name = "qca2066 hw2.1", +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -361,12 +361,66 @@ void ath11k_dp_stop_shadow_timers(struct + ath11k_dp_shadow_stop_timer(ab, &ab->dp.reo_cmd_timer); + } + ++static void ath11k_dp_handle_reo_status_timer(struct timer_list *timer) ++{ ++ struct ath11k_dp *dp = from_timer(dp, timer, reo_status_timer); ++ struct ath11k_base *ab = dp->ab; ++ ++ spin_lock_bh(&dp->reo_cmd_lock); ++ dp->reo_status_timer_running = false; ++ spin_unlock_bh(&dp->reo_cmd_lock); ++ ++ ath11k_dp_process_reo_status(ab); ++} ++ ++void ath11k_dp_start_reo_status_timer(struct ath11k_base *ab) ++{ ++ struct ath11k_dp *dp = &ab->dp; ++ ++ if (!ab->hw_params.reo_status_poll) ++ return; ++ ++ spin_lock_bh(&dp->reo_cmd_lock); ++ if (dp->reo_status_timer_running) { ++ spin_unlock_bh(&dp->reo_cmd_lock); ++ return; ++ } ++ dp->reo_status_timer_running = true; ++ spin_unlock_bh(&dp->reo_cmd_lock); ++ ++ mod_timer(&dp->reo_status_timer, jiffies + ++ msecs_to_jiffies(ATH11K_REO_STATUS_POLL_TIMEOUT_MS)); ++} ++ ++static void ath11k_dp_stop_reo_status_timer(struct ath11k_base *ab) ++{ ++ struct ath11k_dp *dp = &ab->dp; ++ ++ if (!ab->hw_params.reo_status_poll) ++ return; ++ ++ del_timer_sync(&dp->reo_status_timer); ++ dp->reo_status_timer_running = false; ++} ++ ++static void ath11k_dp_init_reo_status_timer(struct ath11k_base *ab) ++{ ++ struct ath11k_dp *dp = &ab->dp; ++ ++ if (!ab->hw_params.reo_status_poll) ++ return; ++ ++ timer_setup(&dp->reo_status_timer, ++ ath11k_dp_handle_reo_status_timer, 0); ++} ++ + static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab) + { + struct ath11k_dp *dp = &ab->dp; + int i; + + ath11k_dp_stop_shadow_timers(ab); ++ ath11k_dp_stop_reo_status_timer(ab); + ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring); +@@ -388,6 +442,8 @@ static int ath11k_dp_srng_common_setup(s + int i, ret; + u8 tcl_num, wbm_num; + ++ ath11k_dp_init_reo_status_timer(ab); ++ + ret = ath11k_dp_srng_setup(ab, &dp->wbm_desc_rel_ring, + HAL_SW2WBM_RELEASE, 0, 0, + DP_WBM_RELEASE_RING_SIZE); +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -44,6 +44,8 @@ struct dp_rx_tid { + #define DP_MON_PURGE_TIMEOUT_MS 100 + #define DP_MON_SERVICE_BUDGET 128 + ++#define ATH11K_REO_STATUS_POLL_TIMEOUT_MS 10 ++ + struct dp_reo_cache_flush_elem { + struct list_head list; + struct dp_rx_tid data; +@@ -286,6 +288,10 @@ struct ath11k_dp { + spinlock_t reo_cmd_lock; + struct ath11k_hp_update_timer reo_cmd_timer; + struct ath11k_hp_update_timer tx_ring_timer[DP_TCL_NUM_RING_MAX]; ++ ++ /* reo status timer and flags */ ++ struct timer_list reo_status_timer; ++ bool reo_status_timer_running; + }; + + /* HTT definitions */ +@@ -1712,5 +1718,6 @@ void ath11k_dp_shadow_init_timer(struct + struct ath11k_hp_update_timer *update_timer, + u32 interval, u32 ring_id); + void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab); ++void ath11k_dp_start_reo_status_timer(struct ath11k_base *ab); + + #endif +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -787,6 +787,10 @@ int ath11k_dp_tx_send_reo_cmd(struct ath + if (cmd_num == 0) + return -EINVAL; + ++ /* Trigger reo status polling if required */ ++ if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) ++ ath11k_dp_start_reo_status_timer(ab); ++ + if (!cb) + return 0; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -232,6 +232,7 @@ struct ath11k_hw_params { + bool smp2p_wow_exit; + bool support_fw_mac_sequence; + bool support_dual_stations; ++ bool reo_status_poll; + }; + + struct ath11k_hw_ops {